@remnic/bench 9.3.727 → 9.3.728
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -9
- package/baselines/coding-graph-baseline.json +13 -11
- package/dist/index.d.ts +29 -3
- package/dist/index.js +137 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -211,7 +211,8 @@ A dedicated benchmark suite for [`@remnic/coding-graph`](https://www.npmjs.com/p
|
|
|
211
211
|
|---|---|
|
|
212
212
|
| `fullIndexMs` | Wall time to index the entire fixture in one batch. |
|
|
213
213
|
| `fullIndexLocsPerSecond` | Sustained LOC/s during full index (higher is better). |
|
|
214
|
-
| `incrementalUpdateP50Ms` / `incrementalUpdateP95Ms` | Single-file re-ingest latency (p50/p95 over ≥20 iterations). |
|
|
214
|
+
| `incrementalUpdateP50Ms` / `incrementalUpdateP95Ms` | Single-file re-ingest latency for UNCHANGED content (the common-case no-op path; p50/p95 over ≥20 iterations). |
|
|
215
|
+
| `incrementalModifiedUpdateP50Ms` / `incrementalModifiedUpdateP95Ms` | Single-file re-ingest latency for MODIFIED content — the change-heavy path (edge deletion/creation, symbol re-resolution). Complementary to `incrementalUpdate` (#1688). |
|
|
215
216
|
| `tracePathP95Ms` | `trace_path` (BFS, depth ≤ 5) p95. |
|
|
216
217
|
| `searchGraphP95Ms` | `search_graph` name-pattern p95. |
|
|
217
218
|
| `deadCodeMs` | Dead-code query wall time. |
|
|
@@ -225,18 +226,21 @@ The harness ships a **deterministic** synthetic repo generator (`generateSynthet
|
|
|
225
226
|
|
|
226
227
|
The measured numbers live in [`baselines/coding-graph-baseline.json`](./baselines/coding-graph-baseline.json) — bench-owned, separate from the structural ratchets in `scripts/ratchet-baseline.json`. The regression gate (`checkCodingGraphRegression`) compares a report against the baseline with a generous tolerance (default 30%). It hard-fails on gross regression — a real failing step, not a warning (rule 50). Tightening the baseline is a deliberate PR act (mirrors `check-ratchets --update`).
|
|
227
228
|
|
|
229
|
+
The gate carries a **machine-fingerprint guard** (`compareMachineFingerprints`): when the report's machine class differs from the baseline's (arch/platform/Node major/cpuModel/cores), the comparison is *skipped* (`passed: true`, `skipped: true`, `machineMismatch` populated) rather than failed, so a cross-machine CI run does not false-positive on legitimate hardware variance (#1688). Regenerate the baseline on the target machine class for a real comparison.
|
|
230
|
+
|
|
228
231
|
### Measured numbers (first baseline)
|
|
229
232
|
|
|
230
233
|
> Numbers below are from the **baseline JSON file** — this section is checked against it so prose can't drift from measurement. Run `remnic bench coding-graph` to reproduce.
|
|
231
234
|
|
|
232
235
|
| Metric | Value | Machine |
|
|
233
236
|
|---|---|---|
|
|
234
|
-
| Full index | ~
|
|
235
|
-
| Incremental update p95 | ~0.
|
|
236
|
-
|
|
|
237
|
-
|
|
|
238
|
-
|
|
|
239
|
-
|
|
|
237
|
+
| Full index | ~15 ms, ~131k LOC/s | Apple M2 Max, Node v22 |
|
|
238
|
+
| Incremental update p95 (idempotent) | ~0.24 ms | Apple M2 Max |
|
|
239
|
+
| Incremental modified update p95 (change-heavy) | ~0.96 ms | Apple M2 Max |
|
|
240
|
+
| trace_path p95 | ~0.13 ms | Apple M2 Max |
|
|
241
|
+
| search_graph p95 | ~0.18 ms | Apple M2 Max |
|
|
242
|
+
| dead_code | ~0.53 ms | Apple M2 Max |
|
|
243
|
+
| DB size | ~270 KB/KLOC | Apple M2 Max |
|
|
240
244
|
|
|
241
245
|
These are **working targets on a small fixture (20 files, 200 symbols)**, NOT parity claims against codebase-memory-mcp's published numbers (28M LOC in 3 min, <1ms Cypher). Scale targets at 1M+ LOC are tracked as stretch goals — the harness will measure them when Tier-L fixtures are wired (issue #1557 PR2).
|
|
242
246
|
|
|
@@ -245,9 +249,14 @@ These are **working targets on a small fixture (20 files, 200 symbols)**, NOT pa
|
|
|
245
249
|
```typescript
|
|
246
250
|
import { runCodingGraphBenchmark, checkCodingGraphRegression } from "@remnic/bench";
|
|
247
251
|
|
|
252
|
+
// Use the SAME fixture as the bundled baseline (20×10, density 0.3, seed
|
|
253
|
+
// 42) so the regression gate compares like-for-like. A custom fixture
|
|
254
|
+
// (different fileCount/symbolsPerFile/callDensity/seed/language) needs its
|
|
255
|
+
// OWN baseline — build one with buildBaselineFromReport(report, note) and
|
|
256
|
+
// commit it, otherwise checkCodingGraphRegression reports a fixture mismatch.
|
|
248
257
|
const report = await runCodingGraphBenchmark({
|
|
249
|
-
fixture: { fileCount:
|
|
250
|
-
iterations:
|
|
258
|
+
fixture: { seed: 42, fileCount: 20, symbolsPerFile: 10, callDensity: 0.3, language: "typescript" },
|
|
259
|
+
iterations: 20,
|
|
251
260
|
});
|
|
252
261
|
|
|
253
262
|
const baseline = require("@remnic/bench/baselines/coding-graph-baseline.json");
|
|
@@ -256,4 +265,7 @@ if (!gate.passed) {
|
|
|
256
265
|
console.error(gate.summary);
|
|
257
266
|
process.exit(1);
|
|
258
267
|
}
|
|
268
|
+
// gate.skipped === true when the report's machine fingerprint differs from
|
|
269
|
+
// the baseline's (different CPU/arch/Node major) — the comparison is skipped
|
|
270
|
+
// rather than failed so cross-machine CI does not false-positive (#1688).
|
|
259
271
|
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion":
|
|
2
|
+
"schemaVersion": 2,
|
|
3
3
|
"machine": {
|
|
4
4
|
"arch": "arm64",
|
|
5
5
|
"platform": "darwin",
|
|
@@ -16,15 +16,17 @@
|
|
|
16
16
|
"language": "typescript"
|
|
17
17
|
},
|
|
18
18
|
"metrics": {
|
|
19
|
-
"fullIndexMs":
|
|
20
|
-
"fullIndexLocsPerSecond":
|
|
21
|
-
"incrementalUpdateP50Ms": 0.
|
|
22
|
-
"incrementalUpdateP95Ms": 0.
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
19
|
+
"fullIndexMs": 15.452540999999655,
|
|
20
|
+
"fullIndexLocsPerSecond": 131111,
|
|
21
|
+
"incrementalUpdateP50Ms": 0.173167000000376,
|
|
22
|
+
"incrementalUpdateP95Ms": 0.23908399999982066,
|
|
23
|
+
"incrementalModifiedUpdateP50Ms": 0.6047909999997501,
|
|
24
|
+
"incrementalModifiedUpdateP95Ms": 0.9565000000002328,
|
|
25
|
+
"tracePathP95Ms": 0.13425000000006548,
|
|
26
|
+
"searchGraphP95Ms": 0.1764170000001286,
|
|
27
|
+
"deadCodeMs": 0.5299169999998412,
|
|
28
|
+
"dbBytesPerKloc": 270468
|
|
27
29
|
},
|
|
28
|
-
"createdAt": "2026-07-
|
|
29
|
-
"note": "
|
|
30
|
+
"createdAt": "2026-07-07T12:35:48.738Z",
|
|
31
|
+
"note": "Regenerated baseline — adds incrementalModifiedUpdate metric (#1688 item 1) + machine-fingerprint guard (#1688 item 2). WAL-aware DB sizing retained (#1557 PR1)."
|
|
30
32
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3979,7 +3979,7 @@ interface WallMetric {
|
|
|
3979
3979
|
* The regression gate compares only keys present in BOTH the report and
|
|
3980
3980
|
* the baseline — new metrics are additive, never blocking.
|
|
3981
3981
|
*/
|
|
3982
|
-
type CodingGraphMetricKey = "fullIndexMs" | "fullIndexLocsPerSecond" | "incrementalUpdateP50Ms" | "incrementalUpdateP95Ms" | "tracePathP95Ms" | "searchGraphP95Ms" | "deadCodeMs" | "dbBytesPerKloc";
|
|
3982
|
+
type CodingGraphMetricKey = "fullIndexMs" | "fullIndexLocsPerSecond" | "incrementalUpdateP50Ms" | "incrementalUpdateP95Ms" | "incrementalModifiedUpdateP50Ms" | "incrementalModifiedUpdateP95Ms" | "tracePathP95Ms" | "searchGraphP95Ms" | "deadCodeMs" | "dbBytesPerKloc";
|
|
3983
3983
|
interface CodingGraphBenchReport {
|
|
3984
3984
|
/** Schema version — bump when the report shape changes. */
|
|
3985
3985
|
readonly schemaVersion: number;
|
|
@@ -3996,8 +3996,14 @@ interface CodingGraphBenchReport {
|
|
|
3996
3996
|
readonly fullIndexMs: WallMetric;
|
|
3997
3997
|
/** LOC/s sustained during full index (higher is better). */
|
|
3998
3998
|
readonly fullIndexLocsPerSecond: number;
|
|
3999
|
-
/** Incremental single-file update latency distribution
|
|
3999
|
+
/** Incremental single-file update latency distribution (idempotent
|
|
4000
|
+
* re-ingest of UNCHANGED content — the common-case no-op path). */
|
|
4000
4001
|
readonly incrementalUpdate: MicroMetric;
|
|
4002
|
+
/** Incremental MODIFIED-content re-ingest latency distribution. A
|
|
4003
|
+
* complementary metric to `incrementalUpdate`: measures the change-heavy
|
|
4004
|
+
* path (edge deletion/creation, symbol re-resolution) rather than the
|
|
4005
|
+
* cache-hit no-op (#1688 item 1). */
|
|
4006
|
+
readonly incrementalModifiedUpdate: MicroMetric;
|
|
4001
4007
|
/** trace_path (depth ≤ 5) latency distribution. */
|
|
4002
4008
|
readonly tracePath: MicroMetric;
|
|
4003
4009
|
/** search_graph name-pattern latency distribution. */
|
|
@@ -4046,6 +4052,24 @@ interface RegressionGateResult {
|
|
|
4046
4052
|
readonly passed: boolean;
|
|
4047
4053
|
readonly regressions: readonly RegressionMetricDetail[];
|
|
4048
4054
|
readonly summary: string;
|
|
4055
|
+
/**
|
|
4056
|
+
* True when the gate SKIPPED the metric comparison because of a
|
|
4057
|
+
* non-fatal precondition (machine-fingerprint mismatch). `passed` is
|
|
4058
|
+
* also true in that case — a hardware variance must not fail CI (#1688
|
|
4059
|
+
* item 2). Callers that want to treat a skip as informational can check
|
|
4060
|
+
* this flag.
|
|
4061
|
+
*/
|
|
4062
|
+
readonly skipped?: boolean;
|
|
4063
|
+
/**
|
|
4064
|
+
* Present when `skipped` is true due to a machine-fingerprint mismatch
|
|
4065
|
+
* between the report and the baseline. Lists the fingerprint fields that
|
|
4066
|
+
* differ so a human can decide whether the comparison is still meaningful.
|
|
4067
|
+
*/
|
|
4068
|
+
readonly machineMismatch?: {
|
|
4069
|
+
readonly report: MachineFingerprint;
|
|
4070
|
+
readonly baseline: MachineFingerprint;
|
|
4071
|
+
readonly differingFields: readonly string[];
|
|
4072
|
+
};
|
|
4049
4073
|
}
|
|
4050
4074
|
interface CodingGraphBenchConfig {
|
|
4051
4075
|
/** Fixture parameters. Defaults to a small, fast smoke fixture. */
|
|
@@ -4073,7 +4097,7 @@ declare const MIN_ITERATIONS = 20;
|
|
|
4073
4097
|
/** Default regression tolerance (issue #1557: generous, e.g. 30%). */
|
|
4074
4098
|
declare const DEFAULT_TOLERANCE_PERCENT = 30;
|
|
4075
4099
|
/** Report schema version — bump when the shape changes. */
|
|
4076
|
-
declare const CODING_GRAPH_BENCH_SCHEMA_VERSION =
|
|
4100
|
+
declare const CODING_GRAPH_BENCH_SCHEMA_VERSION = 2;
|
|
4077
4101
|
|
|
4078
4102
|
/**
|
|
4079
4103
|
* Deterministic synthetic repo generator (issue #1557 design (a)).
|
|
@@ -4169,6 +4193,8 @@ declare const METRIC_DIRECTION: {
|
|
|
4169
4193
|
fullIndexLocsPerSecond: "higher-is-better";
|
|
4170
4194
|
incrementalUpdateP95Ms: "lower-is-better";
|
|
4171
4195
|
incrementalUpdateP50Ms: "lower-is-better";
|
|
4196
|
+
incrementalModifiedUpdateP95Ms: "lower-is-better";
|
|
4197
|
+
incrementalModifiedUpdateP50Ms: "lower-is-better";
|
|
4172
4198
|
tracePathP95Ms: "lower-is-better";
|
|
4173
4199
|
searchGraphP95Ms: "lower-is-better";
|
|
4174
4200
|
deadCodeMs: "lower-is-better";
|
package/dist/index.js
CHANGED
|
@@ -36233,7 +36233,7 @@ var DEFAULT_10K_FIXTURE = {
|
|
|
36233
36233
|
};
|
|
36234
36234
|
var MIN_ITERATIONS = 20;
|
|
36235
36235
|
var DEFAULT_TOLERANCE_PERCENT = 30;
|
|
36236
|
-
var CODING_GRAPH_BENCH_SCHEMA_VERSION =
|
|
36236
|
+
var CODING_GRAPH_BENCH_SCHEMA_VERSION = 2;
|
|
36237
36237
|
|
|
36238
36238
|
// src/coding-graph/harness.ts
|
|
36239
36239
|
function captureMachineFingerprint() {
|
|
@@ -36378,6 +36378,40 @@ async function runCodingGraphBenchmark(config = {}) {
|
|
|
36378
36378
|
}
|
|
36379
36379
|
const kloc = Math.max(1, repo.approximateLoc / 1e3);
|
|
36380
36380
|
const dbBytesPerKloc = dbBytes / kloc;
|
|
36381
|
+
const modifiedSamples = [];
|
|
36382
|
+
for (let i = 0; i < iterations; i++) {
|
|
36383
|
+
const fileIdx = i % storeFiles.length;
|
|
36384
|
+
const original = storeFiles[fileIdx];
|
|
36385
|
+
if (!original) continue;
|
|
36386
|
+
const ownSyms = new Set(original.symbols.map((sym) => sym.qualifiedName));
|
|
36387
|
+
const churnName = `mod.benchChurnSymbol${i}`;
|
|
36388
|
+
const representativeEdges = (original.edges ?? []).filter((e) => !ownSyms.has(e.dstQualifiedName)).slice(0, 2).map((e) => ({ ...e, srcQualifiedName: churnName }));
|
|
36389
|
+
const modified = {
|
|
36390
|
+
...original,
|
|
36391
|
+
contentHash: `${original.contentHash}-mod-${i}`,
|
|
36392
|
+
symbols: [
|
|
36393
|
+
{
|
|
36394
|
+
qualifiedName: churnName,
|
|
36395
|
+
name: `benchChurnSymbol${i}`,
|
|
36396
|
+
kind: "function",
|
|
36397
|
+
span: { startByte: 0, endByte: 0 }
|
|
36398
|
+
}
|
|
36399
|
+
],
|
|
36400
|
+
edges: representativeEdges
|
|
36401
|
+
};
|
|
36402
|
+
const modResult = await timeAsync(
|
|
36403
|
+
() => store.upsertFileBatch([modified])
|
|
36404
|
+
);
|
|
36405
|
+
if (!modResult.result.ok) {
|
|
36406
|
+
throw new Error(`modified update failed: ${modResult.result.code}`);
|
|
36407
|
+
}
|
|
36408
|
+
modifiedSamples.push(modResult.ms);
|
|
36409
|
+
const restoreResult = await store.upsertFileBatch(storeFiles);
|
|
36410
|
+
if (!restoreResult.ok) {
|
|
36411
|
+
throw new Error(`restore failed: ${restoreResult.code}`);
|
|
36412
|
+
}
|
|
36413
|
+
sampleRss();
|
|
36414
|
+
}
|
|
36381
36415
|
sampleRss();
|
|
36382
36416
|
const peakRssBytes = peakRss;
|
|
36383
36417
|
return {
|
|
@@ -36394,6 +36428,9 @@ async function runCodingGraphBenchmark(config = {}) {
|
|
|
36394
36428
|
fullIndexMs: { ms: fullIndex.ms },
|
|
36395
36429
|
fullIndexLocsPerSecond: Math.round(locsPerSecond),
|
|
36396
36430
|
incrementalUpdate: computeMicroMetric(incrementalSamples),
|
|
36431
|
+
incrementalModifiedUpdate: computeMicroMetric(
|
|
36432
|
+
modifiedSamples.length > 0 ? modifiedSamples : [0]
|
|
36433
|
+
),
|
|
36397
36434
|
tracePath: computeMicroMetric(traceSamples.length > 0 ? traceSamples : [0]),
|
|
36398
36435
|
searchGraph: computeMicroMetric(searchSamples),
|
|
36399
36436
|
deadCodeMs: { ms: deadCode.ms },
|
|
@@ -36417,6 +36454,8 @@ var METRIC_DIRECTION = {
|
|
|
36417
36454
|
fullIndexLocsPerSecond: "higher-is-better",
|
|
36418
36455
|
incrementalUpdateP95Ms: "lower-is-better",
|
|
36419
36456
|
incrementalUpdateP50Ms: "lower-is-better",
|
|
36457
|
+
incrementalModifiedUpdateP95Ms: "lower-is-better",
|
|
36458
|
+
incrementalModifiedUpdateP50Ms: "lower-is-better",
|
|
36420
36459
|
tracePathP95Ms: "lower-is-better",
|
|
36421
36460
|
searchGraphP95Ms: "lower-is-better",
|
|
36422
36461
|
deadCodeMs: "lower-is-better",
|
|
@@ -36428,16 +36467,43 @@ function extractMetrics(report) {
|
|
|
36428
36467
|
fullIndexLocsPerSecond: report.fullIndexLocsPerSecond,
|
|
36429
36468
|
incrementalUpdateP50Ms: report.incrementalUpdate.p50,
|
|
36430
36469
|
incrementalUpdateP95Ms: report.incrementalUpdate.p95,
|
|
36470
|
+
incrementalModifiedUpdateP50Ms: report.incrementalModifiedUpdate.p50,
|
|
36471
|
+
incrementalModifiedUpdateP95Ms: report.incrementalModifiedUpdate.p95,
|
|
36431
36472
|
tracePathP95Ms: report.tracePath.p95,
|
|
36432
36473
|
searchGraphP95Ms: report.searchGraph.p95,
|
|
36433
36474
|
deadCodeMs: report.deadCodeMs.ms,
|
|
36434
36475
|
dbBytesPerKloc: report.dbBytesPerKloc
|
|
36435
36476
|
};
|
|
36436
36477
|
}
|
|
36478
|
+
var NODE_MAJOR_CACHE = /* @__PURE__ */ new WeakMap();
|
|
36479
|
+
function nodeMajor(fp) {
|
|
36480
|
+
const cached = NODE_MAJOR_CACHE.get(fp);
|
|
36481
|
+
if (cached !== void 0) return cached;
|
|
36482
|
+
const major = fp.nodeVersion.replace(/^v/, "").split(".")[0] ?? fp.nodeVersion;
|
|
36483
|
+
NODE_MAJOR_CACHE.set(fp, major);
|
|
36484
|
+
return major;
|
|
36485
|
+
}
|
|
36486
|
+
function compareMachineFingerprints(report, baseline) {
|
|
36487
|
+
const differing = [];
|
|
36488
|
+
if (report.arch !== baseline.arch) differing.push("arch");
|
|
36489
|
+
if (report.platform !== baseline.platform) differing.push("platform");
|
|
36490
|
+
if (nodeMajor(report) !== nodeMajor(baseline)) differing.push("nodeVersion(major)");
|
|
36491
|
+
if (report.cpuModel !== null && baseline.cpuModel !== null && report.cpuModel !== baseline.cpuModel) {
|
|
36492
|
+
differing.push("cpuModel");
|
|
36493
|
+
}
|
|
36494
|
+
if (report.cpuCores !== baseline.cpuCores) differing.push("cpuCores");
|
|
36495
|
+
return { differingFields: differing };
|
|
36496
|
+
}
|
|
36497
|
+
function invalidFingerprintFields(fp) {
|
|
36498
|
+
const bad = [];
|
|
36499
|
+
if (typeof fp.arch !== "string") bad.push("arch");
|
|
36500
|
+
if (typeof fp.platform !== "string") bad.push("platform");
|
|
36501
|
+
if (typeof fp.nodeVersion !== "string") bad.push("nodeVersion");
|
|
36502
|
+
if (typeof fp.cpuCores !== "number" || !Number.isFinite(fp.cpuCores)) bad.push("cpuCores");
|
|
36503
|
+
if (fp.cpuModel !== null && typeof fp.cpuModel !== "string") bad.push("cpuModel");
|
|
36504
|
+
return bad;
|
|
36505
|
+
}
|
|
36437
36506
|
function checkCodingGraphRegression(report, baseline, tolerancePercent = DEFAULT_TOLERANCE_PERCENT) {
|
|
36438
|
-
const measured = extractMetrics(report);
|
|
36439
|
-
const baselineMetrics = baseline.metrics;
|
|
36440
|
-
const regressions = [];
|
|
36441
36507
|
const reportFixture = report.fixture.config;
|
|
36442
36508
|
const baselineFixture = baseline.fixtureConfig;
|
|
36443
36509
|
const mismatchedKeys = Object.keys(baselineFixture).filter((key) => reportFixture[key] !== baselineFixture[key]);
|
|
@@ -36451,6 +36517,73 @@ function checkCodingGraphRegression(report, baseline, tolerancePercent = DEFAULT
|
|
|
36451
36517
|
summary: `Fixture mismatch (${diffs}). Metrics are not comparable across different fixtures.`
|
|
36452
36518
|
};
|
|
36453
36519
|
}
|
|
36520
|
+
if (report.schemaVersion !== baseline.schemaVersion) {
|
|
36521
|
+
return {
|
|
36522
|
+
passed: false,
|
|
36523
|
+
regressions: [],
|
|
36524
|
+
summary: `Schema-version mismatch: report=${report.schemaVersion} baseline=${baseline.schemaVersion}. Metrics are not comparable across schema versions \u2014 regenerate the baseline (#1688).`
|
|
36525
|
+
};
|
|
36526
|
+
}
|
|
36527
|
+
const metricChecks = [
|
|
36528
|
+
["incrementalUpdate.p50", report.incrementalUpdate?.p50],
|
|
36529
|
+
["incrementalUpdate.p95", report.incrementalUpdate?.p95],
|
|
36530
|
+
["incrementalModifiedUpdate.p50", report.incrementalModifiedUpdate?.p50],
|
|
36531
|
+
["incrementalModifiedUpdate.p95", report.incrementalModifiedUpdate?.p95],
|
|
36532
|
+
["tracePath.p95", report.tracePath?.p95],
|
|
36533
|
+
["searchGraph.p95", report.searchGraph?.p95],
|
|
36534
|
+
["fullIndexMs.ms", report.fullIndexMs?.ms],
|
|
36535
|
+
["fullIndexLocsPerSecond", report.fullIndexLocsPerSecond],
|
|
36536
|
+
["deadCodeMs.ms", report.deadCodeMs?.ms],
|
|
36537
|
+
["dbBytesPerKloc", report.dbBytesPerKloc]
|
|
36538
|
+
];
|
|
36539
|
+
const missingFields = metricChecks.filter(([, v]) => typeof v !== "number" || !Number.isFinite(v)).map(([k]) => k);
|
|
36540
|
+
if (missingFields.length > 0) {
|
|
36541
|
+
return {
|
|
36542
|
+
passed: false,
|
|
36543
|
+
regressions: [],
|
|
36544
|
+
summary: "Report claims schemaVersion " + report.schemaVersion + " but is missing required metric field(s): " + missingFields.join(", ") + " \u2014 the report is incomplete or corrupt. Regenerate it (#1688)."
|
|
36545
|
+
};
|
|
36546
|
+
}
|
|
36547
|
+
const badBaselineMetrics = Object.keys(METRIC_DIRECTION).filter((key) => {
|
|
36548
|
+
const v = baseline.metrics[key];
|
|
36549
|
+
return v != null && (typeof v !== "number" || !Number.isFinite(v));
|
|
36550
|
+
});
|
|
36551
|
+
if (badBaselineMetrics.length > 0) {
|
|
36552
|
+
return {
|
|
36553
|
+
passed: false,
|
|
36554
|
+
regressions: [],
|
|
36555
|
+
summary: "Baseline has a non-numeric required metric field(s): " + badBaselineMetrics.join(", ") + " \u2014 the baseline is corrupt. Regenerate it (#1688)."
|
|
36556
|
+
};
|
|
36557
|
+
}
|
|
36558
|
+
const reportFpBad = report.machine == null ? ["<missing>"] : invalidFingerprintFields(report.machine);
|
|
36559
|
+
const baselineFpBad = baseline.machine == null ? ["<missing>"] : invalidFingerprintFields(baseline.machine);
|
|
36560
|
+
if (reportFpBad.length > 0 || baselineFpBad.length > 0) {
|
|
36561
|
+
const which = [];
|
|
36562
|
+
if (reportFpBad.length > 0) which.push("report (" + reportFpBad.join(", ") + ")");
|
|
36563
|
+
if (baselineFpBad.length > 0) which.push("baseline (" + baselineFpBad.join(", ") + ")");
|
|
36564
|
+
return {
|
|
36565
|
+
passed: false,
|
|
36566
|
+
regressions: [],
|
|
36567
|
+
summary: "Report or baseline machine fingerprint is missing or has an invalid field type(s): " + which.join("; ") + " \u2014 the artifact is incomplete or corrupt. Regenerate it (#1688)."
|
|
36568
|
+
};
|
|
36569
|
+
}
|
|
36570
|
+
const mismatch = compareMachineFingerprints(report.machine, baseline.machine);
|
|
36571
|
+
if (mismatch.differingFields.length > 0) {
|
|
36572
|
+
return {
|
|
36573
|
+
passed: true,
|
|
36574
|
+
skipped: true,
|
|
36575
|
+
regressions: [],
|
|
36576
|
+
summary: "Machine-fingerprint mismatch \u2014 comparison skipped to avoid a false-positive hardware-variance failure. Differing fields: " + mismatch.differingFields.join(", ") + ". Regenerate the baseline on this machine for a real comparison (#1688).",
|
|
36577
|
+
machineMismatch: {
|
|
36578
|
+
report: report.machine,
|
|
36579
|
+
baseline: baseline.machine,
|
|
36580
|
+
differingFields: mismatch.differingFields
|
|
36581
|
+
}
|
|
36582
|
+
};
|
|
36583
|
+
}
|
|
36584
|
+
const measured = extractMetrics(report);
|
|
36585
|
+
const baselineMetrics = baseline.metrics;
|
|
36586
|
+
const regressions = [];
|
|
36454
36587
|
for (const key of Object.keys(METRIC_DIRECTION)) {
|
|
36455
36588
|
const baseVal = baselineMetrics[key];
|
|
36456
36589
|
const measVal = measured[key];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/bench",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.728",
|
|
4
4
|
"description": "Retrieval latency ladder benchmarks + CI regression gates for @remnic/core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"hyparquet": "^1.25.7",
|
|
38
38
|
"yaml": "^2.4.2",
|
|
39
|
-
"@remnic/coding-graph": "^9.3.
|
|
40
|
-
"@remnic/core": "^9.3.
|
|
39
|
+
"@remnic/coding-graph": "^9.3.728",
|
|
40
|
+
"@remnic/core": "^9.3.728"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"tsup": "^8.5.1",
|