Skip to content

Commit bc3c602

Browse files
committed
Add benchmark JSON report steps in smoke CI
1 parent 6933023 commit bc3c602

2 files changed

Lines changed: 107 additions & 5 deletions

File tree

.github/workflows/smoke.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ jobs:
126126
set -euo pipefail
127127
cmake --build build --target runtime_smoke --config "${BUILD_TYPE}" 2>&1 | tee build/runtime_smoke.log
128128
129+
- name: Report in-tree runtime benchmark JSON
130+
shell: bash
131+
run: |
132+
set -euo pipefail
133+
node_bin="$(ls -1d "${GITHUB_WORKSPACE}/.3rdparty/emsdk"/node/*/bin/node | sort -V | tail -n1)"
134+
"${node_bin}" tests/wasm/tools/validate_dispatch_bench.mjs \
135+
build/runtime_smoke.log \
136+
--emit-json \
137+
--json-out build/runtime_benchmark_report.json \
138+
--no-validate
139+
129140
- name: Validate in-tree runtime benchmark gate
130141
shell: bash
131142
run: |
@@ -269,6 +280,17 @@ jobs:
269280
set -euo pipefail
270281
cmake --build tests-only/build --target runtime_smoke --config "${BUILD_TYPE}" 2>&1 | tee tests-only/build/runtime_smoke.log
271282
283+
- name: Report package runtime benchmark JSON
284+
shell: bash
285+
run: |
286+
set -euo pipefail
287+
node_bin="$(ls -1d "${GITHUB_WORKSPACE}/.3rdparty/emsdk"/node/*/bin/node | sort -V | tail -n1)"
288+
"${node_bin}" tests-only/tests/wasm/tools/validate_dispatch_bench.mjs \
289+
tests-only/build/runtime_smoke.log \
290+
--emit-json \
291+
--json-out tests-only/build/runtime_benchmark_report.json \
292+
--no-validate
293+
272294
- name: Validate package runtime benchmark gate
273295
shell: bash
274296
run: |

tests/wasm/tools/validate_dispatch_bench.mjs

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { readFile } from "node:fs/promises";
1+
import { readFile, writeFile } from "node:fs/promises";
22

33
function parsePositiveFloat(value, label) {
44
const parsed = Number.parseFloat(value);
@@ -89,9 +89,45 @@ function ensureModeEntries(mapByProfile, profileName, modeName) {
8989
return byMode.get(modeName);
9090
}
9191

92-
const logPath = process.argv[2];
92+
let logPath = "";
93+
let emitJson = false;
94+
let jsonOutPath = "";
95+
let enforceThresholds = true;
96+
97+
const args = process.argv.slice(2);
98+
for (let i = 0; i < args.length; ++i) {
99+
const arg = args[i];
100+
if (arg === "--emit-json") {
101+
emitJson = true;
102+
continue;
103+
}
104+
if (arg === "--no-validate") {
105+
enforceThresholds = false;
106+
continue;
107+
}
108+
if (arg === "--json-out") {
109+
const nextValue = args[i + 1];
110+
if (!nextValue) {
111+
throw new Error("--json-out requires a file path");
112+
}
113+
jsonOutPath = nextValue;
114+
i += 1;
115+
continue;
116+
}
117+
if (arg.startsWith("--")) {
118+
throw new Error(`Unknown option '${arg}'`);
119+
}
120+
if (!logPath) {
121+
logPath = arg;
122+
continue;
123+
}
124+
throw new Error("Only one runtime-smoke-log-path argument is supported");
125+
}
126+
93127
if (!logPath) {
94-
throw new Error("Usage: node validate_dispatch_bench.mjs <runtime-smoke-log-path>");
128+
throw new Error(
129+
"Usage: node validate_dispatch_bench.mjs <runtime-smoke-log-path> [--emit-json] [--json-out <path>] [--no-validate]"
130+
);
95131
}
96132

97133
const logText = await readFile(logPath, "utf8");
@@ -113,6 +149,7 @@ for (const summary of summaries) {
113149
}
114150

115151
const requiredProfiles = normalizeRequiredProfiles();
152+
const reportProfiles = [];
116153
for (const profileName of requiredProfiles) {
117154
const fastSamples = ensureModeEntries(benchmarksByProfile, profileName, "fast_wasm");
118155
const rawSamples = ensureModeEntries(benchmarksByProfile, profileName, "raw_llvm_ir");
@@ -121,16 +158,59 @@ for (const profileName of requiredProfiles) {
121158
const rawAvgMs = average(rawSamples);
122159
const speedup = rawAvgMs / fastAvgMs;
123160
const minSpeedup = envThresholdForProfile(profileName);
161+
const profilePass = speedup >= minSpeedup;
162+
163+
reportProfiles.push({
164+
name: profileName,
165+
fast_wasm_avg_ms: fastAvgMs,
166+
raw_llvm_ir_avg_ms: rawAvgMs,
167+
speedup_x: speedup,
168+
required_min_speedup_x: minSpeedup,
169+
pass: profilePass
170+
});
124171

125172
console.log(
126173
`[bench] profile=${profileName} fast_wasm_avg_ms=${fastAvgMs.toFixed(6)} raw_llvm_ir_avg_ms=${rawAvgMs.toFixed(6)} speedup=${speedup.toFixed(3)}x required>=${minSpeedup.toFixed(3)}x`
127174
);
128175

129-
if (speedup < minSpeedup) {
176+
if (enforceThresholds && speedup < minSpeedup) {
130177
throw new Error(
131178
`Benchmark gate failed for profile='${profileName}': observed speedup ${speedup.toFixed(3)}x < required ${minSpeedup.toFixed(3)}x`
132179
);
133180
}
134181
}
135182

136-
console.log("[bench] runtime benchmark gate passed");
183+
const speedupValues = reportProfiles.map((profile) => profile.speedup_x);
184+
const minSpeedupObserved = Math.min(...speedupValues);
185+
const maxSpeedupObserved = Math.max(...speedupValues);
186+
const avgSpeedupObserved = average(speedupValues);
187+
const geometricMeanSpeedupObserved = Math.exp(
188+
speedupValues.reduce((sum, value) => sum + Math.log(value), 0.0) / speedupValues.length
189+
);
190+
191+
const report = {
192+
required_profiles: requiredProfiles,
193+
profiles: reportProfiles,
194+
summary: {
195+
min_speedup_x: minSpeedupObserved,
196+
max_speedup_x: maxSpeedupObserved,
197+
avg_speedup_x: avgSpeedupObserved,
198+
geomean_speedup_x: geometricMeanSpeedupObserved,
199+
all_profiles_pass: reportProfiles.every((profile) => profile.pass)
200+
}
201+
};
202+
203+
if (jsonOutPath) {
204+
await writeFile(jsonOutPath, JSON.stringify(report, null, 2) + "\n", "utf8");
205+
}
206+
207+
if (emitJson) {
208+
console.log("[bench] benchmark report json");
209+
console.log(JSON.stringify(report, null, 2));
210+
}
211+
212+
if (enforceThresholds) {
213+
console.log("[bench] runtime benchmark gate passed");
214+
} else {
215+
console.log("[bench] runtime benchmark report generated without threshold validation");
216+
}

0 commit comments

Comments
 (0)