Repository navigation
fix(qwen35/hip): time shared rounds at the served streams and their row buckets - #1830
Conversation
|
Thanks @AbrahamCete. We read this through and ran the HIP host tests on it ( One change before it lands:
A note, not blocking: one timed run per bucket instead of five makes each entry a single sample, so an unlucky sample can move the depth rule at a boundary. If the load-time budget allows, three timed runs for the shared buckets would lower that risk. After the fix, the smallest check is one server per arm: Qwen3.6-35B-A3B MTP, |
97e5e34 to
493704a
Compare
|
Thanks @ashhart. Done in 493704a, and the branch is rebased onto
Receipt: one server per arm, alternated twice. Qwen3.6-35B-A3B MTP,
On three timed runs for the shared buckets, I tried a scratch patch (1 warm and 3 timed runs for shared forwards of 8 rows or more). Tokens stayed equal, and load time went up:
When I cut the runs, a single run from 8 rows up read within about 2% of the five-run mean on both checkpoints, so I kept one. If you'd rather pay the 27B load time for three runs, it's a one-line change and I'll add it. |
…ow buckets The load-time shared cost table timed 4-row windows at 2, 4, 8, 12 and 16 total rows over at most eight streams. The lane core filled the other totals up to 16 from lone-window timings and scaled the 16-row cost proportionally past it. A 4-stream round of 8 rows was priced as two streams of four, totals 9 to 16, which all run the 16-row bucket, got separate timings of different shapes, and totals past 16 never got the bucket they pad to. HIP now times one forward per row bucket a shared round pads to, up to batch_rows, at the served stream count with the bucket's rows split evenly over the streams (windows up to the lane width, which copy drafts reach), and prices every total of the bucket at that forward. The lone window timings and allocate_shared_drafts (off) are unchanged.
Timing every shared bucket at the served streams added about 20 s to a Qwen3.8-27B load at --parallel 16, most of it in forwards of 8 to 256 rows that ran 3 warm and 5 timed passes. From 8 rows on, one pass reads within about 2% of the five-pass mean on both Qwen checkpoints, and the first pass (the graph capture) costs the same as the rest. Forwards of 8 rows or more now run once warm and once timed; shorter ones keep the default passes.
…rows buckets() capped the timed rows at streams * width, so with batch_rows past that cap the table stopped at the cap and the lane core priced larger totals linearly: 65 rows over 4 streams of 16 at 128 batch rows took about 1.015 times the 64-row cost instead of the 128-row forward the round pads to. Each bucket is now timed at its own rows, split evenly over the streams, up to batch_rows; width no longer takes part and leaves buckets(), forwards() and measure(). The rig's scratch caches hold the widest such share. Served HIP sets batch_rows to streams * 16, so every served table and the rig's caches stay as they were.
493704a to
0ba02c0
Compare
|
Thank you @AbrahamCete. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes. |
What this changes
Fixes #1695, and is the pricing fix #1694 waits on. On HIP, Qwen3.6-35B-A3B MTP at four streams fell into a slower, deeper-drafting mode that held per server process. The lane core prices shared rounds from the cost table HIP times at load, and that table did not match the rounds HIP runs:
round_shape.zig), so a 65-row round ran the 128-row bucket but was priced at about a third of that.The changes, one commit each:
fix(qwen35/hip): time shared rounds at the served streams and their row bucketship_serve/costs.ziggetsbuckets(). It times one forward for each row bucket up tobatch_rows, at the served stream count, with the bucket's rows split evenly over the streams.hip_costs.zigsizes its rig for the served streams; the old cap was 8.hip_lanes.zigpasses the window width.core/lanesis unchanged, andallocate_shared_draftsstays off for HIP, as asked in lanes: on HIP, Qwen3.6-35B-A3B MTP at four streams falls into a slower, deeper-drafting mode that holds per server process #1695.fix(qwen35/hip): time forwards of eight rows or more once--parallel 16.mainalready ran, so both checkpoints now load faster than onmain.fix(qwen35/hip): time each shared bucket at its own rows up to batch_rows(from review)buckets()capped the timed rows atstreams × width. Withbatch_rowspast that cap, the table stopped there and the lane core priced larger totals linearly.batch_rows, split evenly overmin(streams, bucket)windows, so the 128-row bucket at 4 streams runs 4 × 32.widthonly fed the cap, so it leftbuckets(),forwards()andmeasure(), andhip_lanes.zigis back tomain.batch_rows = streams * Hip.max_window(engine/native.zig:210), so no served table changes.The cost table only steers draft depth, so tokens are unchanged.
Receipt
mainba7b2c4. The full A/B below ran on 372af1e, with the first two commits.--parallel 4,--levels 4 --tokens 256 --reps 1 --temperatures 0): code 216.5 and 231.3 onmain, 237.7 and 237.8 with this PR; chat 193.9 and 193.5 onmain, 192.6 and 191.4 with this PR; code9f2923ac14d0and chata8c28b8297b4on every server; load 13.4 and 13.2 s onmain, 10.4 and 10.4 s with this PR. 27B--parallel 16load: 35.5 and 35.6 s onmain, 29.3 and 29.4 s with this PR.--parallel 4): code 195.8, 213.2 and 230.8 onmain, 230.8, 232.0 and 232.9 with this PR; the same token hashes; load 13.8 to 14.0 s against 11.1 to 11.3 s.hip-host-test,test-qwen35-hip-hostandcheck-native-hip-hostexit 0.--context 4096 --prompt-cache-gib 0, with the arms alternated server by server.Qwen3.6-35B-A3B MTP,
--parallel 4,tools/bench_concurrent.py --levels 4 --tokens 256 --reps 5 --temperatures 0, 6 servers an arm. Code aggregate tok/s, one value a server:mainmain(182.5 to 194.3), 193.1 with this PR (191.8 to 193.9), 195.6 with perf(hip): wide stream rounds reuse cached weights, and a chunk's code pairs are made once #1694 too (195.2 to 196.0).mainlands slow varies from run to run. An earlier set of 6 servers an arm, with the first commit only, had 4 of 6mainservers slow: code 208.8 onmain, 234.8 with the fix, 236.1 with perf(hip): wide stream rounds reuse cached weights, and a chunk's code pairs are made once #1694 alone, 240.2 with both. Its chat means were 188.1, 194.8, 197.6 and 197.0.Qwen3.8-27B,
--parallel 16,--levels 1,4,8,16 --reps 3, 2 servers a side: unchanged within noise.main; 13.3, 43.9-44.0, 47.2-47.4, 51.0 with this PR.9f2923ac14d0, chata8c28b8297b4; 27B code2b28aa2108de, chata06fab348fbe. No request failed.tf-qwen35-test check: 27B--only invariants42/0/5 and 35B-A3B 46/0/1, the same asmain.loaded instartup line:--parallel 4: 13.7 to 13.9 s onmain, 11.0 to 11.4 s with this PR.--parallel 16: 35.5 and 35.9 s onmain, 29.0 and 28.9 s with this PR.hip_costs_test.zig, built through the publichip_costs.forwardson a recording rig:error.NotOneForward.hip_serve/costs.zig, "a forward of 8 rows reads steady in one run, so it runs once warm and once timed, and 7 rows keeps the default" failed before the second commit ("expected 1, found 3"). "a forward of long_rows rows or more runs once warm and once timed, a shorter one keeps the default runs" covers the threshold itself.hip-host-test,test-qwen35-hip-host,check-native-hip-hostandnative-hipexit 0.zig build testexits 0 onmainba7b2c4 and on this PR, both run from agit archivetree.tools/zig/lean_check.py: 424 problems, the same asmain.zig fmt --checkis clean.forwardsand is unchanged; I have no other HIP checkpoint locally.Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (Tokens are unchanged: equal hashes on every server, andtf-qwen35-test checkmatchesmain.)SUMMARYline is in the receipt. (No kernel changes.)