From c40ae87fe54d74d2a8b1bed1cdfbb4aa823c7c3d Mon Sep 17 00:00:00 2001 From: not-matthias Date: Fri, 23 Oct 2026 18:06:15 +0200 Subject: [PATCH] fix(core): seed the V8 RNG and string hashes in walltime mode Walltime runs left Math.random() and V8's hash seed random per process, so benchmarks using random inputs or hash-heavy structures varied between runs for reasons unrelated to the code under test. Pass the analysis-mode seed flags (--hash-seed=1, --random-seed=1, and --no-randomize-hashes on Node < 18) in walltime too, via one shared helper. Disabled mode stays unseeded. crypto randomness and key iteration order are unchanged. --- docs/introspection.md | 11 ++++++++++- packages/core/src/introspection.ts | 17 ++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/introspection.md b/docs/introspection.md index 6bd9fd2b..c5d2ca21 100644 --- a/docs/introspection.md +++ b/docs/introspection.md @@ -8,7 +8,16 @@ benchmark is measured. TurboFan remains disabled with `--no-opt`, and Maglev is explicitly disabled on Node.js 24 and newer. This changes the measurement baseline compared with earlier releases. It does not -isolate inline-cache feedback from other benchmarks. Walltime flags are unchanged. +isolate inline-cache feedback from other benchmarks. + +## Seed flags + +Simulation, memory, and walltime mode pass `--hash-seed=1` and `--random-seed=1` (plus +`--no-randomize-hashes` on Node.js < 18). `Math.random()` returns the same +sequence on every run, including in worker threads, and hash tables get the same +internal layout, so hash collisions do not change between runs. Key iteration +order is unaffected, since it follows insertion order. `crypto` randomness stays +random. Walltime mode uses no other analysis flags. ## Testing introspection locally diff --git a/packages/core/src/introspection.ts b/packages/core/src/introspection.ts index 18c29ba0..8906c1d9 100644 --- a/packages/core/src/introspection.ts +++ b/packages/core/src/introspection.ts @@ -7,6 +7,15 @@ const CUSTOM_INTROSPECTION_EXIT_CODE = 0; const V8_LOG_FILENAME_PATTERN = "codspeed-v8-%p.log"; +// Fixed seeds so Math.random() and hash table layout match on every run +const getSeedFlags = (nodeVersionMajor: number) => { + const flags = ["--hash-seed=1", "--random-seed=1"]; + if (nodeVersionMajor < 18) { + flags.push("--no-randomize-hashes"); + } + return flags; +}; + export const getV8Flags = () => { const nodeVersionMajor = parseInt(process.version.slice(1).split(".")[0]); const instrumentMode = getInstrumentMode(); @@ -17,8 +26,7 @@ export const getV8Flags = () => { case "analysis": { flags.push( ...[ - "--hash-seed=1", - "--random-seed=1", + ...getSeedFlags(nodeVersionMajor), "--no-opt", "--always-sparkplug", "--predictable", @@ -28,9 +36,6 @@ export const getV8Flags = () => { "--max-old-space-size=4096", ], ); - if (nodeVersionMajor < 18) { - flags.push("--no-randomize-hashes"); - } if (nodeVersionMajor < 20) { flags.push("--no-scavenge-task"); } else { @@ -48,6 +53,8 @@ export const getV8Flags = () => { } case "walltime": { + flags.push(...getSeedFlags(nodeVersionMajor)); + // Emit the V8 jitdump flags.push("--perf-prof");