chore(vendor): import pi 0.85.1

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: d981de1229ef899957bbe968bc8dcda02a21f477
This commit is contained in:
2026-09-14 10:48:47 -04:00
parent 5ebc7018c3
commit 4def357d09
1669 changed files with 397627 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
# Session benchmarks
Run these commands from `packages/agent`.
These benchmarks use deterministic synthetic data and make no claim to reproduce production traffic. Storage and repository fork datasets are linear branches of user-message entries with fixed 256-byte text payloads, deterministic IDs, and 250-entry seed transactions. Repository catalog datasets contain deterministic closed sessions.
## Timing
The timing suite runs scenarios against every target in `storage-targets.ts` and `session-repo-targets.ts`. Read scenarios reuse one immutable fixture per backend/dataset and validate their result once before timing. Every warmup and measured write runs once against an independently prepared fixture, so entry counts, sequence numbers, and durable artifacts start in equivalent states. Implementations are registered under the same Vitest suite so additional backends are directly comparable.
Storage writes cover a single message, a 100-message transaction, and a mixed message/register/usage append to a 1k-entry synthetic branch. Repository scenarios cover creating an empty session, opening or deleting a closed empty session, listing 100, 1k, and 10k closed sessions, and forking the current branch of open 1k- and 10k-entry source sessions. Fixture preparation, transaction generation, and validation happen outside the measured callback.
```sh
npm run bench:session:timing
npm run bench:session:timing -- -t "scan latest"
```
## Loaded footprint
The memory profile starts a fresh Node.js process with forced GC for every backend/dataset pair. Its baseline is recorded before generating and ingesting the dataset, so the result includes retained payload data and backend structures. Temporary generation data should be collected before the final reading, while RSS can still reflect allocator growth.
```sh
npm run bench:session:memory
```
## Storage allocation sampling
The allocation profile prebuilds all transactions, then samples allocations made while committing them. This excludes synthetic payload generation and reports estimated cumulative allocated bytes, including objects later collected, plus the largest sampled allocation sites. V8 sampling is approximate and does not report an exact object count.
```sh
npm run bench:session:allocations
```
Timing results are not CI performance gates. Run them on an otherwise idle machine and compare results produced on the same hardware and Node.js version.

View File

@@ -0,0 +1,57 @@
import type { HeapProfiler } from "node:inspector";
import { Session } from "node:inspector/promises";
import {
STORAGE_BENCHMARK_DATASETS,
generateStorageBenchmarkSeedTransactions,
} from "../../src/harness/session/testing/index.ts";
import { storageBenchmarkTargets } from "./storage-targets.ts";
const SAMPLING_INTERVAL_BYTES = 4_096;
const TOP_SITE_COUNT = 5;
function collectAllocationSites(node: HeapProfiler.SamplingHeapProfileNode, sites: Map<string, number>): number {
const { functionName, url, lineNumber, columnNumber } = node.callFrame;
const site = `${functionName || "(anonymous)"} (${url || "native"}:${lineNumber + 1}:${columnNumber + 1})`;
if (node.selfSize > 0) sites.set(site, (sites.get(site) ?? 0) + node.selfSize);
let total = node.selfSize;
for (const child of node.children) total += collectAllocationSites(child, sites);
return total;
}
const [targetName, datasetName] = process.argv.slice(2);
const target = storageBenchmarkTargets.find((candidate) => candidate.name === targetName);
if (target === undefined) throw new Error(`Unknown storage benchmark target: ${targetName}`);
const dataset = STORAGE_BENCHMARK_DATASETS.find((candidate) => candidate.name === datasetName);
if (dataset === undefined) throw new Error(`Unknown storage benchmark dataset: ${datasetName}`);
// Inputs and the empty fixture exist before sampling so the profile measures
// allocation caused by Storage.commit(), not synthetic payload generation.
const transactions = [...generateStorageBenchmarkSeedTransactions(dataset)];
await using fixture = await target.createFixture();
const inspector = new Session();
inspector.connect();
try {
await inspector.post("HeapProfiler.startSampling", {
samplingInterval: SAMPLING_INTERVAL_BYTES,
includeObjectsCollectedByMajorGC: true,
includeObjectsCollectedByMinorGC: true,
});
for (const transaction of transactions) await fixture.storage.commit(transaction);
const { profile } = await inspector.post("HeapProfiler.stopSampling");
const sites = new Map<string, number>();
const allocatedBytes = collectAllocationSites(profile.head, sites);
console.log(
JSON.stringify({
target: target.name,
dataset: dataset.name,
entryCount: dataset.entryCount,
allocatedBytes,
topSites: [...sites.entries()]
.map(([site, bytes]) => ({ site, bytes }))
.sort((left, right) => right.bytes - left.bytes)
.slice(0, TOP_SITE_COUNT),
}),
);
} finally {
inspector.disconnect();
}

