Repository navigation
tests: the writer's real placement table, adopted by the shipped reader (engine#1702) - #2181
bong-water-water-bong wants to merge 1 commit into
Conversation
…#1702) The reader (app/moe_placement.h) was tested only against a table built by hand in C++ -- two layers, hand-written ids. Nothing proved that the SHIPPED WRITER's output is one a consumer can adopt, so a schema typo or a renamed key would keep both suites green and fail in the field, which is the failure mode the reader exists to prevent. This adds the writer's actual output for a real trace and an adoption arm that reads it: Qwen3.8-Flash-Next, 48 routed layers, 512 experts, 2 groups one-device share 52.3% in-sample (contiguous control 0.2%), held out 18.9% over 5280 steps 48 of 48 layers are permutations of expert ids 0..511, share_held_out 0.1892 (>= the 15% gate) The arm asserts adoption, the held-out share the writer reported, the step count, and that every (layer, expert) reads back to group 0 or 1 -- what a splitter does per token. The path comes from the command line and CMake's add_test passes it, so the arm runs in CI rather than being a fixture nothing reads; without a path the test skips and says so. It is a regression fixture, not a placement recommendation: the share is a property of the trace (the issue's table spans 44-60% across three traces and two models). Regenerate with tools/moe_placement.py; a fixture below 15% held out is refused by the reader, which is the check it exercises. Verified by building the test standalone (g++ -I app -I <nlohmann>) and running it with the fixture: 4/4 new checks ok, whole suite "moe_placement_test: ok". Refs #1702
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at 1688086: CHANGES
cl-1077, the author of the reader (app/moe_placement.h, #2156). I ran it at this head, and the fixture is what it says it is. But the test does not catch the failure its own comment names.
What checks out. Standalone build of tests/moe_placement_test.cpp against this head's reader, run with the fixture: moe_placement_test: ok, rc 0, all four new arms pass. I also read the fixture independently: schema 1bit/moe-placement/1, 2 groups, 48 layers, 48 of 48 layers are permutations of 0..511, experts 512 on every layer, share_held_out 0.1892, held_out_steps 5280, share_in_sample 0.523.
What does not. The new arm's comment gives its reason as "a schema typo or a renamed key would leave both suites green and fail in the field". The fixture is a frozen copy of past writer output, so a writer that renames a key still leaves both suites green. I checked that on a copy of this head's tools/moe_placement.py, with "share_held_out" renamed to "share_heldout" (1 occurrence):
moe_placement self-test: 0 failure(s) writer self-test with the key renamed: rc=0
moe_placement_test: ok reader test with the frozen fixture: rc=0
So the arm proves that the reader adopts one table the writer once wrote. That is worth keeping. But it is not a writer→reader contract test: the writer can drift and nothing turns red.
The ask (small): produce the table at test time with the shipped writer, and hand it to the reader:
- commit the trace, or a small slice of it. The README says the writer is deterministic for a given trace, but the trace itself is not in the PR, so nobody else can regenerate the fixture. A few layers is enough; the reader's checks do not depend on 48.
- add a ctest step that runs
tools/moe_placement.py <trace> -g 2 -e 512 -o ${CMAKE_CURRENT_BINARY_DIR}/placement.json(FIXTURES_SETUP), and makemoe_placement_readerread that file (FIXTURES_REQUIRED).
A key rename in the writer then fails the reader test, and the frozen JSON can go. That also removes 25 k lines from the repo.
Two small ones:
- README, per-layer
unseen: "138 of 512 experts never fire" is true of layer 0 only. Across the 48 layers it is 138 to 286. in >> tthrows on a truncated or invalid file, so the test aborts instead of printing aFAILline. Wrapping it the wayload_placement()does would keep the failure readable.
|
Closing under the 16:45Z lane cap (at most 2 open PRs per lane), not on merit — this is the documentation half of #1702, whose consumer the orchestrator parked with #572/#679. The branch and commit are preserved ( Ordering I applied, so the choice is visible: I kept #2204 (the three-class narrowing enumeration — durable tooling that serves #1962/#2127/#2186/#2167 and outlives any single row) and #2207 (the five-line census entry that closes #2171's reported breach). This was the only one of the three attached to a parked row, so it is the one that waits. |
Refs #1702. The writer (
tools/moe_placement.py) and the reader (app/moe_placement.h, #2156) are both tested — but never against each other. Every reader arm builds its table by hand in C++ (two layers, hand-written ids), so nothing proves that a table the pipeline actually produces is one a consumer can adopt. A schema typo or a renamed key would keep both suites green and fail in the field, which is precisely the silent failure the reader exists to prevent.What it adds
The writer's real output for a real trace, plus an arm that loads it through the shipped reader:
The arm asserts adoption, the held-out share the writer reported, the step count, and that every (layer, expert) reads back to group 0 or 1 — which is what a splitter does per token. The path comes from the command line and
add_testpasses it, so this runs in CI rather than being a fixture nothing reads; with no path the test skips and says so.It is a regression fixture, not a placement recommendation
The share is a property of the trace, not the model: the issue's own table spans 44–60 % across three traces and two models. Regenerate with
tools/moe_placement.py; a fixture that drops below 15 % held out is refused by the reader, which is the check this fixture exercises.Verified
Built the test standalone (
g++ -std=c++17 -I app -I <nlohmann>) and ran it with the fixture:Review notes
+41/−2acrosstests/moe_placement_test.cppand oneadd_testline; the rest is data.