Files
SnowLuma/tools/bench-common-memory.mjs
T

79 lines
2.5 KiB
JavaScript

// Isolated retained-heap measurements; no QQ session, disk logs or live requests.
// node --expose-gc tools/bench-common-memory.mjs logs|colored-logs|cache [source-directory]
// The optional directory contains log-sanitize.ts and single-flight-cache.ts,
// allowing the same workload to run against sources from a baseline revision.
import { performance } from 'node:perf_hooks';
import { setImmediate, setTimeout } from 'node:timers/promises';
import { resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
if (!global.gc) {
throw new Error('Run with node --expose-gc');
}
const mode = process.argv[2] ?? 'logs';
const source = (name) =>
process.argv[3]
? pathToFileURL(resolve(process.argv[3], name))
: new URL(`../packages/common/src/${name}`, import.meta.url);
async function heapUsed() {
await setImmediate();
global.gc();
global.gc();
return process.memoryUsage().heapUsed;
}
const mib = (bytes) => Number((bytes / 1024 / 1024).toFixed(2));
if (mode === 'logs' || mode === 'colored-logs') {
const { sanitizeLogLine } = await import(source('log-sanitize.ts'));
const before = await heapUsed();
const records = [];
const started = performance.now();
for (let i = 0; i < 1000; i += 1) {
const body = `${i}: ${'0123456789abcdef'.repeat(512)}`;
const line = mode === 'colored-logs' ? `\x1b[32m${body}\x1b[0m` : body;
records.push(sanitizeLogLine(line));
}
const elapsedMs = Number((performance.now() - started).toFixed(1));
const retainedHeapMiB = mib((await heapUsed()) - before);
console.log(JSON.stringify({ mode, records: records.length, retainedHeapMiB, elapsedMs }));
} else if (mode === 'cache') {
const { createSingleFlightCache } = await import(source('single-flight-cache.ts'));
const ttlMs = 2000;
const cache = createSingleFlightCache({
ttlMs,
load: async (key) =>
Array.from({ length: 1000 }, (_, i) => ({
uin: key * 1000 + i,
nickname: `member-${key}-${i}`,
})),
});
const before = await heapUsed();
for (let key = 0; key < 500; key += 1) {
await cache.get(key);
}
const populatedHeapMiB = mib((await heapUsed()) - before);
await setTimeout(ttlMs + 100);
const idleHeapMiB = mib((await heapUsed()) - before);
// Keep the cache itself live through both samples, as it is in ContactsApi.
cache.clear();
console.log(
JSON.stringify({
mode,
rosters: 500,
membersPerRoster: 1000,
ttlMs,
populatedHeapMiB,
idleHeapMiB,
}),
);
} else {
throw new Error(`Unknown workload: ${mode}`);
}