Truer comparisons and new --wasm_branch flag for benchmark.py - #286
Truer comparisons and new --wasm_branch flag for benchmark.py#286tameware wants to merge 10 commits into
--wasm_branch flag for benchmark.py#286Conversation
TOTAL rel was process wall around dtest, so it disagreed with the avg-user per-file ratios that clients and the summary body use. Co-authored-by: Cursor <cursoragent@cursor.com>
Summing raw user_ms mixed file sizes; dividing by deals from listN.txt keeps TOTAL in the same units as the per-file avg_user columns. Co-authored-by: Cursor <cursoragent@cursor.com>
Lets the harness checkout a ref, build //wasm:dtest_wasm, and compare it alongside native --branch/--binary slots. Co-authored-by: Cursor <cursoragent@cursor.com>
User timing is enough for the summary; process CPU clock is unavailable on wasm32 so sys n/a is expected. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the Python benchmark harness for DDS’ dtest to (1) support benchmarking the WASM build via Node and (2) improve cross-binary comparisons by computing TOTAL as a deal-weighted user-time average rather than summing wall-clock time.
Changes:
- Add
--wasm_branch/--wasm-branchto build//wasm:dtest_wasmfrom a git ref, run it under Node, and include it in mixed native/WASM comparisons. - Adjust summary “TOTAL” computation to use
sum(user_ms) / sum(deals)(deal-weighted) for more meaningful aggregate comparisons across hand files. - Expand unit tests to cover WASM timing parsing (
sys_ms=n/a), new CLI flag parsing, git ref handling, and Node invocation behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| python/tests/benchmark.py | Adds WASM branch support, Node execution path, and updates summary aggregation logic. |
| python/tests/benchmark_test.py | Adds/updates tests for WASM parsing, new flags, git handling, and WASM runner behavior. |
Showing 0.00 looked like a real measurement and disagreed with per-file NA rows. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
python/tests/benchmark.py:270
format_summary()no longer usesResultRow.wall_s(TOTAL was switched from wall totals to per-deal user-time averages), butrun_dtest()still measures wall time andResultRowstill storeswall_s. This looks like dead data now and increases complexity (including in tests that have to pass a placeholder value). Consider removingwall_sfromResultRowand dropping the wall-time measurement/return value, or reintroducing a wall-time summary column if it’s still needed.
# Per-solver totals for overall avg user ms = sum(user_ms) / sum(deals).
total_user: dict[str, list[float]] = {s: [0.0] * nb for s in SOLVERS}
total_deals: dict[str, list[int]] = {s: [0] * nb for s in SOLVERS}
user_seen: dict[str, list[bool]] = {s: [False] * nb for s in SOLVERS}
python/tests/benchmark.py:214
dtest_timing_usable()currently returns true wheneveruser_msis present, butrun_dtest()uses it to decide whether to warn about incomplete timing output. As written, output that hasUser time (ms)but lacks enough information to computeavg_user(ms/deal) will not warn and will later render asNAin the per-file summary. Consider requiringavg_useras well (sys time can still be optional for wasm).
This issue also appears on line 267 of the same file.
def dtest_timing_usable(parsed: DtestTiming) -> bool:
"""True when output has the user timing the summary needs.
Sys time may be n/a on platforms without a process CPU clock (e.g. wasm32).
"""
return parsed.user_ms is not None
Wall time was no longer used after TOTAL switched to user ms/deal, and usable timing should match what the summary body needs. Co-authored-by: Cursor <cursoragent@cursor.com>
tameware
left a comment
There was a problem hiding this comment.
Requesting Copilot re-review of the latest changes.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
python/tests/benchmark.py:916
- When running a .js (wasm) binary,
run_dtest()setscwd=binary.parent, but-fcurrently passeshandsas-is. IfHANDS_DIRis a relative path (allowed via env), that relativehandspath will be resolved against the wasm temp dir / bazel-bin/wasm rather than the repo root, causing dtest_wasm to fail to find the hand file. Passing an absolute/normalized hand-file path avoids this discrepancy between native and wasm runs.
def dtest_command(self, binary: Path, solver: str, hands: Path) -> list[str]:
args = ["-f", str(hands), "-s", solver, *self.cfg.dtest_extra]
if binary.suffix == ".js":
return ["node", str(binary), *args]
return [str(binary), *args]
Node runs use the js parent as cwd; relative HANDS_DIR paths must not resolve against that directory. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
python/tests/benchmark.py:283
- The TOTAL row weights overall avg user ms by
deals_from_hand_file(row.file), which assumes the number of hands processed always matches thelistN.txtfilename. Butparse_dtest_output()already parses "Number of hands" and can deriveavg_userfrom that; if a hand file’s contents don’t match its name (or dtest runs fewer hands than the filename implies), the TOTAL averages become incorrect. Consider carrying the parsed hand count throughDtestTiming/ResultRow(or otherwise using the dtest-reported count) and using that for weighting.
deals = deals_from_hand_file(row.file)
if (
row.user_ms is not None
and deals is not None
and deals > 0
python/tests/benchmark.py:917
- When benchmarking a wasm dtest passed via
--binaryas a relative.jspath (e.g.bazel-bin/wasm/dtest.js),run_dtest()setscwd=binary.parentbut still passes the full relative script path tonode. That makes Node resolve the script relative tocwd(effectivelycwd/bazel-bin/wasm/dtest.js), which will fail to find the file. Using an absolute script path avoids this and still keepscwdpointing at the.wasmsibling.
# Absolute -f so wasm runs (cwd = js parent) still find HANDS_DIR paths.
args = ["-f", str(hands.resolve()), "-s", solver, *self.cfg.dtest_extra]
if binary.suffix == ".js":
return ["node", str(binary), *args]
return [str(binary), *args]
…ths. Filename deal counts can disagree with what dtest actually ran, and a relative --binary .js failed under cwd=js.parent. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
python/tests/benchmark.py:299
- TOTAL aggregation currently includes rows as long as user_ms is present (even when avg_user is missing). This can produce a numeric TOTAL cell for a binary where the per-file row prints NA (avg_user=None) and where run_dtest already warned that timing output is incomplete. To keep the summary consistent and avoid totals being computed from partial timing output, only include rows that have usable user timing (at least avg_user) when accumulating total_user/total_deals.
if (
row.user_ms is not None
and deals is not None
and deals > 0
and row.solver in total_user
):
total_user[row.solver][row.bin_idx] += row.user_ms
total_deals[row.solver][row.bin_idx] += deals
user_seen[row.solver][row.bin_idx] = True
Otherwise incomplete dtest output could produce a numeric TOTAL while the per-file cell is NA. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
python/tests/benchmark.py:290
- In TOTAL aggregation, a parsed
row.handsvalue of 0 is treated as “missing” and replaced with the deal count inferred from the filename (e.g. list1.txt => 1). If dtest legitimately reports 0 hands processed (e.g. empty/invalid hand file), this will incorrectly include that row in totals and can render a misleading 0.00 ms/deal instead of excluding the row (or showing NA).
deals = (
row.hands
if row.hands is not None and row.hands > 0
else deals_from_hand_file(row.file)
)
hands=0 from dtest is real; falling back to listN.txt incorrectly pulled those rows into TOTAL. Co-authored-by: Cursor <cursoragent@cursor.com>
No description provided.