Fix SM100 V32 sparse decode FP8 handling#184
Open
jianglan89 wants to merge 1 commit into
Open
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.
Summary
Background
The real B300 precision issue is the V32 FP8 scale handling in the SM100 sparse decode head64 path. V32 stores four float32 scale values per token. The old SM100 code converted those float32 scales through e8m0 before dequantization, which changes the scale values and produces wrong FP8 dequantization.
SM9x and SM10x do not share this sparse decode implementation. SM9x was already correct; the bug is in the SM100 path.
flowchart TD A[V32 FP8 sparse decode] --> B{GPU path} B -->|SM9x / H20| C[SM90 path] C --> D[reads V32 scales correctly] D --> E[matches PyTorch reference] B -->|SM10x / B300| F[SM100 head64 path] F --> G[BUG: float32 scale -> e8m0] G --> H[wrong dequantization] H --> I[precision mismatch] J[fix] --> K[read float32 scale directly] K --> L[SM10x matches PyTorch reference]The V32 FP8 KV cache stores each token as 656 bytes:
[0, 512): 512 e4m3 FP8 values for the latent/nope part[512, 528): four float32 scale values, one per 128 FP8 values[528, 656): 64 bf16 values for the RoPE partThis patch also masks positive sparse indices outside the physical KV cache. That is a related correctness fix for partial/local KV cache inputs, but it is not the main source of the B300 numeric drift. With OOB indices manually masked before calling the old SM100 kernel, the old path still differs from the PyTorch reference by about 28%-32% relative mean error on the H20 dump layers. That remaining error comes from the V32 scale conversion through e8m0.
Before / After
All numbers below compare
flash_mlaagainst the exact PyTorch dequantized sparse decode reference on the same V32 FP8 H20 dump, running on CUDA 12.9 / sm103.To isolate the root cause, I also manually masked physical-OOB indices before calling the old SM100 kernel. The old kernel still had large error, which points to the V32 scale conversion as the primary cause.
Testing
PYTHONPATH=tests CUDA_LAUNCH_BLOCKING=1 python tests/test_flash_mla_sm100_sparse_decode.py