Repository navigation
Flash Next CUDA: the PLE gather keeps a parked worker pool instead of spawning threads per call - #1195
Closed
jschmied wants to merge 1 commit into
Closed
Flash Next CUDA: the PLE gather keeps a parked worker pool instead of spawning threads per call#1195jschmied wants to merge 1 commit into
jschmied wants to merge 1 commit into
Conversation
…er call Ngram.rows spawned min(128, items/4) threads per call and joined them: about 150 us for one decode token, 1 ms for four and 10-11 ms for a prefill chunk. The table now keeps a pool for its life; workers are spawned on first need and park on a futex. A call's parts (the same [lo, hi) ranges as before) are claimed from one counter by the caller and the workers, and each claim that leaves parts unclaimed wakes one more worker, so quick parts stay mostly on the caller and slow (faulting) parts fan out. The first failed part's error is returned, in part order, as before. rows() on the FP4-Local table, p50: 16 items 164 -> 20 us, 64 items 0.99 ms -> 31 us, 4096 items 10.8 -> 0.96 ms, 8192 items 11.0 -> 1.66 ms; output bytes identical. Served (fresh server per arm, A B A B): c=4 decode -1.0 ms/step (61.5 -> 60.5), c=1 and 8k TTFT inside noise; greedy, seeded and resumed-cache replies identical.
Owner
|
Landed on main as e2cfdae with a maintainer fix-up (8e9ab68) covering the pooled gather's bytes and worker ownership, thank you @jschmied. The Flash Next per-layer embedding (PLE) gather now uses a parked worker pool instead of spawning a thread per call. It came through the union campaign's Flash Next cells with identical tokens, and the host suites pass on current main. - Synthema, Ash's AI Assistant. |
ashhart
added a commit
that referenced
this pull request
Oct 24, 2026
Owner
|
This shipped in TensorFold 1.1.4: https://github.com/ashhart/TensorFold/releases/tag/v1.1.4 Thank you @jschmied for the contribution. - Synthema, Ash's AI Assistant. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Flash Next's PLE n-gram gather (
Ngram.rowsinfamilies/flashnext/cuda_ple.zig) spawnedmin(128, items / 4)threads per call and joined them. The gather itself is a few memcpys from the mapped table, so most of a call was thread start-up: about 65-80 µs per thread, 150 µs for one decode token, 1 ms for a 4-token batch (4 streams, or a drafted verify) and 10-11 ms per prompt chunk.The table now keeps a pool for its life (
cuda_ple_pool.zig, 139 lines with a unit test):deinitwakes and joins them;[lo, hi)ranges as before, claimed from one counter by the caller and the workers. Each claim that leaves parts unclaimed wakes one more worker, so quick parts stay mostly on the caller and slow (faulting) parts fan out;Only which thread copies a range changes, so the output bytes are identical by construction. A table without a pool (for example a path that builds
Ngramsome other way) gathers on the caller's thread.Branch
fn-ple-pool@e4062e6a, onmain@bd264368a.Receipt
nvidia/Qwen3.8-Flash-Next-FP4-Local@b9ce18a6(itstensors/index loads since Checkpoints: an index may name a shard in a folder inside the model folder; shard edits keep plain names #1179).rows()on the real table, p50, alternating old / new / old / new, output hashes identical at every size:--no-drafts, then one MTP pair:The c=4 gain has the same size and sign in both rounds. The microbench accounts for about 0.65 ms of it (4 lanes x 4 calls a round); the remaining ~0.35 ms is not explained. Prompt chunks mostly hide the old cost, because the batch path gathers on a side thread while the layers before the PLE layer run.
zig build test -Dnvcc=...exit 0. lean_check 0 new.zig fmtclean.Not run, and why
cuda_ple.zigbelongs to the CUDA family only, so there was nothing to run there.cuda_batch.zigstarts per pass (~28 µs) could use the same pool; left for a separate change.Overlap
#1113 sets the thread count for its EXL3 table in the same function and builds that table without going through
Ngram.init, so a rebase there needs a hand-merge; its table then simply gathers on the caller's thread, as before.