pipe: blocking pipe_wait (COLI_PIPE_BLOCK=1) + PIPE_WORKERS implies PIPE=1#270
pipe: blocking pipe_wait (COLI_PIPE_BLOCK=1) + PIPE_WORKERS implies PIPE=1#270ebootheee wants to merge 1 commit into
Conversation
|
Heads-up: this now conflicts with |
|
Could you rewrite this against current It's 167 commits behind The PIPE/CUDA area moved hard: #274 (pipe2 resident pipeline, device-count-aware gate) and #293 (COLI_CUDA_MTP off by default) both landed. Some of your delta may now be redundant, and some may conflict with the new gating. What changed under you, which makes a rewrite cheaper than it sounds:
What would help it land fast: the smallest version that does one thing. A 300β700 line PR touching If you'd rather not, say so and I'll close it with thanks β no hard feelings either way. And if you think I've misjudged and it should go in as-is, push back: I've been wrong twice today already and both times a contributor caught it. |
|
Datapoint for commit 1 (COLI_PIPE_BLOCK) from a different regime: Apple M5 Pro 64GB, Metal decode with COLI_METAL=1 DIRECT=1 PIPE=1 PIPE_WORKERS=8, GLM-5.2 REAP-pruned E=168 int4, greedy 128 tokens, --ram 50, TOPP=0.7. These measurements are noisy on a shared box, so the method matters. Three interleaved SPIN/BLOCK pairs on one binary, with the env knob as the only variable. I restored a snapshot of
*ambient contention hit this slot and doubled its GPU-phase walls, so I treat it as an outlier. Throughput verdict on this hardware: null. BLOCK means 2.47 vs SPIN 2.37 with the outlier excluded, inside this machine's noise floor. I read that as consistent with your mechanism, not against it. Your win was on CPU decode, where the spinning core competes with the matmul team. Here decode compute runs on the GPU and there are idle cores, so parking instead of spinning frees nothing scarce. One thing I noticed but can't prove at n=3: the BLOCK arm had the tightest spread I've measured on this box, 2.4% across three runs, and the only badly contention-hit run in the sweep was a SPIN one. The blocking wait may be more robust when something else grabs the machine. I can rerun under deliberate background load if that would help decide the default. |
β¦IPE=1 pipe_wait's sched_yield spin storms the scheduler for the full 0.5-3ms of each in-flight expert read; behind COLI_PIPE_BLOCK=1 it parks on a condvar instead (~5us wake, no lost-wakeup: workers store ready with release BEFORE taking the mutex to broadcast, and the waiter re-checks under the lock). Default OFF = byte-identical spin. Pthread pool only: the URING backend has no waiter spin to replace. Setting PIPE_WORKERS>0 in the env without PIPE now implies PIPE=1 with a stderr note: sizing the pool declares the intent to use it (a full benchmark campaign ran with PIPE_WORKERS=16 and the pipe silently off). The implication fires only when the platform default left the pipe off (no-op on _WIN32 where PIPE defaults to 1), only on a positive value (PIPE_WORKERS=0/empty does not enable a clamped 1-worker pipe), and an explicit PIPE=0 always wins. The rule lives in pipe_workers_imply_pipe() so the table is unit-testable. tests/test_pipe_block.c (in TEST_BINS, all platforms): pins the implication table, and drives the pool through 200 generations under each waiter against an on-disk expert fixture β spin as control, condvar arm alternating parked and fast-path waits β verifying identical slot bytes. Measured on the spin side (2x5090 + Gen5 NVMe, GLM-5.2 744B int4, CPU decode): 1.98 -> 2.16 tok/s at 192 tokens (expert-disk service 44.6s -> 33.5s). On Metal/GPU decode an M5 Pro A/B (PR thread) measured no change, consistent with the mechanism: the win is freeing the core the spinner was stealing from the CPU matmul team. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4554cbc to
dc19663
Compare
|
@JustVugg β rebased and narrowed as you asked. This is now just the blocking-wait + the You were right about #274/#293 moving the ground under @michael-denyer β thank you for the disciplined A/B; snapshot-restoring |
What this is now
Rewritten against current
dev(post #274/#293) and narrowed to the piece flagged as worth keeping: the pipe blocking-wait and thePIPE_WORKERSβPIPEimplication. One commit β 39 lines inglm.cplus a test.COLI_PIPE_BLOCK=1:pipe_waitparks on a condvar instead of thesched_yieldspin (~5 Β΅s wake against 0.5β3 ms expert reads; no lost wakeup β workers release-storeready[]before taking the mutex to broadcast, and the waiter re-checks under the lock). Default OFF = byte-identical spin. Pthread pool only: the URING backend has no waiter spin to replace.PIPE_WORKERS>0withoutPIPEimpliesPIPE=1with a stderr note β sizing the pool declares the intent to use it (an entire benchmark campaign once ran withPIPE_WORKERS=16and the pipe silently off). Adapted to currentdev: it's a no-op on Windows wherePIPEnow defaults to 1,PIPE_WORKERS=0/empty does not enable a clamped 1-worker pipe (hardening carried over from the adversarial review of the v1 series), and an explicitPIPE=0always wins. The rule lives inpipe_workers_imply_pipe()so the table is unit-testable.tests/test_pipe_block.c(inTEST_BINS, all three platforms): pins the implication truth table, then drives the pool through 200 generations under each waiter against an on-disk expert fixture β spin as the control arm, condvar arm alternating parked and fast-path waits β verifying identical slot bytes. Temp file is CWD-relative pertest_stops' warning about the Windows job.Numbers
Measured on the v1 branch (2Γ5090 + Gen5 NVMe, GLM-5.2 744B int4, CPU decode): 1.98 β 2.16 tok/s at 192 tokens (expert-disk service 44.6 s β 33.5 s). The ported logic is identical, but I have not re-benchmarked on current
dev. On Metal/GPU decode, @michael-denyer's M5 Pro A/B in this thread measured no change β consistent with the mechanism: the win comes from freeing the core the spinner steals from the CPU matmul team, and on GPU decode with idle cores nothing scarce is being stolen. Default stays OFF.Dropped from this PR
COLI_CUDA_BATCHandCOLI_CUDA_SYNCare out. #274's pipe2 resident pipeline and #293's profiling rework moved the ground they stood on; I'll re-evaluate and re-benchmark against the new code and, if anything still earns its keep, bring it back as a separate focused PR.