Files
dashfront/scripts/bench-avif-encode.mjs
seb 09fb1da335 Enhance article photo handling and API documentation.
- Updated API documentation to reflect changes in article data structure.
- Improved ArticlePhoto component to support dynamic sizing and object fit options.
- Added performance logging for image upload in ArticlePhotosDialog and ArticlePhotoTile components.
- Enhanced ArticleTile to conditionally render photos and adjusted styling for better layout.
- Optimized image preparation process with configurable AVIF encoding speed.
2026-08-01 04:32:49 +02:00

108 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Benchmark AVIF encode options (+ JPEG/WebP reference) on a real fixture.
* Usage: node scripts/bench-avif-encode.mjs [path-to-avif]
*/
import { readFile } from 'node:fs/promises';
import { resolve, dirname } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import decodeAvif, { init as initAvifDecode } from '@jsquash/avif/decode.js';
import encodeAvif, { init as initAvifEncode } from '@jsquash/avif/encode.js';
import encodeJpeg, { init as initJpegEncode } from '@jsquash/jpeg/encode.js';
import encodeWebp, { init as initWebpEncode } from '@jsquash/webp/encode.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, '..');
const RUNS = 3;
const fixturePath = resolve(
process.argv[2] ?? resolve(root, 'tmp/bench-source.avif'),
);
async function compileWasm(relPath) {
const bytes = await readFile(resolve(root, relPath));
return WebAssembly.compile(bytes);
}
function median(values) {
const sorted = [...values].sort((a, b) => a - b);
return sorted[Math.floor(sorted.length / 2)];
}
async function timeEncode(label, fn) {
const times = [];
let bytes = 0;
for (let i = 0; i < RUNS; i += 1) {
const start = performance.now();
const buf = await fn();
times.push(performance.now() - start);
bytes = buf.byteLength ?? buf.length;
}
return {
label,
encodeMs: Math.round(median(times)),
outputBytes: bytes,
};
}
console.log('init wasm…');
await initAvifDecode(await compileWasm('node_modules/@jsquash/avif/codec/dec/avif_dec.wasm'));
await initAvifEncode(await compileWasm('node_modules/@jsquash/avif/codec/enc/avif_enc.wasm'));
await initJpegEncode(await compileWasm('node_modules/@jsquash/jpeg/codec/enc/mozjpeg_enc.wasm'));
await initWebpEncode(await compileWasm('node_modules/@jsquash/webp/codec/enc/webp_enc.wasm'));
const raw = await readFile(fixturePath);
console.log(`fixture: ${fixturePath} (${raw.byteLength} bytes)`);
const decodeStarted = performance.now();
const imageData = await decodeAvif(raw.buffer.slice(raw.byteOffset, raw.byteOffset + raw.byteLength));
const decodeMs = Math.round(performance.now() - decodeStarted);
console.log(`decode: ${decodeMs}ms → ${imageData.width}×${imageData.height}`);
// Warmup
await encodeAvif(imageData, { quality: 80, speed: 10 });
const variants = [
{ label: 'avif q80/s6 (baseline)', fn: () => encodeAvif(imageData, { quality: 80, speed: 6 }) },
{ label: 'avif q80/s8', fn: () => encodeAvif(imageData, { quality: 80, speed: 8 }) },
{ label: 'avif q80/s10', fn: () => encodeAvif(imageData, { quality: 80, speed: 10 }) },
{ label: 'avif q70/s8', fn: () => encodeAvif(imageData, { quality: 70, speed: 8 }) },
{ label: 'avif q70/s10', fn: () => encodeAvif(imageData, { quality: 70, speed: 10 }) },
{ label: 'jpeg q80 (ref)', fn: () => encodeJpeg(imageData, { quality: 80 }) },
{ label: 'webp q80 (ref)', fn: () => encodeWebp(imageData, { quality: 80 }) },
];
const rows = [];
for (const variant of variants) {
process.stdout.write(`… ${variant.label}\n`);
rows.push(await timeEncode(variant.label, variant.fn));
}
const baseline = rows.find((row) => row.label.includes('baseline'));
console.log('\nresults (median of 3 runs):');
console.log(
'label'.padEnd(28),
'encodeMs'.padStart(10),
'bytes'.padStart(10),
'vsBaseMs'.padStart(10),
'vsBaseSz'.padStart(10),
);
for (const row of rows) {
const vsMs = baseline ? `${(row.encodeMs / baseline.encodeMs).toFixed(2)}x` : '-';
const vsSz = baseline ? `${(row.outputBytes / baseline.outputBytes).toFixed(2)}x` : '-';
console.log(
row.label.padEnd(28),
String(row.encodeMs).padStart(10),
String(row.outputBytes).padStart(10),
vsMs.padStart(10),
vsSz.padStart(10),
);
}
console.log(JSON.stringify({
fixturePath,
decodeMs,
size: `${imageData.width}×${imageData.height}`,
rows,
}, null, 2));