Skip to content

Commit d7e8d33

Browse files
committed
fixup benchmark
1 parent 019ae25 commit d7e8d33

1 file changed

Lines changed: 71 additions & 78 deletions

File tree

apps/zipsync/src/benchmark.test.ts

Lines changed: 71 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@ interface IMeasurement {
6161
sizeBytes?: number;
6262
}
6363
const measurements: IMeasurement[] = [];
64-
// Allow specifying iterations via env BENCH_ITERATIONS or command arg --iterations N (jest passes args; we scan process.argv)
64+
// Allow specifying iterations via env BENCH_ITERATIONS. Defaults to 0 to avoid running the benchmark unless explicitly enabled.
6565
function detectIterations(): number {
66-
let iter = 1;
66+
let iter = 0;
6767
const envParsed: number = parseInt(process.env.BENCH_ITERATIONS || '', 10);
6868
if (!isNaN(envParsed) && envParsed > 0) {
6969
iter = envParsed;
7070
}
71-
return iter || 5;
71+
return iter;
7272
}
7373
const ITERATIONS: number = detectIterations();
7474

@@ -223,16 +223,16 @@ function benchZipSyncScenario(
223223
}
224224

225225
// the benchmarks are skipped by default because they require external tools (tar, zip) to be installed
226-
describe.skip(`archive benchmarks (iterations=${ITERATIONS})`, () => {
227-
it('tar', () => {
226+
describe(`archive benchmarks (iterations=${ITERATIONS})`, () => {
227+
it('gtar', () => {
228228
if (!isTarAvailable()) {
229229
console.log('Skipping tar test because tar is not available');
230230
return;
231231
}
232232
if (!tempDir) throw new Error('Temp directory is not set up.');
233-
bench('tar', {
234-
pack: ({ archive, demoDir }) => execSync(`tar -cf "${archive}" -C "${demoDir}" .`),
235-
unpack: ({ archive, unpackDir }) => execSync(`tar -xf "${archive}" -C "${unpackDir}"`),
233+
bench('gtar', {
234+
pack: ({ archive, demoDir }) => execSync(`gtar -cf "${archive}" -C "${demoDir}" .`),
235+
unpack: ({ archive, unpackDir }) => execSync(`gtar -xf "${archive}" -C "${unpackDir}"`),
236236
archive: path.join(tempDir, 'archive.tar'),
237237
unpackDir: path.join(tempDir, 'unpacked-tar'),
238238
populateUnpackDir: 'full',
@@ -388,87 +388,80 @@ afterAll(() => {
388388
]
389389
}
390390
];
391-
interface ITableRow {
392-
group: string;
393-
isBaseline: boolean;
394-
s: IStats;
395-
deltaMeanPct: number;
396-
}
397-
const tableRows: ITableRow[] = [];
398-
for (const g of groupsDef) {
399-
const baselinePack: IStats | undefined = stats.find((s) => s.kind === g.baseline && s.phase === 'pack');
400-
const baselineUnpack: IStats | undefined = stats.find(
401-
(s) => s.kind === g.baseline && s.phase === 'unpack'
402-
);
403-
for (const member of g.members) {
404-
for (const phase of ['pack', 'unpack'] as const) {
405-
const s = stats.find((st) => st.kind === member && st.phase === phase);
406-
if (!s) continue;
407-
const baseline = phase === 'pack' ? baselinePack : baselineUnpack;
408-
const deltaMeanPct = baseline ? ((s.mean - baseline.mean) / baseline.mean) * 100 : 0;
409-
tableRows.push({ group: g.title, isBaseline: member === g.baseline, s, deltaMeanPct });
391+
// Build per-group markdown tables (no Group column) for each phase
392+
function buildGroupTable(
393+
group: { title: string; baseline: string; members: string[] },
394+
phase: 'pack' | 'unpack'
395+
): string[] {
396+
// Human readable bytes formatter
397+
function formatBytes(bytes: number): string {
398+
const units = ['B', 'KB', 'MB', 'GB'];
399+
let value = bytes;
400+
let i = 0;
401+
while (value >= 1024 && i < units.length - 1) {
402+
value /= 1024;
403+
i++;
410404
}
405+
const formatted = value >= 100 ? value.toFixed(0) : value >= 10 ? value.toFixed(1) : value.toFixed(2);
406+
return `${formatted} ${units[i]}`;
411407
}
412-
}
413-
414-
function buildTable(rowsData: ITableRow[], phaseFilter: 'pack' | 'unpack'): string[] {
415408
const headers =
416-
phaseFilter === 'pack'
417-
? [
418-
'Group',
419-
'Archive',
420-
'iter',
421-
'min(ms)',
422-
'mean(ms)',
423-
'Δmean%',
424-
'p95(ms)',
425-
'max(ms)',
426-
'std(ms)',
427-
'size(bytes)'
428-
]
429-
: ['Group', 'Archive', 'iter', 'min(ms)', 'mean(ms)', 'Δmean%', 'p95(ms)', 'max(ms)', 'std(ms)'];
430-
const rows: string[][] = [headers];
431-
for (const row of rowsData.filter((r) => r.s.phase === phaseFilter)) {
432-
const baseCols = [
433-
row.isBaseline ? row.group : '',
434-
row.s.kind + (row.isBaseline ? '*' : ''),
435-
String(row.s.n),
436-
row.s.min.toFixed(2),
437-
row.s.mean.toFixed(2),
438-
(row.deltaMeanPct >= 0 ? '+' : '') + row.deltaMeanPct.toFixed(1),
439-
row.s.p95.toFixed(2),
440-
row.s.max.toFixed(2),
441-
row.s.std.toFixed(2)
409+
phase === 'pack'
410+
? ['Archive', 'min (ms)', 'mean (ms)', 'p95 (ms)', 'max (ms)', 'std (ms)', 'speed×', 'size']
411+
: ['Archive', 'min (ms)', 'mean (ms)', 'p95 (ms)', 'max (ms)', 'std (ms)', 'speed×'];
412+
const lines: string[] = [];
413+
lines.push('| ' + headers.join(' | ') + ' |');
414+
const align: string[] = headers.map((header, idx) => (idx === 0 ? '---' : '---:'));
415+
lines.push('| ' + align.join(' | ') + ' |');
416+
const baselineStats: IStats | undefined = stats.find(
417+
(s) => s.kind === group.baseline && s.phase === phase
418+
);
419+
for (const member of group.members) {
420+
const s: IStats | undefined = stats.find((st) => st.kind === member && st.phase === phase);
421+
if (!s) continue;
422+
const isBaseline: boolean = member === group.baseline;
423+
const speedFactor: number = baselineStats ? baselineStats.mean / s.mean : 1;
424+
const cols: string[] = [
425+
(isBaseline ? '**' : '') + s.kind + (isBaseline ? '**' : ''),
426+
s.min.toFixed(2),
427+
s.mean.toFixed(2),
428+
s.p95.toFixed(2),
429+
s.max.toFixed(2),
430+
s.std.toFixed(2),
431+
speedFactor.toFixed(2) + 'x'
442432
];
443-
if (phaseFilter === 'pack') {
444-
baseCols.push(row.s.sizeMean !== undefined ? Math.round(row.s.sizeMean).toString() : '');
433+
if (phase === 'pack') {
434+
cols.push(s.sizeMean !== undefined ? formatBytes(Math.round(s.sizeMean)) : '');
445435
}
446-
rows.push(baseCols);
436+
lines.push('| ' + cols.join(' | ') + ' |');
447437
}
448-
const colWidths: number[] = headers.map((header, i) =>
449-
rows.reduce((w, r) => Math.max(w, r[i].length), 0)
450-
);
451-
return rows.map((r) => r.map((c, i) => c.padStart(colWidths[i], ' ')).join(' '));
438+
return lines;
452439
}
453-
const packTable: string[] = buildTable(tableRows, 'pack');
454-
const unpackTable: string[] = buildTable(tableRows, 'unpack');
455440
const outputLines: string[] = [];
456-
outputLines.push('\nBenchmark Results (iterations=' + ITERATIONS + '):');
457-
outputLines.push('PACK PHASE:');
458-
outputLines.push(packTable[0]);
459-
outputLines.push('-'.repeat(packTable[0].length));
460-
for (let i = 1; i < packTable.length; i++) outputLines.push(packTable[i]);
461-
outputLines.push('* baseline (pack)');
441+
outputLines.push('# Benchmark Results');
442+
outputLines.push('');
443+
outputLines.push(
444+
'This document contains performance measurements for packing and unpacking a synthetic dataset using traditional archive tools (tar, zip) and various zipsync modes. The dataset consists of two directory trees (subdir1, subdir2) populated with text files. Each scenario was executed multiple iterations; metrics shown are aggregated timing statistics. The speed× column shows how many times faster a scenario is compared to the baseline in that group (values >1 = faster, <1 = slower). Baseline rows are shown in bold.'
445+
);
446+
outputLines.push('');
447+
outputLines.push(`Iterations: ${ITERATIONS}`);
462448
outputLines.push('');
463-
outputLines.push('UNPACK PHASE:');
464-
outputLines.push(unpackTable[0]);
465-
outputLines.push('-'.repeat(unpackTable[0].length));
466-
for (let i = 1; i < unpackTable.length; i++) outputLines.push(unpackTable[i]);
467-
outputLines.push('* baseline (unpack)');
449+
for (const g of groupsDef) {
450+
outputLines.push(`## ${g.title}`);
451+
outputLines.push('');
452+
outputLines.push('### Pack Phase');
453+
outputLines.push('');
454+
outputLines.push(...buildGroupTable(g, 'pack'));
455+
outputLines.push('');
456+
outputLines.push('### Unpack Phase');
457+
outputLines.push('');
458+
outputLines.push(...buildGroupTable(g, 'unpack'));
459+
outputLines.push('');
460+
}
468461
const resultText = outputLines.join('\n');
469462
console.log(resultText);
470463
try {
471-
const resultFile = path.join(__dirname, '..', 'temp', `benchmark-results.txt`);
464+
const resultFile = path.join(__dirname, '..', 'temp', `benchmark-results.md`);
472465
fs.writeFileSync(resultFile, resultText, { encoding: 'utf-8' });
473466
console.log(`Benchmark results written to: ${resultFile}`);
474467
} catch (e) {
@@ -488,7 +481,7 @@ function isZipAvailable(): boolean {
488481
}
489482
function isTarAvailable(): boolean {
490483
try {
491-
const checkTar = process.platform === 'win32' ? 'where tar' : 'command -v tar';
484+
const checkTar = process.platform === 'win32' ? 'where gtar' : 'command -v gtar';
492485
execSync(checkTar, { stdio: 'ignore' });
493486
return true;
494487
} catch {

0 commit comments

Comments
 (0)