View File

@@ -0,0 +1,55 @@
import { execFile } from "node:child_process";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { STORAGE_BENCHMARK_DATASETS } from "../../src/harness/session/testing/index.ts";
import { storageBenchmarkTargets } from "./storage-targets.ts";
interface AllocationSiteResult {
readonly site: string;
readonly bytes: number;
}
interface AllocationProfileResult {
readonly target: string;
readonly dataset: string;
readonly entryCount: number;
readonly allocatedBytes: number;
readonly topSites: readonly AllocationSiteResult[];
}
const execFileAsync = promisify(execFile);
const packageRoot = fileURLToPath(new URL("../..", import.meta.url));
const workerPath = fileURLToPath(new URL("./allocation-profile-worker.ts", import.meta.url));
const tsconfigPath = fileURLToPath(new URL("../tsconfig.json", import.meta.url));
const results: AllocationProfileResult[] = [];
for (const target of storageBenchmarkTargets) {
for (const dataset of STORAGE_BENCHMARK_DATASETS) {
const { stdout } = await execFileAsync(
process.execPath,
["--import", "tsx", workerPath, target.name, dataset.name],
{ cwd: packageRoot, env: { ...process.env, TSX_TSCONFIG_PATH: tsconfigPath } },
);
results.push(JSON.parse(stdout) as AllocationProfileResult);
}
}
const mebibytes = (bytes: number): string => (bytes / 1024 / 1024).toFixed(2);
console.log("Storage allocation sampling (inputs generated before sampling):");
console.table(
results.map((result) => ({
backend: result.target,
dataset: result.dataset,
"allocated MiB": mebibytes(result.allocatedBytes),
"allocated bytes/entry": Math.round(result.allocatedBytes / result.entryCount),
})),
);
for (const result of results) {
console.log(`\n${result.target}, ${result.dataset}, top sampled allocation sites:`);
console.table(
result.topSites.map(({ site, bytes }) => ({
site,
"sampled MiB": mebibytes(bytes),
})),
);
}

View File

