chore(vendor): import pi 1.3.0

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
alexion committed 2026-10-15 17:58:48 -04:00
1 parent 719370c8f7
commit 6081e3e325
1743 files changed
+205459 -131704

No files matched your search

@@ -0,0 +1,210 @@
import { spawnSync } from "node:child_process";
import { writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
type Mode = "tracker";
type Scenario =
| "import"
| "committed-read"
| "draft-read"
| "sparse"
| "queue"
| "sort"
| "dense"
| "unshift"
| "watch-authority-control"
| "watch-live-borrowed"
| "watch-acquire-shared"
| "watch-acquire-detached"
| "watch-acquire-staggered"
| "watch-lag"
| "watch-catch-up"
| "watch-catch-up-coalesced"
| "watch-live-immutable";
type Result = {
mode: Mode;
scenario: Scenario;
size: number;
trial: number;
queueOperations: number;
watchProfile: string | null;
watchCount: number;
watchUpdates: number;
queuedBatchReferences: number;
importMs: number;
mutateMs: number;
prepareMs: number;
serializeMs: number;
adoptMs: number;
acquireMs: number;
enqueueMs: number;
deliveryMs: number;
scenarioWorkMs: number;
opBytes: number;
operationCount: number;
checksum: number;
readyMiB: number;
sampledAcquireMiB: number;
sampledTransientMiB: number;
retainedMiB: number;
retainedOverReadyMiB: number;
postAcquireRetainedMiB: number;
postLagRetainedMiB: number;
postDeliveryRetainedMiB: number;
releasedRetainedMiB: number;
maxRssMiB: number;
};
type Failure = { mode: Mode; scenario: Scenario; trial: number; stderr: string };
const ordinaryScenarios: readonly Scenario[] = [
"import",
"committed-read",
"draft-read",
"sparse",
"queue",
"sort",
"dense",
"unshift",
];
const watchScenarios: readonly Scenario[] = [
"watch-authority-control",
"watch-live-borrowed",
"watch-acquire-shared",
"watch-acquire-detached",
"watch-acquire-staggered",
"watch-lag",
"watch-catch-up",
"watch-catch-up-coalesced",
"watch-live-immutable",
];
const modes: readonly Mode[] = ["tracker"];
const quick = process.argv.includes("--quick");
const watchOnly = process.argv.includes("--watch-only");
const ordinaryOnly = process.argv.includes("--ordinary-only");
if (watchOnly && ordinaryOnly) throw new Error("Choose at most one scenario filter");
const scenarios = watchOnly
? watchScenarios
: ordinaryOnly
? ordinaryScenarios
: [...ordinaryScenarios, ...watchScenarios];
const size = quick ? 10_000 : 100_000;
const trials = quick ? 1 : 3;
const watchCount = quick ? 4 : 8;
const watchUpdates = quick ? 20 : 100;
const root = fileURLToPath(new URL("../../../..", import.meta.url));
const worker = fileURLToPath(new URL("./benchmark.worker.ts", import.meta.url));
const results: Result[] = [];
const failures: Failure[] = [];
for (let trial = 0; trial < trials; trial++) {
for (const scenario of scenarios) {
for (const mode of modes) {
const child = spawnSync(
process.execPath,
[
"--expose-gc",
"--max-old-space-size=4096",
worker,
mode,
scenario,
String(size),
String(trial),
String(watchCount),
String(watchUpdates),
],
{ cwd: root, encoding: "utf8", timeout: 300_000, maxBuffer: 10 * 1024 * 1024 },
);
if (child.status !== 0) {
const failure = { mode, scenario, trial, stderr: child.stderr } satisfies Failure;
failures.push(failure);
console.log(JSON.stringify(failure));
} else {
const result = JSON.parse(child.stdout) as Result;
results.push(result);
console.log(JSON.stringify(result));
}
}
}
}
const average = (values: readonly number[]): number | null =>
values.length === 0 ? null : values.reduce((total, value) => total + value, 0) / values.length;
const median = (values: readonly number[]): number | null => {
if (values.length === 0) return null;
const ordered = [...values].sort((left, right) => left - right);
return ordered[Math.floor(ordered.length / 2)]!;
};
const range = (values: readonly number[]): readonly [number, number] | null =>
values.length === 0 ? null : [Math.min(...values), Math.max(...values)];
const metricNames = [
"importMs",
"mutateMs",
"prepareMs",
"serializeMs",
"adoptMs",
"acquireMs",
"enqueueMs",
"deliveryMs",
"scenarioWorkMs",
"opBytes",
"operationCount",
"readyMiB",
"sampledAcquireMiB",
"sampledTransientMiB",
"retainedMiB",
"retainedOverReadyMiB",
"postAcquireRetainedMiB",
"postLagRetainedMiB",
"postDeliveryRetainedMiB",
"releasedRetainedMiB",
"maxRssMiB",
] as const satisfies readonly (keyof Result)[];
const summary = scenarios.flatMap((scenario) =>
modes.map((mode) => {
const selected = results.filter((result) => result.mode === mode && result.scenario === scenario);
const averages: Record<string, number | null> = {};
const medians: Record<string, number | null> = {};
const ranges: Record<string, readonly [number, number] | null> = {};
for (const metric of metricNames) {
const values = selected.map((result) => result[metric]);
averages[metric] = average(values);
medians[metric] = median(values);
ranges[metric] = range(values);
}
return {
mode,
scenario,
watchProfile: selected[0]?.watchProfile ?? null,
watchCount: selected[0]?.watchCount ?? 0,
watchUpdates: selected[0]?.watchUpdates ?? 0,
averages,
medians,
ranges,
};
}),
);
const output = join(tmpdir(), "chord-delta-benchmark.json");
writeFileSync(
output,
JSON.stringify(
{ node: process.version, size, trials, watchCount, watchUpdates, results, failures, summary },
null,
2,
),
);
console.log(
JSON.stringify({
output,
node: process.version,
size,
trials,
watchCount,
watchUpdates,
failures: failures.length,
summary,
}),
);
@@ -0,0 +1,436 @@
import assert from "node:assert/strict";
import { performance } from "node:perf_hooks";
import { setImmediate } from "node:timers/promises";
import type { Draft } from "../../src/delta/draft.ts";
import { applyImmutable, type Op, track } from "../../src/delta/index.ts";
type Mode = "tracker";
type Scenario =
| "import"
| "committed-read"
| "draft-read"
| "sparse"
| "queue"
| "sort"
| "dense"
| "unshift"
| "watch-authority-control"
| "watch-live-borrowed"
| "watch-acquire-shared"
| "watch-acquire-detached"
| "watch-acquire-staggered"
| "watch-lag"
| "watch-catch-up"
| "watch-catch-up-coalesced"
| "watch-live-immutable";
type Row = { id: number; value: number; payload: string };
type Document = { rows: Row[] };
type PreparedLike = { readonly value: Readonly<Document>; readonly ops: readonly Op[] };
type ChangeLike = { readonly state: Draft<Document>; prepare(): PreparedLike };
type TrackerLike = { readonly value: Document; beginChange(): ChangeLike; adopt(prepared: PreparedLike): void };
type Frame = { readonly sequence: number; readonly ops: readonly Op[] };
type WatchReplica = {
value: Document;
readonly baseSequence: number;
sequence: number;
readonly queue: Frame[];
};
const MiB = 1024 * 1024;
const MODES = new Set<Mode>(["tracker"]);
const SCENARIOS = new Set<Scenario>([
"import",
"committed-read",
"draft-read",
"sparse",
"queue",
"sort",
"dense",
"unshift",
"watch-authority-control",
"watch-live-borrowed",
"watch-acquire-shared",
"watch-acquire-detached",
"watch-acquire-staggered",
"watch-lag",
"watch-catch-up",
"watch-catch-up-coalesced",
"watch-live-immutable",
]);
const WATCH_SCENARIOS = new Set<Scenario>([
"watch-authority-control",
"watch-live-borrowed",
"watch-acquire-shared",
"watch-acquire-detached",
"watch-acquire-staggered",
"watch-lag",
"watch-catch-up",
"watch-catch-up-coalesced",
"watch-live-immutable",
]);
const mode = process.argv[2] as Mode;
const scenario = process.argv[3] as Scenario;
if (!MODES.has(mode)) throw new Error(`Unknown benchmark mode ${mode}`);
if (!SCENARIOS.has(scenario)) throw new Error(`Unknown benchmark scenario ${scenario}`);
const size = Number(process.argv[4] ?? 100_000);
const trial = Number(process.argv[5] ?? 0);
const requestedWatchCount = Number(process.argv[6] ?? 8);
const requestedWatchUpdates = Number(process.argv[7] ?? 100);
if (!Number.isSafeInteger(requestedWatchCount) || requestedWatchCount < 1) throw new RangeError("Invalid watch count");
if (!Number.isSafeInteger(requestedWatchUpdates) || requestedWatchUpdates < 0)
throw new RangeError("Invalid watch update count");
function fixture(count: number): Document {
return {
rows: Array.from({ length: count }, (_, id) => ({ id, value: id, payload: `row-${id}` })),
};
}
function cloneTrusted<T>(value: T): T {
if (value === null || typeof value !== "object") return value;
if (Array.isArray(value)) return value.map((child) => cloneTrusted(child)) as T;
const result = Object.create(Object.getPrototypeOf(value)) as Record<string, unknown>;
for (const key of Object.keys(value)) {
Object.defineProperty(result, key, {
value: cloneTrusted((value as Record<string, unknown>)[key]),
writable: true,
enumerable: true,
configurable: true,
});
}
return result as T;
}
function sum(document: Document | Draft<Document>): number {
let checksum = 0;
for (let index = 0; index < document.rows.length; index++) checksum += document.rows[index]!.value;
return checksum;
}
async function gc(): Promise<void> {
assert.ok(global.gc, "benchmark worker requires --expose-gc");
for (let index = 0; index < 3; index++) {
await setImmediate();
global.gc();
}
}
await gc();
const baselineHeap = process.memoryUsage().heapUsed;
let input: Document | undefined = fixture(size);
if (scenario === "sort") input.rows.reverse();
const importStart = performance.now();
const tracker = track(input) as TrackerLike;
const importMs = performance.now() - importStart;
input = undefined;
await gc();
const readyHeap = process.memoryUsage().heapUsed;
let peakHeap = readyHeap;
let mutateMs = 0;
let prepareMs = 0;
let serializeMs = 0;
let adoptMs = 0;
let acquireMs = 0;
let enqueueMs = 0;
let deliveryMs = 0;
let opBytes = 0;
let operationCount = 0;
let checksum = 0;
let authoritySequence = 0;
let postAcquireRetainedMiB = 0;
let postLagRetainedMiB = 0;
let postDeliveryRetainedMiB = 0;
let sampledAcquireMiB = 0;
let watchers: WatchReplica[] = [];
let borrowedListeners: Array<(value: Document, sequence: number) => number> = [];
const queueOperations = Math.min(size, 1_000);
const watchCount = WATCH_SCENARIOS.has(scenario) ? requestedWatchCount : 0;
const watchUpdateTarget = WATCH_SCENARIOS.has(scenario) ? requestedWatchUpdates : 0;
const samplePeak = (): void => {
peakHeap = Math.max(peakHeap, process.memoryUsage().heapUsed);
};
const retainedFromBaseline = async (): Promise<number> => {
await gc();
return (process.memoryUsage().heapUsed - baselineHeap) / MiB;
};
const createWatch = (value: Document): WatchReplica => ({
value,
baseSequence: authoritySequence,
sequence: authoritySequence,
queue: [],
});
const acquireSharedWatches = (): void => {
const beforeHeap = process.memoryUsage().heapUsed;
const start = performance.now();
const base = tracker.value;
watchers = Array.from({ length: watchCount }, () => createWatch(base));
acquireMs += performance.now() - start;
sampledAcquireMiB = Math.max(sampledAcquireMiB, (process.memoryUsage().heapUsed - beforeHeap) / MiB);
for (const watcher of watchers) assert.equal(watcher.value, tracker.value);
samplePeak();
};
const acquireDetachedWatches = (): void => {
const beforeHeap = process.memoryUsage().heapUsed;
const start = performance.now();
watchers = Array.from({ length: watchCount }, () => createWatch(cloneTrusted(tracker.value)));
acquireMs += performance.now() - start;
sampledAcquireMiB = Math.max(sampledAcquireMiB, (process.memoryUsage().heapUsed - beforeHeap) / MiB);
for (let left = 0; left < watchers.length; left++) {
assert.notEqual(watchers[left]!.value, tracker.value);
for (let right = left + 1; right < watchers.length; right++) {
assert.notEqual(watchers[left]!.value, watchers[right]!.value);
}
}
samplePeak();
};
const acquireOneWatch = (): void => {
const beforeHeap = process.memoryUsage().heapUsed;
const start = performance.now();
watchers.push(createWatch(tracker.value));
acquireMs += performance.now() - start;
sampledAcquireMiB = Math.max(sampledAcquireMiB, (process.memoryUsage().heapUsed - beforeHeap) / MiB);
samplePeak();
};
const commitWatchUpdate = (update: number): Frame => {
let change: ChangeLike | undefined = tracker.beginChange();
const row = ((update + 1) * 997) % size;
const mutateStart = performance.now();
change.state.rows[row]!.value = -update - 1;
mutateMs += performance.now() - mutateStart;
samplePeak();
const prepareStart = performance.now();
let prepared: PreparedLike | undefined = change.prepare();
prepareMs += performance.now() - prepareStart;
samplePeak();
const operations = prepared.ops;
operationCount += operations.length;
const adoptStart = performance.now();
tracker.adopt(prepared);
adoptMs += performance.now() - adoptStart;
samplePeak();
change = undefined;
prepared = undefined;
authoritySequence += 1;
return { sequence: authoritySequence, ops: operations };
};
const enqueueFrame = (frame: Frame): void => {
const start = performance.now();
for (const watcher of watchers) {
assert.equal(frame.sequence, watcher.sequence + watcher.queue.length + 1);
watcher.queue.push(frame);
}
enqueueMs += performance.now() - start;
};
const applyFrame = (watcher: WatchReplica, frame: Frame): void => {
assert.equal(frame.sequence, watcher.sequence + 1);
watcher.value = applyImmutable(watcher.value, frame.ops);
watcher.sequence = frame.sequence;
};
const drainWatchQueues = (measure: boolean): void => {
const start = performance.now();
for (const watcher of watchers) {
for (const frame of watcher.queue) applyFrame(watcher, frame);
watcher.queue.length = 0;
samplePeak();
}
if (measure) deliveryMs += performance.now() - start;
};
const drainWatchQueuesCoalesced = (): void => {
const start = performance.now();
for (const watcher of watchers) {
const operations = watcher.queue.flatMap((frame) => frame.ops);
watcher.value = applyImmutable(watcher.value, operations);
watcher.sequence = watcher.queue.at(-1)?.sequence ?? watcher.sequence;
watcher.queue.length = 0;
samplePeak();
}
deliveryMs += performance.now() - start;
};
const assertWatchConvergence = (): void => {
for (const watcher of watchers) {
assert.equal(watcher.sequence, authoritySequence);
assert.deepEqual(watcher.value, tracker.value);
}
};
if (scenario === "committed-read") {
const start = performance.now();
checksum = sum(tracker.value);
mutateMs = performance.now() - start;
samplePeak();
} else if (scenario === "watch-authority-control") {
for (let update = 0; update < watchUpdateTarget; update++) commitWatchUpdate(update);
postDeliveryRetainedMiB = await retainedFromBaseline();
checksum = sum(tracker.value);
} else if (scenario === "watch-live-borrowed") {
const start = performance.now();
borrowedListeners = Array.from({ length: watchCount }, () => (value, sequence) => value.rows[0]!.value + sequence);
for (const listener of borrowedListeners) checksum += listener(tracker.value, authoritySequence);
acquireMs = performance.now() - start;
postAcquireRetainedMiB = await retainedFromBaseline();
for (let update = 0; update < watchUpdateTarget; update++) {
commitWatchUpdate(update);
const deliveryStart = performance.now();
for (const listener of borrowedListeners) checksum += listener(tracker.value, authoritySequence);
deliveryMs += performance.now() - deliveryStart;
samplePeak();
}
postDeliveryRetainedMiB = await retainedFromBaseline();
} else if (scenario === "watch-acquire-shared" || scenario === "watch-acquire-detached") {
if (scenario === "watch-acquire-shared") acquireSharedWatches();
else acquireDetachedWatches();
postAcquireRetainedMiB = await retainedFromBaseline();
checksum = sum(watchers[0]!.value) + watchers.length;
} else if (scenario === "watch-acquire-staggered") {
for (let watcher = 0; watcher < watchCount; watcher++) {
acquireOneWatch();
if (watcher + 1 === watchCount) break;
const frame = commitWatchUpdate(watcher);
enqueueFrame(frame);
}
for (let watcher = 0; watcher < watchers.length; watcher++) {
assert.equal(watchers[watcher]!.baseSequence, watcher);
assert.equal(watchers[watcher]!.queue.length, watchCount - watcher - 1);
}
postAcquireRetainedMiB = await retainedFromBaseline();
drainWatchQueues(false);
assertWatchConvergence();
postDeliveryRetainedMiB = await retainedFromBaseline();
checksum = sum(watchers[0]!.value) + sum(watchers.at(-1)!.value);
} else if (
scenario === "watch-lag" ||
scenario === "watch-catch-up" ||
scenario === "watch-catch-up-coalesced" ||
scenario === "watch-live-immutable"
) {
acquireSharedWatches();
postAcquireRetainedMiB = await retainedFromBaseline();
for (let update = 0; update < watchUpdateTarget; update++) {
const frame = commitWatchUpdate(update);
if (scenario === "watch-live-immutable") {
const start = performance.now();
for (const watcher of watchers) applyFrame(watcher, frame);
deliveryMs += performance.now() - start;
samplePeak();
} else enqueueFrame(frame);
}
postLagRetainedMiB = await retainedFromBaseline();
if (scenario === "watch-catch-up-coalesced") drainWatchQueuesCoalesced();
else if (scenario !== "watch-live-immutable") drainWatchQueues(scenario === "watch-catch-up");
assertWatchConvergence();
postDeliveryRetainedMiB = await retainedFromBaseline();
checksum = sum(watchers[0]!.value) + sum(tracker.value);
} else if (scenario !== "import") {
let change: ChangeLike | undefined = tracker.beginChange();
const start = performance.now();
switch (scenario) {
case "draft-read":
checksum = sum(change.state);
break;
case "sparse":
for (let index = 0; index < size; index += 1_000) change.state.rows[index]!.value = -index;
checksum = change.state.rows.at(-1)!.value;
break;
case "queue":
for (let index = 0; index < queueOperations; index++) {
change.state.rows.shift();
change.state.rows.push({ id: size + index, value: index, payload: "queue" });
}
checksum = change.state.rows.length;
break;
case "sort":
change.state.rows.sort((left, right) => left.id - right.id);
checksum = change.state.rows[0]!.id;
break;
case "dense":
for (let index = 0; index < size; index++) change.state.rows[index]!.value = -index;
checksum = change.state.rows.at(-1)!.value;
break;
case "unshift": {
const inserted = Array.from({ length: 100_000 }, (_, id) => ({ id: -id, value: id, payload: "inserted" }));
Reflect.apply(change.state.rows.unshift, change.state.rows, inserted);
checksum = change.state.rows.length;
break;
}
default:
throw new Error(`Unhandled benchmark scenario ${scenario}`);
}
mutateMs = performance.now() - start;
samplePeak();
const prepareStart = performance.now();
let prepared: PreparedLike | undefined = change.prepare();
prepareMs = performance.now() - prepareStart;
samplePeak();
const serializeStart = performance.now();
const serialized = JSON.stringify(prepared.ops);
serializeMs = performance.now() - serializeStart;
opBytes = Buffer.byteLength(serialized);
operationCount = prepared.ops.length;
samplePeak();
const adoptStart = performance.now();
tracker.adopt(prepared);
adoptMs = performance.now() - adoptStart;
change = undefined;
prepared = undefined;
}
const scenarioWorkMs = mutateMs + prepareMs + serializeMs + adoptMs + acquireMs + enqueueMs + deliveryMs;
samplePeak();
await gc();
const retainedHeap = process.memoryUsage().heapUsed;
const retainedMiB = (retainedHeap - baselineHeap) / MiB;
const retainedOverReadyMiB = (retainedHeap - readyHeap) / MiB;
const watcherRoots = watchers.length;
const queuedBatchReferences = watchers.reduce((total, watcher) => total + watcher.queue.length, 0);
const listenerCount = borrowedListeners.length;
watchers = [];
borrowedListeners = [];
await gc();
const releasedRetainedMiB = (process.memoryUsage().heapUsed - baselineHeap) / MiB;
console.log(
JSON.stringify({
mode,
scenario,
size,
trial,
queueOperations,
watchProfile: WATCH_SCENARIOS.has(scenario) ? "local-sparse-array-leaf" : null,
watchCount: watcherRoots + listenerCount,
watchUpdates: authoritySequence,
queuedBatchReferences,
importMs,
mutateMs,
prepareMs,
serializeMs,
adoptMs,
acquireMs,
enqueueMs,
deliveryMs,
scenarioWorkMs,
opBytes,
operationCount,
checksum,
readyMiB: (readyHeap - baselineHeap) / MiB,
sampledAcquireMiB,
sampledTransientMiB: (peakHeap - readyHeap) / MiB,
retainedMiB,
retainedOverReadyMiB,
postAcquireRetainedMiB,
postLagRetainedMiB,
postDeliveryRetainedMiB,
releasedRetainedMiB,
maxRssMiB: process.resourceUsage().maxRSS / 1024,
}),
);
@@ -0,0 +1,337 @@
import { spawnSync } from "node:child_process";
import { writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
// Provisional Pico5 benchmark schemas. Results are architecture experiments,
// not compatibility commitments for public or persisted ConversationView data.
const PROVISIONAL_SCHEMA = "pico5.conversation-view.provisional.v0";
type Mode = "tracker";
type Scenario =
| "projection-control"
| "normal"
| "fanout"
| "delayed-start-lag"
| "churn-reconnect"
| "overflow-reset"
| "branch-head-replacement";
type Distribution = {
count: number;
totalMs: number;
medianMs: number;
p95Ms: number;
p99Ms: number;
minMs: number;
maxMs: number;
};
type PhaseName =
| "mutation"
| "prepare"
| "adopt"
| "synthesize"
| "projectApply"
| "project"
| "enqueue"
| "sessionLine"
| "delivery"
| "opSerialize"
| "resetSerialize"
| "conversationAcquire"
| "documentAcquire"
| "conversationSerialize"
| "documentSerialize"
| "resetClone";
type Result = {
mode: Mode;
scenario: Scenario;
trial: number;
provisionalSchema: string;
headlineEntries: number;
commitTarget: number;
initialAggregateBytes: number;
conversationWatchCount: number;
documentWatchCount: number;
queueCapacity: number | null;
queueCapacityUnit: "pending-operations";
deliveryModel: "async-serialized-ops-only-no-consumer-apply";
conversationSequence: number;
documentSequences: Record<string, number>;
publishedAggregateBatches: number;
publishedDocumentBatches: number;
conversationRegistrations: number;
documentRegistrations: number;
conversationBaseBytes: number;
documentBaseBytes: number;
noOpCommitCount: number;
unmountedCommitCount: number;
resetCount: number;
resetBytes: number;
deliveryFrameReferences: number;
deliveryOperationReferences: number;
aggregateOperationCount: number;
aggregateOperationBytes: number;
documentOperationCount: number;
documentOperationBytes: number;
peakQueueReferences: number;
peakQueuedOperationReferences: number;
peakPendingOperationsPerWatch: number;
peakUniqueQueuedBatches: number;
finalQueueReferences: number;
finalQueuedOperationReferences: number;
finalUniqueQueuedBatches: number;
phaseDistributions: Record<PhaseName, Distribution>;
authorityReadyMiB: number;
postAcquireRetainedMiB: number;
postWorkRetainedMiB: number;
postVerificationRetainedMiB: number;
releaseHeapMiB: number;
releaseTotalMiB: number;
peakHeapMiB: number;
peakTotalHeapMiB: number;
maxRssMiB: number;
checksum: number;
};
type Failure = { mode: Mode; scenario: Scenario; trial: number; stderr: string };
type NumericResultKey =
| "initialAggregateBytes"
| "conversationSequence"
| "publishedAggregateBatches"
| "publishedDocumentBatches"
| "conversationRegistrations"
| "documentRegistrations"
| "conversationBaseBytes"
| "documentBaseBytes"
| "noOpCommitCount"
| "unmountedCommitCount"
| "resetCount"
| "resetBytes"
| "deliveryFrameReferences"
| "deliveryOperationReferences"
| "aggregateOperationCount"
| "aggregateOperationBytes"
| "documentOperationCount"
| "documentOperationBytes"
| "peakQueueReferences"
| "peakQueuedOperationReferences"
| "peakPendingOperationsPerWatch"
| "peakUniqueQueuedBatches"
| "finalQueueReferences"
| "finalQueuedOperationReferences"
| "finalUniqueQueuedBatches"
| "authorityReadyMiB"
| "postAcquireRetainedMiB"
| "postWorkRetainedMiB"
| "postVerificationRetainedMiB"
| "releaseHeapMiB"
| "releaseTotalMiB"
| "peakHeapMiB"
| "peakTotalHeapMiB"
| "maxRssMiB";
type SummaryValue = { median: number | null; range: readonly [number, number] | null };
const modes: readonly Mode[] = ["tracker"];
const scenarios: readonly Scenario[] = [
"projection-control",
"normal",
"fanout",
"delayed-start-lag",
"churn-reconnect",
"overflow-reset",
"branch-head-replacement",
];
const phaseNames: readonly PhaseName[] = [
"mutation",
"prepare",
"adopt",
"synthesize",
"projectApply",
"project",
"enqueue",
"sessionLine",
"delivery",
"opSerialize",
"resetSerialize",
"conversationAcquire",
"documentAcquire",
"conversationSerialize",
"documentSerialize",
"resetClone",
];
const numericMetrics: readonly NumericResultKey[] = [
"initialAggregateBytes",
"conversationSequence",
"publishedAggregateBatches",
"publishedDocumentBatches",
"conversationRegistrations",
"documentRegistrations",
"conversationBaseBytes",
"documentBaseBytes",
"noOpCommitCount",
"unmountedCommitCount",
"resetCount",
"resetBytes",
"deliveryFrameReferences",
"deliveryOperationReferences",
"aggregateOperationCount",
"aggregateOperationBytes",
"documentOperationCount",
"documentOperationBytes",
"peakQueueReferences",
"peakQueuedOperationReferences",
"peakPendingOperationsPerWatch",
"peakUniqueQueuedBatches",
"finalQueueReferences",
"finalQueuedOperationReferences",
"finalUniqueQueuedBatches",
"authorityReadyMiB",
"postAcquireRetainedMiB",
"postWorkRetainedMiB",
"postVerificationRetainedMiB",
"releaseHeapMiB",
"releaseTotalMiB",
"peakHeapMiB",
"peakTotalHeapMiB",
"maxRssMiB",
];
function optionNumber(name: string, fallback: number): number {
const prefix = `--${name}=`;
const raw = process.argv.find((argument) => argument.startsWith(prefix))?.slice(prefix.length);
if (raw === undefined) return fallback;
const value = Number(raw);
if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`Invalid --${name}`);
return value;
}
function median(values: readonly number[]): number | null {
if (values.length === 0) return null;
const ordered = [...values].sort((left, right) => left - right);
return ordered[Math.floor(ordered.length / 2)]!;
}
function range(values: readonly number[]): readonly [number, number] | null {
return values.length === 0 ? null : [Math.min(...values), Math.max(...values)];
}
function summarize(values: readonly number[]): SummaryValue {
return { median: median(values), range: range(values) };
}
const quick = process.argv.includes("--quick");
const selectedScenario = process.argv
.find((argument) => argument.startsWith("--scenario="))
?.slice("--scenario=".length);
if (selectedScenario !== undefined && !(scenarios as readonly string[]).includes(selectedScenario)) {
throw new Error(`Unknown scenario ${selectedScenario}`);
}
const selectedScenarios = selectedScenario === undefined ? scenarios : [selectedScenario as Scenario];
const headlineEntries = optionNumber("entries", quick ? 128 : 512);
const commits = optionNumber("commits", quick ? 120 : 1_000);
const trials = optionNumber("trials", quick ? 1 : 3);
const queueOperationCapacity = quick ? 64 : 256;
const root = fileURLToPath(new URL("../../../..", import.meta.url));
const worker = fileURLToPath(new URL("./conversation-view-benchmark.worker.ts", import.meta.url));
const results: Result[] = [];
const failures: Failure[] = [];
for (let trial = 0; trial < trials; trial++) {
for (const scenario of selectedScenarios) {
const offset = trial % modes.length;
const trialModes = [...modes.slice(offset), ...modes.slice(0, offset)];
for (const mode of trialModes) {
const child = spawnSync(
process.execPath,
[
"--expose-gc",
"--max-old-space-size=4096",
worker,
mode,
scenario,
String(headlineEntries),
String(commits),
String(trial),
String(queueOperationCapacity),
],
{ cwd: root, encoding: "utf8", timeout: 300_000, maxBuffer: 20 * 1024 * 1024 },
);
if (child.status !== 0) {
const failure = { mode, scenario, trial, stderr: child.stderr } satisfies Failure;
failures.push(failure);
console.log(JSON.stringify(failure));
continue;
}
const result = JSON.parse(child.stdout) as Result;
results.push(result);
console.log(JSON.stringify(result));
}
}
}
const summary = selectedScenarios.flatMap((scenario) =>
modes.map((mode) => {
const selected = results.filter((result) => result.scenario === scenario && result.mode === mode);
const metrics: Record<string, SummaryValue> = {};
for (const metric of numericMetrics) metrics[metric] = summarize(selected.map((result) => result[metric]));
const phases: Record<
string,
{ medianMs: SummaryValue; p95Ms: SummaryValue; p99Ms: SummaryValue; totalMs: SummaryValue }
> = {};
for (const phase of phaseNames) {
phases[phase] = {
medianMs: summarize(selected.map((result) => result.phaseDistributions[phase].medianMs)),
p95Ms: summarize(selected.map((result) => result.phaseDistributions[phase].p95Ms)),
p99Ms: summarize(selected.map((result) => result.phaseDistributions[phase].p99Ms)),
totalMs: summarize(selected.map((result) => result.phaseDistributions[phase].totalMs)),
};
}
return {
mode,
scenario,
provisionalSchema: PROVISIONAL_SCHEMA,
conversationWatchCount: selected[0]?.conversationWatchCount ?? 0,
documentWatchCount: selected[0]?.documentWatchCount ?? 0,
queueCapacity: selected[0]?.queueCapacity ?? null,
queueCapacityUnit: "pending-operations",
deliveryModel: "async-serialized-ops-only-no-consumer-apply",
metrics,
phases,
};
}),
);
const output = join(tmpdir(), "chord-conversation-view-benchmark.json");
writeFileSync(
output,
JSON.stringify(
{
node: process.version,
provisionalSchema: PROVISIONAL_SCHEMA,
profile: "local-pico5-provisional-conversation-view",
headlineEntries,
commits,
trials,
queueOperationCapacity,
results,
failures,
summary,
},
null,
2,
),
);
console.log(
JSON.stringify({
output,
node: process.version,
provisionalSchema: PROVISIONAL_SCHEMA,
headlineEntries,
commits,
trials,
queueOperationCapacity,
failures: failures.length,
summary,
}),
);
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,142 @@
import { spawnSync } from "node:child_process";
import { writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
type Mode = "tracker";
type Scenario = "idle-control" | "sparse-burst" | "staggered-revisions" | "lagging-watches";
type Result = {
mode: Mode;
scenario: Scenario;
trial: number;
size: number;
updates: number;
watchCount: number;
workMs: number;
acquisitionMs: number;
gcCount: number;
gcMs: number;
readyHeapMiB: number;
readyRssMiB: number;
peakOverReadyHeapMiB: number;
peakOverProcessBaselineHeapMiB: number;
peakOverReadyRssMiB: number;
retainedOverReadyHeapMiB: number;
retainedOverProcessBaselineHeapMiB: number;
retainedOverReadyRssMiB: number;
snapshotRetainedHeapMiB: number;
afterSnapshotReleaseOverReadyHeapMiB: number;
afterSnapshotReleaseOverProcessBaselineHeapMiB: number;
releasedOverProcessBaselineHeapMiB: number;
releasedOverProcessBaselineRssMiB: number;
maxRssMiB: number;
checksum: number;
};
type Failure = { mode: Mode; scenario: Scenario; trial: number; stderr: string };
const modes: readonly Mode[] = ["tracker"];
const scenarios: readonly Scenario[] = ["idle-control", "sparse-burst", "staggered-revisions", "lagging-watches"];
const metrics = [
"workMs",
"acquisitionMs",
"gcCount",
"gcMs",
"readyHeapMiB",
"readyRssMiB",
"peakOverReadyHeapMiB",
"peakOverProcessBaselineHeapMiB",
"peakOverReadyRssMiB",
"retainedOverReadyHeapMiB",
"retainedOverProcessBaselineHeapMiB",
"retainedOverReadyRssMiB",
"snapshotRetainedHeapMiB",
"afterSnapshotReleaseOverReadyHeapMiB",
"afterSnapshotReleaseOverProcessBaselineHeapMiB",
"releasedOverProcessBaselineHeapMiB",
"releasedOverProcessBaselineRssMiB",
"maxRssMiB",
] as const satisfies readonly (keyof Result)[];
function median(values: readonly number[]): number | null {
if (values.length === 0) return null;
const ordered = [...values].sort((left, right) => left - right);
return ordered[Math.floor(ordered.length / 2)]!;
}
function range(values: readonly number[]): readonly [number, number] | null {
return values.length === 0 ? null : [Math.min(...values), Math.max(...values)];
}
const quick = process.argv.includes("--quick");
const size = quick ? 10_000 : 100_000;
const updates = quick ? 20 : 100;
const watchCount = quick ? 4 : 8;
const trials = quick ? 1 : 3;
const root = fileURLToPath(new URL("../../../..", import.meta.url));
const worker = fileURLToPath(new URL("./memory-benchmark.worker.ts", import.meta.url));
const results: Result[] = [];
const failures: Failure[] = [];
for (let trial = 0; trial < trials; trial++) {
for (const scenario of scenarios) {
const offset = trial % modes.length;
const trialModes = [...modes.slice(offset), ...modes.slice(0, offset)];
for (const mode of trialModes) {
const child = spawnSync(
process.execPath,
[
"--expose-gc",
"--no-flush-bytecode",
"--max-old-space-size=4096",
worker,
mode,
scenario,
String(size),
String(updates),
String(watchCount),
String(trial),
],
{ cwd: root, encoding: "utf8", timeout: 300_000, maxBuffer: 10 * 1024 * 1024 },
);
if (child.status !== 0) {
const failure = { mode, scenario, trial, stderr: child.stderr } satisfies Failure;
failures.push(failure);
console.log(JSON.stringify(failure));
continue;
}
const result = JSON.parse(child.stdout) as Result;
results.push(result);
console.log(JSON.stringify(result));
}
}
}
const summary = scenarios.flatMap((scenario) =>
modes.map((mode) => {
const selected = results.filter((result) => result.scenario === scenario && result.mode === mode);
const values: Record<string, { median: number | null; range: readonly [number, number] | null }> = {};
for (const metric of metrics) {
const samples = selected.map((result) => result[metric]);
values[metric] = { median: median(samples), range: range(samples) };
}
return { scenario, mode, metrics: values };
}),
);
const output = join(tmpdir(), "chord-delta-memory-benchmark.json");
writeFileSync(
output,
JSON.stringify({ node: process.version, size, updates, watchCount, trials, results, failures, summary }, null, 2),
);
console.log(
JSON.stringify({
output,
node: process.version,
size,
updates,
watchCount,
trials,
failures: failures.length,
summary,
}),
);
@@ -0,0 +1,163 @@
import assert from "node:assert/strict";
import { PerformanceObserver, performance } from "node:perf_hooks";
import { setImmediate } from "node:timers/promises";
import type { Draft } from "../../src/delta/draft.ts";
import { type Op, track } from "../../src/delta/index.ts";
const MiB = 1024 * 1024;
type Mode = "tracker";
type Scenario = "idle-control" | "sparse-burst" | "staggered-revisions" | "lagging-watches";
type Row = { id: number; value: number; payload: string };
type Document = { rows: Row[] };
type PreparedLike = { readonly value: Document; readonly ops: readonly Op[] };
type ChangeLike = { readonly state: Draft<Document>; prepare(): PreparedLike };
type TrackerLike = { readonly value: Document; beginChange(): ChangeLike; adopt(prepared: PreparedLike): void };
const MODES = new Set<Mode>(["tracker"]);
const SCENARIOS = new Set<Scenario>(["idle-control", "sparse-burst", "staggered-revisions", "lagging-watches"]);
const mode = process.argv[2] as Mode;
const scenario = process.argv[3] as Scenario;
const size = Number(process.argv[4] ?? 100_000);
const updates = Number(process.argv[5] ?? 100);
const watchCount = Number(process.argv[6] ?? 8);
const trial = Number(process.argv[7] ?? 0);
if (!MODES.has(mode)) throw new Error(`Unknown mode ${mode}`);
if (!SCENARIOS.has(scenario)) throw new Error(`Unknown scenario ${scenario}`);
if (!Number.isSafeInteger(size) || size < 1) throw new RangeError("Invalid size");
if (!Number.isSafeInteger(updates) || updates < 1) throw new RangeError("Invalid update count");
if (!Number.isSafeInteger(watchCount) || watchCount < 1) throw new RangeError("Invalid watch count");
function fixture(count: number): Document {
return { rows: Array.from({ length: count }, (_, id) => ({ id, value: id, payload: `row-${id}` })) };
}
async function collect(): Promise<void> {
assert.ok(global.gc, "memory benchmark requires --expose-gc");
for (let attempt = 0; attempt < 3; attempt++) {
await setImmediate();
global.gc();
}
}
function createTracker(value: Document): TrackerLike {
return track(value) as TrackerLike;
}
function commitUpdate(tracker: TrackerLike, update: number): void {
const change = tracker.beginChange();
const row = ((update + 1) * 997) % size;
change.state.rows[row]!.value = -update - 1;
const prepared = change.prepare();
tracker.adopt(prepared);
}
function acquireSnapshot(tracker: TrackerLike): Document {
return tracker.value;
}
let warmup: TrackerLike | undefined = createTracker(fixture(16));
const warmupChange = warmup.beginChange();
warmupChange.state.rows[0]!.value = -1;
const warmupPrepared = warmupChange.prepare();
warmup.adopt(warmupPrepared);
warmup = undefined;
await collect();
const processBaseline = process.memoryUsage();
let input: Document | undefined = fixture(size);
let tracker: TrackerLike | undefined = createTracker(input);
input = undefined;
await collect();
const ready = process.memoryUsage();
let peakHeap = ready.heapUsed;
let peakRss = ready.rss;
let acquisitionMs = 0;
let snapshots: Document[] = [];
let gcCount = 0;
let gcMs = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
gcCount += 1;
gcMs += entry.duration;
}
});
observer.observe({ entryTypes: ["gc"] });
const samplePeak = (): void => {
const memory = process.memoryUsage();
peakHeap = Math.max(peakHeap, memory.heapUsed);
peakRss = Math.max(peakRss, memory.rss);
};
const acquire = (): void => {
const start = performance.now();
snapshots.push(acquireSnapshot(tracker!));
acquisitionMs += performance.now() - start;
samplePeak();
};
const workStart = performance.now();
if (scenario === "idle-control") {
for (let update = 0; update < updates; update++) samplePeak();
} else if (scenario === "sparse-burst") {
for (let update = 0; update < updates; update++) {
commitUpdate(tracker, update);
samplePeak();
}
} else if (scenario === "staggered-revisions") {
for (let watcher = 0; watcher < watchCount; watcher++) {
acquire();
if (watcher + 1 < watchCount) commitUpdate(tracker, watcher);
samplePeak();
}
} else {
for (let watcher = 0; watcher < watchCount; watcher++) acquire();
for (let update = 0; update < updates; update++) {
commitUpdate(tracker, update);
samplePeak();
}
}
const workMs = performance.now() - workStart;
for (let attempt = 0; attempt < 3; attempt++) await setImmediate();
observer.disconnect();
samplePeak();
await collect();
const retained = process.memoryUsage();
const checksum = tracker.value.rows[0]!.value + tracker.value.rows.at(-1)!.value + snapshots.length;
snapshots = [];
await collect();
const afterSnapshotRelease = process.memoryUsage();
tracker = undefined;
await collect();
const released = process.memoryUsage();
console.log(
JSON.stringify({
mode,
scenario,
trial,
size,
updates: scenario === "staggered-revisions" ? watchCount - 1 : scenario === "idle-control" ? 0 : updates,
watchCount: scenario === "staggered-revisions" || scenario === "lagging-watches" ? watchCount : 0,
workMs,
acquisitionMs,
gcCount,
gcMs,
readyHeapMiB: (ready.heapUsed - processBaseline.heapUsed) / MiB,
readyRssMiB: (ready.rss - processBaseline.rss) / MiB,
peakOverReadyHeapMiB: (peakHeap - ready.heapUsed) / MiB,
peakOverProcessBaselineHeapMiB: (peakHeap - processBaseline.heapUsed) / MiB,
peakOverReadyRssMiB: (peakRss - ready.rss) / MiB,
retainedOverReadyHeapMiB: (retained.heapUsed - ready.heapUsed) / MiB,
retainedOverProcessBaselineHeapMiB: (retained.heapUsed - processBaseline.heapUsed) / MiB,
retainedOverReadyRssMiB: (retained.rss - ready.rss) / MiB,
snapshotRetainedHeapMiB: (retained.heapUsed - afterSnapshotRelease.heapUsed) / MiB,
afterSnapshotReleaseOverReadyHeapMiB: (afterSnapshotRelease.heapUsed - ready.heapUsed) / MiB,
afterSnapshotReleaseOverProcessBaselineHeapMiB: (afterSnapshotRelease.heapUsed - processBaseline.heapUsed) / MiB,
releasedOverProcessBaselineHeapMiB: (released.heapUsed - processBaseline.heapUsed) / MiB,
releasedOverProcessBaselineRssMiB: (released.rss - processBaseline.rss) / MiB,
maxRssMiB: process.resourceUsage().maxRSS / 1024,
checksum,
}),
);