perf(gdn): optimize post-conv and causal-conv kernels - #155
Conversation
Build and upload exact causal-convolution work descriptors once per step, reuse them across GDN layers, and make the exact register-kernel mapping the default. Keep the production AsyncLLM benchmark path in the same checkpoint so the measured scheduler and kernel improvement is reproducible on current upstream/main. Row: row/KERNEL-SSM-MAMBA-EXACT-CHUNKS FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
Canonicalize semantic consumer bodies without Python-version-only AST fields, and gate the representation under Python 3.12 and 3.13. FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
Make the row-PR provenance visible to GitHub's synthetic merge check without changing the measured tree. FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
Record the merged Gemma-4 MoE path as known merged-GEMM drift and close the stale embeddings claim after PR mudler#137 landed. FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
Select the measured sm_120 post-conv residual, reject the existing split grid, and bind the upstream 16-token CUDA experiment and gates. FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
Port the upstream 16-token per-head schedule behind an opt-in flag, preserve the existing reduction order byte-exactly, and record the sm_120 A/B evidence. Row: row/KERNEL-SSM-MAMBA-POSTCONV-TILE FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
Route runtime-width and both K=4 CUDA launch arms through the same portable, mutation-tested callback dispatcher. Pin strict arm-2 parsing and resolved non-K4 fallback semantics. row/KERNEL-SSM-MAMBA-POSTCONV-TILE FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
FOLLOWING_AGENTS_PROTOCOL Assisted-by: Codex:gpt-5.6-sol [Codex]
…re-push sandbox (#159) Two guards on main were RED and between them blocked every open external contributor PR (#127, #154, #155) and every push. Both premises were verified in the tree before changing anything. 1) check-device-leakage: src/vllm/v1/worker/gpu/runner.cpp named vt::DeviceType::kCUDA in the device-agnostic shared layer (DSR bucket 'kcuda' 1 > baseline 0). It came in with the QueueSupportsAsyncInputCombine rescope during the PR #140 fix round - ours, not the contributors'; richiejp reported it in #127's honest gaps. Fixed the way the guard's own message prescribes, mirroring the SupportsAuxStream precedent: ask the backend, not the device. New vt::Backend::SupportsAsyncSampledTokenReadback() (base false) answers whether the host may validly read the sampled token id back between steps; CPU overrides true (host and device memory are one allocation) and CUDA overrides true (the id is device-mirrored). The runner asks vt::TryGetBackend(queue.device.type), whose nullptr for a device absent from the build also subsumes the old #ifdef VLLM_CPP_CUDA guard. SEMANTICS UNCHANGED: CPU async-ON, CUDA async-ON, discrete non-CUDA (ROCm gfx1201) async-OFF - the "!"-token hazard stays closed. 2) .githooks/pre-push ran check-policy.py inside a PARTIAL export (README.md docs scripts .agents), but policy_contract.py:428 asserts AGENTS.md is a non-symlink regular file and resolves its Markdown links against that sandbox. AGENTS.md and its .env.example link were both missing, so the hook failed closed on content that is fine in the real tree - every push refused. EXPORT_PATHS is now a superset of what the CHECKERS read. Gates: check-device-leakage RED->GREEN (kcuda=0, DSR 32 == baseline 32); all four hook checkers OK in the reproduced sandbox; test_async_llm 8/8-347, test_engine_core 6/6-44, test_llm_engine 11/11-204 (CPU still resolves async-ON); clean -Werror CPU build; full 11-gate record battery green. FOLLOWING_AGENTS_PROTOCOL Assisted-by: Claude (Opus 5) via Claude Code
localai-bot
left a comment
There was a problem hiding this comment.
Read through this one on top of #127. The shape is right and I want it, with one sequencing note.
What I like:
- Both arms are genuinely opt-in.
ConvChannelTileArmFromEnvaccepts only the exact strings1and2; unset,0, and every other spelling keep arm 0, and non-K=4shapes fall back regardless.GdnPostConvTokenTileFlagIsOnlikewise keeps the byte-identical megablock default. That is the conservative default discipline we ask for, and the comments say plainly that a microbenchmark win implies no default flip. - Putting the arm selection in one
ConvChannelTileLaunchContractForseam shared by the CUDA launcher and the tests is the right call — the tests exercise the production branch logic instead of a parallel copy, with no launch counter or debug state in the hot path. VT_CONV_CHANNEL_TILEis documented indocs/ENVIRONMENT.mdwith the fallback semantics spelled out, and both spikes are written up under.agents/specs/.
Sequencing: this is stacked on #127, so it lands after it. Its pr-size and documentation-checkpoint failures are largely inherited from the stack; once #127 is in, please rebase so this PR shows only its own delta and we can judge those two on their merits.
Not a blocker, a question: since both arms stay off until same-binary correctness and performance gates close — what would close them for you? If it is a GB10/sm_121 A/B I can run it here; if it needs sm_120 specifically, say so and I will note the arms as measured-on-sm_120-only in the kernel matrix so nobody later mistakes "opt-in" for "unproven".
Same note as on #127: agent-record is currently red repo-wide because .agents/policy-cutover points at a pre-squash SHA that never landed on main. Not yours.
|
Reviewed on GB10 (sm_121a, full production build: CUTLASS + FA2 + vendored Triton-AOT). The structure of this PR is exactly right — committed spikes with upstream anchors, two independently selectable default-off arms, and a retained falsification arm. Most of the red CI is staleness. But there is one real blocker, and it is in the new work. Blocker: a 0-ulp equivalence regression that
|
| tree | test_ops_gdn |
|---|---|
current main (0cf3dbbb) |
66 cases, 4300/4300, SUCCESS |
this branch rebased on main |
67 cases, 4630/4631, 1 FAILED |
c=1024, k=4 is precisely the shape VT_CONV_CHANNEL_TILE specialises, and 2661 mismatching elements is not a rounding tail — it is a wrong result, at default settings, on a test that asserts bit equality. Note this fires with the arms unset, so it is not confined to the opt-in path.
Worth checking whether the specialisation changed what the "tiled" reference computes for k=4, rather than only what the register kernel computes — if the reference moved, the test is comparing two new things and the 0-ulp claim in the spike no longer holds.
What I verified, so you don't have to re-do it
Rebasing onto main clears three of the four red checks by itself:
device-leakage— not your bug. Your branch predates1f3b7148, which replaced the rawqueue.device.type == kCUDAtest inQueueSupportsAsyncInputCombinewith theBackend::SupportsAsyncSampledTokenReadback()capability. Rebased, the DSR bucket is clean.pr-size— errors withbase must be an ancestor of head, pure staleness. Passes after rebase.agent-record— green on currentmain.
And the default-off contract holds, which is the important safety property:
default (arms unset) 27B 235/235 35B 315/315 (SACRED)
VT_GDN_POSTCONV_TOKEN_TILE=1 27B 235/235
VT_CONV_CHANNEL_TILE=1 27B 235/235
test_gdn_prefill_conv 88/88
So both arms are token-safe on the 27B gate; the problem is confined to the conv equivalence test above.
Still owed after that
doc-checkpointis per-commit: commits66087699and the final record commit change.agents/benchmark-record.mdwithout touching.agents/NOW.md. Both need the NOW row in the same commit.- STATUS ratchet slack —
test_the_live_page_keeps_the_character_ratchet_tightwantsslack <= 25; re-pincharsto the measured value in the same change.
Heads-up for the rebase: main rewrote AGENTS.md and deleted the structured state record (.agents/state.csv, state-events/, state-index/), so drop any edits to those — narrative lives in the commit message now. I hit a conflict there myself and briefly re-added a 7k-line archived blob before catching it.
I haven't pushed the rebase or touched the code — findings go back to you rather than getting repaired in review. Fix the conv equivalence and the two doc gates and I'll re-run this whole set and merge.
Row
KERNEL-SSM-MAMBA— Qwen GDN post-conv and causal-conv kernel sublevers; one row per PR.Before starting
scripts/ready-for-helper.pyresult when applicable:.agents/kernel-matrix.md:157(KERNEL-SSM-MAMBA,INVENTORIED), feedingROAD-V1-C2-LOCAL-BF16. Spikes:.agents/specs/sm120-qwen35-postconv-token-tile-2026-08-08.mdand.agents/specs/sm120-qwen35-conv-channel-tile-2026-08-08.md.src/vt/cuda/cuda_gdn.cu,src/vt/cuda/gdn_prefill_conv.h,tests/vt/test_gdn_prefill_conv.cpp,tests/vt/test_ops_gdn.cpp, and pinned vLLM/FLA causal-conv plus fused post-conv kernels recorded withfile:lineanchors in the two spikes.What changed
Add two independently selectable, default-off CUDA specializations.
VT_GDN_POSTCONV_TOKEN_TILE=1ports the upstream-shaped 16-token/four-warp post-conv schedule while preserving the existing 128-lane reduction tree byte-for-byte.VT_CONV_CHANNEL_TILE=1specializes the causal-conv path for width four at the existing one-channel-per-thread grid; arm=2is retained as the measured 256-channel/two-channels-per-thread falsification arm. Production dispatch and the portable mutation gate share one callback dispatcher, so the tests prove that each selected arm is actually invoked.Evidence
scripts/agent-preflight.shpasses after rebasing onto canonicalupstream/mainc05cee1d2; all record gates, mutation suites, live-row audit, and committed-range document checks are green.vllm-benchrelink; portable dispatch 9/9 cases, 88/88 assertions; CUDA GDN 67/67 cases, 4631/4631 assertions; Qwen3.5 paged-forward 4/4 cases, 8/8 assertions. The accepted production arms produce identical token hashes.docs/STATUS.mdanddocs/BENCHMARKS.mdupdated; the model/feature surface did not expand. Full profiler evidence and the void stale-link series are retained in the two specs and.agents/benchmark-record.md.Speed claims
${GPU_LOCK}and they are recorded indocs/BENCHMARKS.mdwith the repro recipe.On the local RTX 5070 Ti / Qwen3.5-4B c32 workload, the post-conv tile reduces graph-node time 227.887 to 122.587 ms (1.859x) and improves every observed enclosing axis, including total/output throughput by 0.532%. The causal-conv K4 arm reduces graph-node time 234.605 to 219.506 ms (6.44%) and improves whole-run total/output throughput by about 0.12%. The 256-channel arm is 4.05% slower than the K4 arm and is recorded as falsified.
Honest gaps
vllm-benchhad not relinked the rebuilt CUDA library. The accepted series explicitly relinked the benchmark and trace-proved the specialized kernels.mainc05cee1d2independently failscheck-device-leakage.py:src/vllm/v1/worker/gpu/runner.cppcontains one new hard-codedDeviceType::kCUDA(DSRkcuda1 > baseline 0). perf(gdn): dispatch exact causal-conv chunks #127 and this PR inherit that merged-tree failure; neither kernel diff introduced it.