669 lines
25 KiB
TypeScript
669 lines
25 KiB
TypeScript
/**
|
|
* @file test.perf-06.cpu-utilization.ts
|
|
* @description Performance tests for CPU utilization monitoring
|
|
*/
|
|
|
|
import { tap } from '@git.zone/tstest/tapbundle';
|
|
import * as plugins from '../../plugins.js';
|
|
import { EInvoice } from '../../../ts/index.js';
|
|
import { CorpusLoader } from '../../suite/corpus.loader.js';
|
|
import { PerformanceTracker } from '../../suite/performance.tracker.js';
|
|
import * as os from 'os';
|
|
|
|
const corpusLoader = new CorpusLoader();
|
|
const performanceTracker = new PerformanceTracker('PERF-06: CPU Utilization');
|
|
|
|
tap.test('PERF-06: CPU Utilization - should maintain efficient CPU usage patterns', async (t) => {
|
|
// Helper function to get CPU usage
|
|
const getCPUUsage = () => {
|
|
const cpus = os.cpus();
|
|
let user = 0;
|
|
let nice = 0;
|
|
let sys = 0;
|
|
let idle = 0;
|
|
let irq = 0;
|
|
|
|
for (const cpu of cpus) {
|
|
user += cpu.times.user;
|
|
nice += cpu.times.nice;
|
|
sys += cpu.times.sys;
|
|
idle += cpu.times.idle;
|
|
irq += cpu.times.irq;
|
|
}
|
|
|
|
const total = user + nice + sys + idle + irq;
|
|
|
|
return {
|
|
user: user / total,
|
|
system: sys / total,
|
|
idle: idle / total,
|
|
total: total
|
|
};
|
|
};
|
|
|
|
// Test 1: CPU usage baseline for operations
|
|
const cpuBaseline = await performanceTracker.measureAsync(
|
|
'cpu-usage-baseline',
|
|
async () => {
|
|
const einvoice = new EInvoice();
|
|
const results = {
|
|
operations: [],
|
|
cpuCount: os.cpus().length,
|
|
cpuModel: os.cpus()[0]?.model || 'Unknown'
|
|
};
|
|
|
|
// Operations to test
|
|
const operations = [
|
|
{
|
|
name: 'Idle baseline',
|
|
fn: async () => {
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
}
|
|
},
|
|
{
|
|
name: 'Format detection (100x)',
|
|
fn: async () => {
|
|
const xml = '<?xml version="1.0"?><Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"><ID>CPU-TEST</ID></Invoice>';
|
|
for (let i = 0; i < 100; i++) {
|
|
await einvoice.detectFormat(xml);
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'XML parsing (50x)',
|
|
fn: async () => {
|
|
const xml = `<?xml version="1.0"?>
|
|
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
|
|
<ID>CPU-PARSE</ID>
|
|
<IssueDate>2024-01-01</IssueDate>
|
|
${Array(20).fill('<InvoiceLine><ID>Line</ID></InvoiceLine>').join('\n')}
|
|
</Invoice>`;
|
|
for (let i = 0; i < 50; i++) {
|
|
await einvoice.parseInvoice(xml, 'ubl');
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'Validation (30x)',
|
|
fn: async () => {
|
|
const invoice = {
|
|
format: 'ubl' as const,
|
|
data: {
|
|
documentType: 'INVOICE',
|
|
invoiceNumber: 'CPU-VAL-001',
|
|
issueDate: '2024-02-15',
|
|
seller: { name: 'CPU Test Seller', address: 'Address', country: 'US', taxId: 'US123' },
|
|
buyer: { name: 'CPU Test Buyer', address: 'Address', country: 'US', taxId: 'US456' },
|
|
items: Array.from({ length: 20 }, (_, i) => ({
|
|
description: `Item ${i + 1}`,
|
|
quantity: 1,
|
|
unitPrice: 100,
|
|
vatRate: 10,
|
|
lineTotal: 100
|
|
})),
|
|
totals: { netAmount: 2000, vatAmount: 200, grossAmount: 2200 }
|
|
}
|
|
};
|
|
for (let i = 0; i < 30; i++) {
|
|
await einvoice.validateInvoice(invoice);
|
|
}
|
|
}
|
|
},
|
|
{
|
|
name: 'Conversion (20x)',
|
|
fn: async () => {
|
|
const invoice = {
|
|
format: 'ubl' as const,
|
|
data: {
|
|
documentType: 'INVOICE',
|
|
invoiceNumber: 'CPU-CONV-001',
|
|
issueDate: '2024-02-15',
|
|
seller: { name: 'Seller', address: 'Address', country: 'US', taxId: 'US123' },
|
|
buyer: { name: 'Buyer', address: 'Address', country: 'US', taxId: 'US456' },
|
|
items: Array.from({ length: 10 }, (_, i) => ({
|
|
description: `Item ${i + 1}`,
|
|
quantity: 1,
|
|
unitPrice: 100,
|
|
vatRate: 10,
|
|
lineTotal: 100
|
|
})),
|
|
totals: { netAmount: 1000, vatAmount: 100, grossAmount: 1100 }
|
|
}
|
|
};
|
|
for (let i = 0; i < 20; i++) {
|
|
await einvoice.convertFormat(invoice, 'cii');
|
|
}
|
|
}
|
|
}
|
|
];
|
|
|
|
// Execute operations and measure CPU
|
|
for (const operation of operations) {
|
|
const startCPU = getCPUUsage();
|
|
const startTime = Date.now();
|
|
const startUsage = process.cpuUsage();
|
|
|
|
await operation.fn();
|
|
|
|
const endUsage = process.cpuUsage(startUsage);
|
|
const endTime = Date.now();
|
|
const endCPU = getCPUUsage();
|
|
|
|
const duration = endTime - startTime;
|
|
const userCPU = endUsage.user / 1000; // Convert to milliseconds
|
|
const systemCPU = endUsage.system / 1000;
|
|
|
|
results.operations.push({
|
|
name: operation.name,
|
|
duration,
|
|
userCPU: userCPU.toFixed(2),
|
|
systemCPU: systemCPU.toFixed(2),
|
|
totalCPU: (userCPU + systemCPU).toFixed(2),
|
|
cpuPercentage: ((userCPU + systemCPU) / duration * 100).toFixed(2),
|
|
efficiency: (duration / (userCPU + systemCPU)).toFixed(2)
|
|
});
|
|
}
|
|
|
|
return results;
|
|
}
|
|
);
|
|
|
|
// Test 2: Multi-core utilization
|
|
const multiCoreUtilization = await performanceTracker.measureAsync(
|
|
'multi-core-utilization',
|
|
async () => {
|
|
const einvoice = new EInvoice();
|
|
const results = {
|
|
coreCount: os.cpus().length,
|
|
parallelTests: []
|
|
};
|
|
|
|
// Test invoice batch
|
|
const invoices = Array.from({ length: 50 }, (_, i) => ({
|
|
format: 'ubl' as const,
|
|
data: {
|
|
documentType: 'INVOICE',
|
|
invoiceNumber: `MULTI-CORE-${i + 1}`,
|
|
issueDate: '2024-02-15',
|
|
seller: { name: `Seller ${i + 1}`, address: 'Address', country: 'US', taxId: `US${i}` },
|
|
buyer: { name: `Buyer ${i + 1}`, address: 'Address', country: 'US', taxId: `US${i + 1000}` },
|
|
items: Array.from({ length: 10 }, (_, j) => ({
|
|
description: `Item ${j + 1}`,
|
|
quantity: 1,
|
|
unitPrice: 100,
|
|
vatRate: 10,
|
|
lineTotal: 100
|
|
})),
|
|
totals: { netAmount: 1000, vatAmount: 100, grossAmount: 1100 }
|
|
}
|
|
}));
|
|
|
|
// Test different parallelism levels
|
|
const parallelismLevels = [1, 2, 4, 8, results.coreCount];
|
|
|
|
for (const parallelism of parallelismLevels) {
|
|
if (parallelism > results.coreCount) continue;
|
|
|
|
const startUsage = process.cpuUsage();
|
|
const startTime = Date.now();
|
|
|
|
// Process invoices in parallel
|
|
const batchSize = Math.ceil(invoices.length / parallelism);
|
|
const promises = [];
|
|
|
|
for (let i = 0; i < parallelism; i++) {
|
|
const batch = invoices.slice(i * batchSize, (i + 1) * batchSize);
|
|
promises.push(
|
|
Promise.all(batch.map(async (invoice) => {
|
|
await einvoice.validateInvoice(invoice);
|
|
await einvoice.convertFormat(invoice, 'cii');
|
|
}))
|
|
);
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
const endTime = Date.now();
|
|
const endUsage = process.cpuUsage(startUsage);
|
|
|
|
const duration = endTime - startTime;
|
|
const totalCPU = (endUsage.user + endUsage.system) / 1000;
|
|
const theoreticalSpeedup = parallelism;
|
|
const actualSpeedup = results.parallelTests.length > 0 ?
|
|
results.parallelTests[0].duration / duration : 1;
|
|
|
|
results.parallelTests.push({
|
|
parallelism,
|
|
duration,
|
|
totalCPU: totalCPU.toFixed(2),
|
|
cpuEfficiency: ((totalCPU / duration) * 100).toFixed(2),
|
|
theoreticalSpeedup,
|
|
actualSpeedup: actualSpeedup.toFixed(2),
|
|
efficiency: ((actualSpeedup / theoreticalSpeedup) * 100).toFixed(2)
|
|
});
|
|
}
|
|
|
|
return results;
|
|
}
|
|
);
|
|
|
|
// Test 3: CPU-intensive operations profiling
|
|
const cpuIntensiveOperations = await performanceTracker.measureAsync(
|
|
'cpu-intensive-operations',
|
|
async () => {
|
|
const einvoice = new EInvoice();
|
|
const results = {
|
|
operations: []
|
|
};
|
|
|
|
// Test scenarios
|
|
const scenarios = [
|
|
{
|
|
name: 'Complex validation',
|
|
fn: async () => {
|
|
const invoice = {
|
|
format: 'ubl' as const,
|
|
data: {
|
|
documentType: 'INVOICE',
|
|
invoiceNumber: 'COMPLEX-VAL-001',
|
|
issueDate: '2024-02-15',
|
|
dueDate: '2024-03-15',
|
|
currency: 'EUR',
|
|
seller: {
|
|
name: 'Complex Validation Test Seller GmbH',
|
|
address: 'Hauptstraße 123',
|
|
city: 'Berlin',
|
|
postalCode: '10115',
|
|
country: 'DE',
|
|
taxId: 'DE123456789',
|
|
registrationNumber: 'HRB12345',
|
|
email: 'billing@seller.de',
|
|
phone: '+49 30 12345678'
|
|
},
|
|
buyer: {
|
|
name: 'Complex Validation Test Buyer Ltd',
|
|
address: 'Business Street 456',
|
|
city: 'Munich',
|
|
postalCode: '80331',
|
|
country: 'DE',
|
|
taxId: 'DE987654321',
|
|
email: 'ap@buyer.de'
|
|
},
|
|
items: Array.from({ length: 100 }, (_, i) => ({
|
|
description: `Complex Product ${i + 1} with detailed specifications and compliance requirements`,
|
|
quantity: Math.floor(Math.random() * 100) + 1,
|
|
unitPrice: Math.random() * 1000,
|
|
vatRate: [0, 7, 19][Math.floor(Math.random() * 3)],
|
|
lineTotal: 0,
|
|
itemId: `ITEM-${String(i + 1).padStart(5, '0')}`,
|
|
additionalCharges: Math.random() * 50,
|
|
discounts: Math.random() * 20
|
|
})),
|
|
totals: { netAmount: 0, vatAmount: 0, grossAmount: 0 }
|
|
}
|
|
};
|
|
|
|
// Calculate totals
|
|
invoice.data.items.forEach(item => {
|
|
item.lineTotal = item.quantity * item.unitPrice + (item.additionalCharges || 0) - (item.discounts || 0);
|
|
invoice.data.totals.netAmount += item.lineTotal;
|
|
invoice.data.totals.vatAmount += item.lineTotal * (item.vatRate / 100);
|
|
});
|
|
invoice.data.totals.grossAmount = invoice.data.totals.netAmount + invoice.data.totals.vatAmount;
|
|
|
|
// Perform all validation levels
|
|
await einvoice.validateInvoice(invoice, { level: 'syntax' });
|
|
await einvoice.validateInvoice(invoice, { level: 'semantic' });
|
|
await einvoice.validateInvoice(invoice, { level: 'business' });
|
|
}
|
|
},
|
|
{
|
|
name: 'Large XML generation',
|
|
fn: async () => {
|
|
const invoice = {
|
|
format: 'ubl' as const,
|
|
data: {
|
|
documentType: 'INVOICE',
|
|
invoiceNumber: 'LARGE-XML-001',
|
|
issueDate: '2024-02-15',
|
|
seller: { name: 'XML Generator Corp', address: 'XML Street', country: 'US', taxId: 'US123456789' },
|
|
buyer: { name: 'XML Consumer Inc', address: 'XML Avenue', country: 'US', taxId: 'US987654321' },
|
|
items: Array.from({ length: 200 }, (_, i) => ({
|
|
description: `Product ${i + 1} with very long description `.repeat(10),
|
|
quantity: Math.random() * 100,
|
|
unitPrice: Math.random() * 1000,
|
|
vatRate: Math.random() * 25,
|
|
lineTotal: 0
|
|
})),
|
|
totals: { netAmount: 0, vatAmount: 0, grossAmount: 0 }
|
|
}
|
|
};
|
|
|
|
// Calculate totals
|
|
invoice.data.items.forEach(item => {
|
|
item.lineTotal = item.quantity * item.unitPrice;
|
|
invoice.data.totals.netAmount += item.lineTotal;
|
|
invoice.data.totals.vatAmount += item.lineTotal * (item.vatRate / 100);
|
|
});
|
|
invoice.data.totals.grossAmount = invoice.data.totals.netAmount + invoice.data.totals.vatAmount;
|
|
|
|
await einvoice.generateXML(invoice);
|
|
}
|
|
},
|
|
{
|
|
name: 'Chain conversions',
|
|
fn: async () => {
|
|
const invoice = {
|
|
format: 'ubl' as const,
|
|
data: {
|
|
documentType: 'INVOICE',
|
|
invoiceNumber: 'CHAIN-CONV-001',
|
|
issueDate: '2024-02-15',
|
|
seller: { name: 'Chain Seller', address: 'Chain Street', country: 'US', taxId: 'US123' },
|
|
buyer: { name: 'Chain Buyer', address: 'Chain Avenue', country: 'US', taxId: 'US456' },
|
|
items: Array.from({ length: 50 }, (_, i) => ({
|
|
description: `Chain Item ${i + 1}`,
|
|
quantity: i + 1,
|
|
unitPrice: 100 + i * 10,
|
|
vatRate: 10,
|
|
lineTotal: (i + 1) * (100 + i * 10)
|
|
})),
|
|
totals: { netAmount: 0, vatAmount: 0, grossAmount: 0 }
|
|
}
|
|
};
|
|
|
|
// Calculate totals
|
|
invoice.data.items.forEach(item => {
|
|
invoice.data.totals.netAmount += item.lineTotal;
|
|
invoice.data.totals.vatAmount += item.lineTotal * 0.1;
|
|
});
|
|
invoice.data.totals.grossAmount = invoice.data.totals.netAmount + invoice.data.totals.vatAmount;
|
|
|
|
// Chain conversions
|
|
let current = invoice;
|
|
const formats = ['cii', 'zugferd', 'xrechnung', 'ubl'];
|
|
|
|
for (const format of formats) {
|
|
current = await einvoice.convertFormat(current, format);
|
|
}
|
|
}
|
|
}
|
|
];
|
|
|
|
// Profile each scenario
|
|
for (const scenario of scenarios) {
|
|
const iterations = 5;
|
|
const measurements = [];
|
|
|
|
for (let i = 0; i < iterations; i++) {
|
|
const startUsage = process.cpuUsage();
|
|
const startTime = process.hrtime.bigint();
|
|
|
|
await scenario.fn();
|
|
|
|
const endTime = process.hrtime.bigint();
|
|
const endUsage = process.cpuUsage(startUsage);
|
|
|
|
const duration = Number(endTime - startTime) / 1_000_000;
|
|
const cpuTime = (endUsage.user + endUsage.system) / 1000;
|
|
|
|
measurements.push({
|
|
duration,
|
|
cpuTime,
|
|
efficiency: cpuTime / duration
|
|
});
|
|
}
|
|
|
|
// Calculate averages
|
|
const avgDuration = measurements.reduce((sum, m) => sum + m.duration, 0) / iterations;
|
|
const avgCpuTime = measurements.reduce((sum, m) => sum + m.cpuTime, 0) / iterations;
|
|
const avgEfficiency = measurements.reduce((sum, m) => sum + m.efficiency, 0) / iterations;
|
|
|
|
results.operations.push({
|
|
name: scenario.name,
|
|
iterations,
|
|
avgDuration: avgDuration.toFixed(2),
|
|
avgCpuTime: avgCpuTime.toFixed(2),
|
|
avgEfficiency: (avgEfficiency * 100).toFixed(2),
|
|
cpuIntensity: avgCpuTime > avgDuration * 0.8 ? 'HIGH' :
|
|
avgCpuTime > avgDuration * 0.5 ? 'MEDIUM' : 'LOW'
|
|
});
|
|
}
|
|
|
|
return results;
|
|
}
|
|
);
|
|
|
|
// Test 4: Corpus processing CPU profile
|
|
const corpusCPUProfile = await performanceTracker.measureAsync(
|
|
'corpus-cpu-profile',
|
|
async () => {
|
|
const files = await corpusLoader.getFilesByPattern('**/*.xml');
|
|
const einvoice = new EInvoice();
|
|
const results = {
|
|
filesProcessed: 0,
|
|
totalCPUTime: 0,
|
|
totalWallTime: 0,
|
|
cpuByOperation: {
|
|
detection: { time: 0, count: 0 },
|
|
parsing: { time: 0, count: 0 },
|
|
validation: { time: 0, count: 0 },
|
|
conversion: { time: 0, count: 0 }
|
|
}
|
|
};
|
|
|
|
// Sample files
|
|
const sampleFiles = files.slice(0, 25);
|
|
const overallStart = Date.now();
|
|
|
|
for (const file of sampleFiles) {
|
|
try {
|
|
const content = await plugins.fs.readFile(file, 'utf-8');
|
|
|
|
// Format detection
|
|
let startUsage = process.cpuUsage();
|
|
const format = await einvoice.detectFormat(content);
|
|
let endUsage = process.cpuUsage(startUsage);
|
|
results.cpuByOperation.detection.time += (endUsage.user + endUsage.system) / 1000;
|
|
results.cpuByOperation.detection.count++;
|
|
|
|
if (!format || format === 'unknown') continue;
|
|
|
|
// Parsing
|
|
startUsage = process.cpuUsage();
|
|
const invoice = await einvoice.parseInvoice(content, format);
|
|
endUsage = process.cpuUsage(startUsage);
|
|
results.cpuByOperation.parsing.time += (endUsage.user + endUsage.system) / 1000;
|
|
results.cpuByOperation.parsing.count++;
|
|
|
|
// Validation
|
|
startUsage = process.cpuUsage();
|
|
await einvoice.validateInvoice(invoice);
|
|
endUsage = process.cpuUsage(startUsage);
|
|
results.cpuByOperation.validation.time += (endUsage.user + endUsage.system) / 1000;
|
|
results.cpuByOperation.validation.count++;
|
|
|
|
// Conversion
|
|
const targetFormat = format === 'ubl' ? 'cii' : 'ubl';
|
|
startUsage = process.cpuUsage();
|
|
await einvoice.convertFormat(invoice, targetFormat);
|
|
endUsage = process.cpuUsage(startUsage);
|
|
results.cpuByOperation.conversion.time += (endUsage.user + endUsage.system) / 1000;
|
|
results.cpuByOperation.conversion.count++;
|
|
|
|
results.filesProcessed++;
|
|
|
|
} catch (error) {
|
|
// Skip failed files
|
|
}
|
|
}
|
|
|
|
results.totalWallTime = Date.now() - overallStart;
|
|
|
|
// Calculate totals and averages
|
|
for (const op of Object.keys(results.cpuByOperation)) {
|
|
const opData = results.cpuByOperation[op];
|
|
results.totalCPUTime += opData.time;
|
|
}
|
|
|
|
return {
|
|
filesProcessed: results.filesProcessed,
|
|
totalWallTime: results.totalWallTime,
|
|
totalCPUTime: results.totalCPUTime.toFixed(2),
|
|
cpuEfficiency: ((results.totalCPUTime / results.totalWallTime) * 100).toFixed(2),
|
|
operations: Object.entries(results.cpuByOperation).map(([op, data]) => ({
|
|
operation: op,
|
|
totalTime: data.time.toFixed(2),
|
|
avgTime: data.count > 0 ? (data.time / data.count).toFixed(3) : 'N/A',
|
|
percentage: ((data.time / results.totalCPUTime) * 100).toFixed(1)
|
|
}))
|
|
};
|
|
}
|
|
);
|
|
|
|
// Test 5: Sustained CPU load test
|
|
const sustainedCPULoad = await performanceTracker.measureAsync(
|
|
'sustained-cpu-load',
|
|
async () => {
|
|
const einvoice = new EInvoice();
|
|
const testDuration = 5000; // 5 seconds
|
|
const results = {
|
|
samples: [],
|
|
avgCPUUsage: 0,
|
|
peakCPUUsage: 0,
|
|
consistency: 0
|
|
};
|
|
|
|
// Test invoice
|
|
const testInvoice = {
|
|
format: 'ubl' as const,
|
|
data: {
|
|
documentType: 'INVOICE',
|
|
invoiceNumber: 'SUSTAINED-CPU-001',
|
|
issueDate: '2024-02-15',
|
|
seller: { name: 'CPU Load Seller', address: 'Address', country: 'US', taxId: 'US123' },
|
|
buyer: { name: 'CPU Load Buyer', address: 'Address', country: 'US', taxId: 'US456' },
|
|
items: Array.from({ length: 20 }, (_, i) => ({
|
|
description: `Item ${i + 1}`,
|
|
quantity: 1,
|
|
unitPrice: 100,
|
|
vatRate: 10,
|
|
lineTotal: 100
|
|
})),
|
|
totals: { netAmount: 2000, vatAmount: 200, grossAmount: 2200 }
|
|
}
|
|
};
|
|
|
|
const startTime = Date.now();
|
|
let sampleCount = 0;
|
|
|
|
// Run sustained load
|
|
while (Date.now() - startTime < testDuration) {
|
|
const sampleStart = process.cpuUsage();
|
|
const sampleStartTime = Date.now();
|
|
|
|
// Perform operations
|
|
await einvoice.validateInvoice(testInvoice);
|
|
await einvoice.convertFormat(testInvoice, 'cii');
|
|
|
|
const sampleEndTime = Date.now();
|
|
const sampleEnd = process.cpuUsage(sampleStart);
|
|
|
|
const sampleDuration = sampleEndTime - sampleStartTime;
|
|
const cpuTime = (sampleEnd.user + sampleEnd.system) / 1000;
|
|
const cpuUsage = (cpuTime / sampleDuration) * 100;
|
|
|
|
results.samples.push(cpuUsage);
|
|
|
|
if (cpuUsage > results.peakCPUUsage) {
|
|
results.peakCPUUsage = cpuUsage;
|
|
}
|
|
|
|
sampleCount++;
|
|
}
|
|
|
|
// Calculate statistics
|
|
if (results.samples.length > 0) {
|
|
results.avgCPUUsage = results.samples.reduce((a, b) => a + b, 0) / results.samples.length;
|
|
|
|
// Calculate standard deviation for consistency
|
|
const variance = results.samples.reduce((sum, val) =>
|
|
sum + Math.pow(val - results.avgCPUUsage, 2), 0) / results.samples.length;
|
|
const stdDev = Math.sqrt(variance);
|
|
results.consistency = 100 - (stdDev / results.avgCPUUsage * 100);
|
|
}
|
|
|
|
return {
|
|
duration: Date.now() - startTime,
|
|
samples: results.samples.length,
|
|
avgCPUUsage: results.avgCPUUsage.toFixed(2),
|
|
peakCPUUsage: results.peakCPUUsage.toFixed(2),
|
|
consistency: results.consistency.toFixed(2),
|
|
stable: results.consistency > 80
|
|
};
|
|
}
|
|
);
|
|
|
|
// Summary
|
|
t.comment('\n=== PERF-06: CPU Utilization Test Summary ===');
|
|
|
|
t.comment('\nCPU Baseline:');
|
|
t.comment(` System: ${cpuBaseline.result.cpuCount} cores, ${cpuBaseline.result.cpuModel}`);
|
|
t.comment(' Operation benchmarks:');
|
|
cpuBaseline.result.operations.forEach(op => {
|
|
t.comment(` ${op.name}:`);
|
|
t.comment(` - Duration: ${op.duration}ms`);
|
|
t.comment(` - CPU time: ${op.totalCPU}ms (user: ${op.userCPU}ms, system: ${op.systemCPU}ms)`);
|
|
t.comment(` - CPU usage: ${op.cpuPercentage}%`);
|
|
t.comment(` - Efficiency: ${op.efficiency}x`);
|
|
});
|
|
|
|
t.comment('\nMulti-Core Utilization:');
|
|
t.comment(' Parallelism | Duration | CPU Time | Efficiency | Speedup | Scaling');
|
|
t.comment(' ------------|----------|----------|------------|---------|--------');
|
|
multiCoreUtilization.result.parallelTests.forEach(test => {
|
|
t.comment(` ${String(test.parallelism).padEnd(11)} | ${String(test.duration + 'ms').padEnd(8)} | ${test.totalCPU.padEnd(8)}ms | ${test.cpuEfficiency.padEnd(10)}% | ${test.actualSpeedup.padEnd(7)}x | ${test.efficiency}%`);
|
|
});
|
|
|
|
t.comment('\nCPU-Intensive Operations:');
|
|
cpuIntensiveOperations.result.operations.forEach(op => {
|
|
t.comment(` ${op.name}:`);
|
|
t.comment(` - Avg duration: ${op.avgDuration}ms`);
|
|
t.comment(` - Avg CPU time: ${op.avgCpuTime}ms`);
|
|
t.comment(` - CPU efficiency: ${op.avgEfficiency}%`);
|
|
t.comment(` - Intensity: ${op.cpuIntensity}`);
|
|
});
|
|
|
|
t.comment('\nCorpus CPU Profile:');
|
|
t.comment(` Files processed: ${corpusCPUProfile.result.filesProcessed}`);
|
|
t.comment(` Total wall time: ${corpusCPUProfile.result.totalWallTime}ms`);
|
|
t.comment(` Total CPU time: ${corpusCPUProfile.result.totalCPUTime}ms`);
|
|
t.comment(` CPU efficiency: ${corpusCPUProfile.result.cpuEfficiency}%`);
|
|
t.comment(' By operation:');
|
|
corpusCPUProfile.result.operations.forEach(op => {
|
|
t.comment(` - ${op.operation}: ${op.totalTime}ms (${op.percentage}%), avg ${op.avgTime}ms`);
|
|
});
|
|
|
|
t.comment('\nSustained CPU Load (5 seconds):');
|
|
t.comment(` Samples: ${sustainedCPULoad.result.samples}`);
|
|
t.comment(` Average CPU usage: ${sustainedCPULoad.result.avgCPUUsage}%`);
|
|
t.comment(` Peak CPU usage: ${sustainedCPULoad.result.peakCPUUsage}%`);
|
|
t.comment(` Consistency: ${sustainedCPULoad.result.consistency}%`);
|
|
t.comment(` Stable performance: ${sustainedCPULoad.result.stable ? 'YES ✅' : 'NO ⚠️'}`);
|
|
|
|
// Performance targets check
|
|
t.comment('\n=== Performance Targets Check ===');
|
|
const avgCPUEfficiency = parseFloat(corpusCPUProfile.result.cpuEfficiency);
|
|
const cpuStability = sustainedCPULoad.result.stable;
|
|
|
|
t.comment(`CPU efficiency: ${avgCPUEfficiency}% ${avgCPUEfficiency > 50 ? '✅' : '⚠️'} (target: >50%)`);
|
|
t.comment(`CPU stability: ${cpuStability ? 'STABLE ✅' : 'UNSTABLE ⚠️'}`);
|
|
|
|
// Overall performance summary
|
|
t.comment('\n=== Overall Performance Summary ===');
|
|
performanceTracker.logSummary();
|
|
|
|
t.end();
|
|
});
|
|
|
|
tap.start(); |