[Feature] Support DeepSeek tile-wise (1×128) FP8 activation dispatch - #39
Open
silencelamb wants to merge 1 commit into
Open
silencelamb wants to merge 1 commit into
silencelamb wants to merge 1 commit into
Conversation
Accept DeepEP-style FP8 activations in Buffer.dispatch: a (data, scales) tuple of contiguous [S, H] float8_e4m3fn data and [S, H // 128] fp32 dequantization scales (one per 1x128 tile, row- or column-major). Dispatch routes FP8 payload and scales together, expands both for duplicate tokens in the epilogue, and zeroes padding. Outputs keep the expert-grouped layout and reuse the existing BF16 communication allocation, so zero_copy, async_finish, cached plans and CUDA graphs keep their contracts. Combine still consumes BF16 expert outputs. When scales are contiguous, 16-byte aligned and H % 512 == 0, scale rows are loaded by TMA into smem alongside their payload rows in both the dispatch and epilogue kernels, removing a dependent global load from the per-token critical path. Other layouts fall back to per-element scale loads. Add distributed tests, a BF16/FP8 dispatch benchmark and README docs.
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
Buffer.dispatchnow accepts DeepEP-style prequantized FP8 activations as a(data, scales)tuple:data: contiguous[S, H]torch.float8_e4m3fnscales:[S, H // 128]torch.float32dequantization scales, one per 1×128 tile (row-major or column-major);Hmust be divisible by 128It returns
(recv_data [NvS, H], recv_scales [NvS, H // 128]), both contiguous, in the same expert-grouped layout as BF16. Padding rows of both data and scales are zeroed. Combine still takes BF16 expert outputs.Design
zero_copy=Truereturns views for both tensors. The README explains thathidden_nvsh_buffer_view(the BF16 expert output) shares their storage.H % 512 == 0(e.g. 3584, 7168), the producer also loads each token's scale row into a small smem ring with TMA, on the same mbarrier as the payload row. The consumer warp then scatters scales from smem with coalesced stores to the destinations that receive the payload (dst >= 0). Other layouts (column-major, misaligned,H % 512 != 0) fall back to per-element global loads.async_finish,router_weights_zero_copy, PDL and CUDA graphs all behave as before. All ranks must use the same activation format for a given call.The staged-scale path matters. With per-element loads on the consumer's critical path, scale handling took up to about one third of FP8 dispatch time (measured by disabling it). At H=3584 with skewed routing, FP8 was then only 0.99–1.16× faster than BF16.
Validation
8× H200 (NV18 full mesh), PyTorch 2.9.1+cu128, nvidia-cutlass-dsl 4.6.2, rebased on
33327eb(Public Release 26/09).torchrun --standalone --nproc_per_node=8 -m pytest -q \ tests/test_dispatch_fp8.py tests/test_dispatch.py tests/test_combine.py torchrun --standalone --nproc_per_node=8 -m pytest -q tests/test_e2e.pytest_e2e.pypasses. (test_e2e.pyneeds its owntorchruninvocation. Collected together withtest_dispatch.py, it fails with "initialize the default process group twice" onmasteras well.)Performance
benchmarks/bench_dispatch_fp8.pyon 8× H200. E=256, K=8, token_padding=128, 32 dispatch SMs, PDL on. 48 configurations: S∈{256, 2048, 8192} × H∈{3584, 7168} × routing bias∈{0, 1} × fresh/cached plan × zero_copy∈{0, 1}. Inputs are prequantized, and timings include scales, duplicate expansion, planning (fresh) and output copies (zero_copy=0).master33327eb)*The +4.09% outlier (S=256, ~60 µs) is noise: three re-runs gave 59.1–60.6 µs upstream and 58.6–59.4 µs on the branch. The BF16 kernel code only changed in its index arithmetic.
FP8 is faster than BF16 in all 48 configurations. Selected rows (cached plan, zero_copy=1):
Each 1×128 tile carries 132 B in FP8 (128 B data + 4 B scale) instead of 256 B in BF16, i.e. 48.4% less activation traffic.