@@ -0,0 +1,179 @@
import { strictEqual } from "node:assert/strict";
import { bench, describe } from "vitest";
export interface BenchmarkTarget<TFixture extends AsyncDisposable> {
readonly name: string;
createFixture(): Promise<TFixture>;
}
const READ_BENCHMARK_OPTIONS = {
time: 500,
iterations: 10,
warmupTime: 100,
warmupIterations: 5,
} as const;
const WRITE_BENCHMARK_ITERATIONS = 30;
const WRITE_BENCHMARK_WARMUP_ITERATIONS = 5;
const WRITE_BENCHMARK_OPTIONS = {
time: 0,
iterations: WRITE_BENCHMARK_ITERATIONS,
warmupTime: 0,
warmupIterations: WRITE_BENCHMARK_WARMUP_ITERATIONS,
} as const;
interface NamedBenchmarkDataset {
readonly name: string;
}
interface ReadBenchmarkScenario<TSubject, TDataset> {
readonly name: string;
expectedResult(dataset: TDataset): number;
run(subject: TSubject, dataset: TDataset): Promise<number>;
}
interface RegisterReadBenchmarksOptions<
TDataset extends NamedBenchmarkDataset,
TFixture extends AsyncDisposable,
TSubject,
> {
readonly datasets: readonly TDataset[];
readonly targets: readonly BenchmarkTarget<TFixture>[];
readonly scenarios: readonly ReadBenchmarkScenario<TSubject, TDataset>[];
prepare(fixture: TFixture, dataset: TDataset): Promise<void>;
getSubject(fixture: TFixture): TSubject;
}
interface PreparedReadFixture<TFixture> {
readonly datasetName: string;
readonly targetName: string;
readonly fixture: TFixture;
}
interface NamedBenchmarkScenario {
readonly name: string;
}
interface RegisterWriteBenchmarksOptions<
TScenario extends NamedBenchmarkScenario,
TFixture extends AsyncDisposable,
TSubject,
> {
readonly targets: readonly BenchmarkTarget<TFixture>[];
readonly scenarios: readonly TScenario[];
prepare(fixture: TFixture, scenario: TScenario): Promise<TSubject>;
expectedResult(scenario: TScenario): number;
run(subject: TSubject, scenario: TScenario): Promise<number>;
}
interface PreparedWriteFixtures<TSubject> {
readonly scenarioName: string;
readonly targetName: string;
readonly pendingSubjects: TSubject[];
}
/** Prepares, validates, registers, and disposes one immutable fixture per target and dataset. */
export async function registerReadBenchmarks<
TDataset extends NamedBenchmarkDataset,
TFixture extends AsyncDisposable,
TSubject,
>(options: RegisterReadBenchmarksOptions<TDataset, TFixture, TSubject>): Promise<void> {
const fixtures: TFixture[] = [];
const preparedFixtures: PreparedReadFixture<TFixture>[] = [];
process.once("beforeExit", async () => {
await Promise.all(fixtures.map((fixture) => fixture[Symbol.asyncDispose]()));
});
for (const dataset of options.datasets) {
for (const target of options.targets) {
const fixture = await target.createFixture();
fixtures.push(fixture);
await options.prepare(fixture, dataset);
const subject = options.getSubject(fixture);
for (const scenario of options.scenarios) {
strictEqual(await scenario.run(subject, dataset), scenario.expectedResult(dataset));
}
preparedFixtures.push({ datasetName: dataset.name, targetName: target.name, fixture });
}
}
for (const dataset of options.datasets) {
for (const scenario of options.scenarios) {
describe(`${scenario.name} (${dataset.name})`, () => {
for (const target of options.targets) {
const prepared = preparedFixtures.find(
(candidate) => candidate.datasetName === dataset.name && candidate.targetName === target.name,
);
if (prepared === undefined) throw new Error("Benchmark fixture was not initialized");
const subject = options.getSubject(prepared.fixture);
bench(
target.name,
async () => {
await scenario.run(subject, dataset);
},
READ_BENCHMARK_OPTIONS,
);
}
});
}
}
}
/** Prepares, validates, registers, and disposes one equivalent fixture per write invocation. */
export async function registerWriteBenchmarks<
TScenario extends NamedBenchmarkScenario,
TFixture extends AsyncDisposable,
TSubject,
>(options: RegisterWriteBenchmarksOptions<TScenario, TFixture, TSubject>): Promise<void> {
const fixtures: TFixture[] = [];
const preparedFixtures: PreparedWriteFixtures<TSubject>[] = [];
process.once("beforeExit", async () => {
await Promise.all(fixtures.map((fixture) => fixture[Symbol.asyncDispose]()));
});
for (const scenario of options.scenarios) {
for (const target of options.targets) {
const validationFixture = await target.createFixture();
fixtures.push(validationFixture);
const validationSubject = await options.prepare(validationFixture, scenario);
strictEqual(await options.run(validationSubject, scenario), options.expectedResult(scenario));
const pendingSubjects: TSubject[] = [];
for (
let index = 0;
index < WRITE_BENCHMARK_WARMUP_ITERATIONS + WRITE_BENCHMARK_ITERATIONS;
index++
) {
const fixture = await target.createFixture();
fixtures.push(fixture);
pendingSubjects.push(await options.prepare(fixture, scenario));
}
preparedFixtures.push({ scenarioName: scenario.name, targetName: target.name, pendingSubjects });
}
}
for (const scenario of options.scenarios) {
describe(scenario.name, () => {
for (const target of options.targets) {
const prepared = preparedFixtures.find(
(candidate) => candidate.scenarioName === scenario.name && candidate.targetName === target.name,
);
if (prepared === undefined) throw new Error("Benchmark fixtures were not initialized");
bench(
target.name,
async () => {
const subject = prepared.pendingSubjects.shift();
if (subject === undefined) throw new Error("Benchmark fixture pool was exhausted");
await options.run(subject, scenario);
},
WRITE_BENCHMARK_OPTIONS,
);
}
});
}
}

