[SM120] Avoid per-token-count JIT recompilation in swapped GEMM paths - #76
Open
ormandj wants to merge 2 commits into
Open
[SM120] Avoid per-token-count JIT recompilation in swapped GEMM paths#76ormandj wants to merge 2 commits into
ormandj wants to merge 2 commits into
Conversation
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.
Motivation
The SM120 small-M GEMM paths swap A/B and M/N before launch but pass
compiled_dimsthrough unchanged. This makes compile-time dimension labels refer to the swapped kernel axes instead of the caller's logical axes.For the DeepSeek-V4 W_o_A shape, the logical token dimension is intended to remain dynamic. After the swap, the unchanged labels instead make the residual token count compile-time, producing a separate JIT kernel for each token count.
Observed failure
A controlled sequence sent 40 distinct, cache-busted prompts near 8K tokens through a server configured with an 8,192-token prefill chunk size.
One prompt contained 8,200 input tokens. After the first 8,192-token chunk, this left an eight-token residual. It was the first workload on that persistent cache to require the corresponding residual shape.
That request took 2.931 seconds. Nearby prompts containing 8,195–8,199 input tokens completed in approximately 1.03–1.08 seconds.
Server metrics located the additional time in
prefill_forwardandchunked_prefillon both TP ranks. During the request, the only newly created cache artifacts were the CUDA source and cubin for an SM120 FP8/FP4 BMM kernel.The generated source appeared 891 ms after the request began, and the cubin completed 181 ms before the response completed. The generated CUDA specialized the dimensions as
(0, 8, 4096), making the eight-token residual a compile-time dimension.Modifications
mandnincompiled_dimswhen an SM120 path swaps operands.sgl_deep_gemmtest runner.Direct kernel validation
A cold-cache diagnostic used the production W_o_A shapes: activation
T × 4 × 4096, weight4 × 1024 × 4096, andT=1..32.The unpatched path generated 32 kernels, with compile-time shapes
(0, T, 4096). The patched path generated one kernel with compile-time shape(1024, 0, 4096).For token counts 3 and 11, the regression test computes independent BF16 PyTorch references:
torch.einsum("bhr,hdr->bhd", activation, weight)activation @ weight.TThe DeepGEMM paths consume the corresponding UE8M0-scaled FP8 operands and produce BF16 output.
DeepGEMM's
calc_diffconverts the output and reference to FP64 and computes:1 - 2 * dot(output, reference) / (||output||² + ||reference||²)Both regression-test paths require a value below
1e-3. The separateT=1..32diagnostic checked outputs atT=1,8,16,32and observed values from0.000695to0.000720.Serving validation
The same 40 saved request bodies were replayed in the same order on the patched server with an empty DeepGEMM runtime cache.
The first request incurred the expected general first-use kernel compilation. After that initialization, requests 2–40 completed in 0.869–0.897 seconds. The 8,200-token prompt that previously triggered the 2.931-second compilation completed in 0.891 seconds.
The patched cache contained the runtime-token kernel shape
(1024, 0, 4096)and did not contain a(0, 8, 4096)residual-token specialization.A previously unseen residual token count therefore no longer caused a new W_o_A kernel compilation. General first-use JIT compilation remains unchanged.
Prepared with AI assistance.