View File

@@ -0,0 +1,34 @@
import {
STORAGE_BENCHMARK_DATASETS,
seedStorageBenchmark,
} from "../../src/harness/session/testing/index.ts";
import { storageBenchmarkTargets } from "./storage-targets.ts";
function collectGarbage(): void {
if (globalThis.gc === undefined) throw new Error("Loaded footprint measurement requires Node.js --expose-gc");
for (let index = 0; index < 3; index++) globalThis.gc();
}
const [targetName, datasetName] = process.argv.slice(2);
const target = storageBenchmarkTargets.find((candidate) => candidate.name === targetName);
if (target === undefined) throw new Error(`Unknown storage benchmark target: ${targetName}`);
const dataset = STORAGE_BENCHMARK_DATASETS.find((candidate) => candidate.name === datasetName);
if (dataset === undefined) throw new Error(`Unknown storage benchmark dataset: ${datasetName}`);
await using fixture = await target.createFixture();
collectGarbage();
const before = process.memoryUsage();
await seedStorageBenchmark(fixture.storage, dataset);
collectGarbage();
const after = process.memoryUsage();
console.log(
JSON.stringify({
target: target.name,
dataset: dataset.name,
entryCount: dataset.entryCount,
heapUsedBytes: after.heapUsed - before.heapUsed,
rssBytes: after.rss - before.rss,
externalBytes: after.external - before.external,
}),
);

View File

@@ -0,0 +1,51 @@
import { execFile } from "node:child_process";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { STORAGE_BENCHMARK_DATASETS } from "../../src/harness/session/testing/index.ts";
import { storageBenchmarkTargets } from "./storage-targets.ts";
interface LoadedFootprintResult {
readonly target: string;
readonly dataset: string;
readonly entryCount: number;
readonly heapUsedBytes: number;
readonly rssBytes: number;
readonly externalBytes: number;
}
const execFileAsync = promisify(execFile);
const packageRoot = fileURLToPath(new URL("../..", import.meta.url));
const workerPath = fileURLToPath(new URL("./loaded-footprint-worker.ts", import.meta.url));
const tsconfigPath = fileURLToPath(new URL("../tsconfig.json", import.meta.url));
const results: LoadedFootprintResult[] = [];
for (const target of storageBenchmarkTargets) {
for (const dataset of STORAGE_BENCHMARK_DATASETS) {
const { stdout } = await execFileAsync(
process.execPath,
[
"--expose-gc",
"--import",
"tsx",
workerPath,
target.name,
dataset.name,
],
{ cwd: packageRoot, env: { ...process.env, TSX_TSCONFIG_PATH: tsconfigPath } },
);
results.push(JSON.parse(stdout) as LoadedFootprintResult);
}
}
const mebibytes = (bytes: number): string => (bytes / 1024 / 1024).toFixed(2);
console.log("Loaded footprint (synthetic payloads generated and ingested after the baseline):");
console.table(
results.map((result) => ({
backend: result.target,
dataset: result.dataset,
"heap MiB": mebibytes(result.heapUsedBytes),
"heap bytes/entry": Math.round(result.heapUsedBytes / result.entryCount),
"RSS MiB": mebibytes(result.rssBytes),
"external MiB": mebibytes(result.externalBytes),
})),
);

View File

@@ -0,0 +1,22 @@
import { MemorySessionRepo } from "../../src/harness/session/index.ts";
import type { SessionRepo } from "../../src/harness/session/types.ts";
import type { BenchmarkTarget } from "./benchmark.ts";
export interface SessionRepoBenchmarkFixture extends AsyncDisposable {
readonly repo: SessionRepo;
}
const NOW = 1_700_000_000_000;
export const sessionRepoBenchmarkTargets = [
{
name: "memory",
createFixture() {
const repo = new MemorySessionRepo({ now: () => NOW });
return Promise.resolve({
repo,
[Symbol.asyncDispose]: () => repo.close(),
});
},
},
] satisfies readonly BenchmarkTarget<SessionRepoBenchmarkFixture>[];

View File

@@ -0,0 +1,62 @@
import {
SESSION_REPO_CATALOG_BENCHMARK_DATASETS,
SESSION_REPO_CATALOG_READ_BENCHMARK_SCENARIOS,
SESSION_REPO_CATALOG_WRITE_BENCHMARK_SCENARIOS,
SESSION_REPO_FORK_BENCHMARK_DATASETS,
SESSION_REPO_FORK_WRITE_BENCHMARK_SCENARIOS,
seedSessionRepoCatalogBenchmark,
seedSessionRepoForkBenchmark,
} from "../../src/harness/session/testing/index.ts";
import { registerReadBenchmarks, registerWriteBenchmarks } from "./benchmark.ts";
import { sessionRepoBenchmarkTargets } from "./session-repo-targets.ts";
await registerReadBenchmarks({
datasets: SESSION_REPO_CATALOG_BENCHMARK_DATASETS,
targets: sessionRepoBenchmarkTargets,
scenarios: SESSION_REPO_CATALOG_READ_BENCHMARK_SCENARIOS,
async prepare(fixture, dataset) {
await seedSessionRepoCatalogBenchmark(fixture.repo, dataset);
},
getSubject(fixture) {
return fixture.repo;
},
});
await registerWriteBenchmarks({
targets: sessionRepoBenchmarkTargets,
scenarios: SESSION_REPO_CATALOG_WRITE_BENCHMARK_SCENARIOS,
prepare(fixture, scenario) {
return scenario.prepare(fixture.repo);
},
expectedResult(scenario) {
return scenario.expectedResult;
},
run(operation) {
return operation.run();
},
});
const forkBenchmarks = SESSION_REPO_FORK_BENCHMARK_DATASETS.flatMap((dataset) =>
SESSION_REPO_FORK_WRITE_BENCHMARK_SCENARIOS.map((scenario) => ({
name: `${scenario.name} (${dataset.name})`,
dataset,
scenario,
})),
);
await registerWriteBenchmarks({
targets: sessionRepoBenchmarkTargets,
scenarios: forkBenchmarks,
async prepare(fixture, benchmark) {
return {
repo: fixture.repo,
source: await seedSessionRepoForkBenchmark(fixture.repo, benchmark.dataset),
};
},
expectedResult(benchmark) {
return benchmark.scenario.expectedResult(benchmark.dataset);
},
run(subject, benchmark) {
return benchmark.scenario.run(subject.repo, subject.source, benchmark.dataset);
},
});

View File

@@ -0,0 +1,18 @@
import { MemoryStorage } from "../../src/harness/session/memory.ts";
import type { StorageFixture } from "../../src/harness/session/testing/index.ts";
import type { BenchmarkTarget } from "./benchmark.ts";
const NOW = 1_700_000_000_000;
export const storageBenchmarkTargets = [
{
name: "memory",
createFixture() {
const storage = new MemoryStorage({ now: () => NOW });
return Promise.resolve({
storage,
[Symbol.asyncDispose]: () => storage.close(),
});
},
},
] satisfies readonly BenchmarkTarget<StorageFixture>[];

View File

@@ -0,0 +1,35 @@
import {
STORAGE_BENCHMARK_DATASETS,
STORAGE_READ_BENCHMARK_SCENARIOS,
STORAGE_WRITE_BENCHMARK_SCENARIOS,
seedStorageBenchmark,
} from "../../src/harness/session/testing/index.ts";
import { registerReadBenchmarks, registerWriteBenchmarks } from "./benchmark.ts";
import { storageBenchmarkTargets } from "./storage-targets.ts";
await registerReadBenchmarks({
datasets: STORAGE_BENCHMARK_DATASETS,
targets: storageBenchmarkTargets,
scenarios: STORAGE_READ_BENCHMARK_SCENARIOS,
prepare(fixture, dataset) {
return seedStorageBenchmark(fixture.storage, dataset);
},
getSubject(fixture) {
return fixture.storage;
},
});
await registerWriteBenchmarks({
targets: storageBenchmarkTargets,
scenarios: STORAGE_WRITE_BENCHMARK_SCENARIOS,
async prepare(fixture, scenario) {
await scenario.prepare?.(fixture.storage);
return fixture.storage;
},
expectedResult(scenario) {
return scenario.writeCount;
},
run(storage, scenario) {
return scenario.run(storage);
},
});