DeepSpeed
bb5c23e0 - Use int64 program ids in the SwiGLU and AutoEP fused restore Triton kernels (#8591)

Commit
7 days ago
Use int64 program ids in the SwiGLU and AutoEP fused restore Triton kernels (#8591) # Use int64 program ids in the SwiGLU and AutoEP fused restore Triton kernels Fixes https://github.com/deepspeedai/DeepSpeed/issues/8590 ## The problem Four Triton kernels compute memory offsets from `tl.program_id`, which is int32. When the tensor has more than 2^31 elements, the offset wraps to a negative number. The mask looks like a bounds check, but a negative offset passes it. The kernels then load and store memory before the start of the tensor, and CUDA reports `an illegal memory access was encountered`. | kernel | int32 product | overflows when | |---|---|---| | `_swiglu_fwd_kernel`, `_swiglu_bwd_kernel` (`swiglu_triton.py`, #8244) | `pid * BLOCK_SIZE`, with `BLOCK_SIZE = 2048` | elements > 2^31 | | `_weighted_restore_forward_kernel` (`autoep_fused_token_ops.py`, #8326) | `token * out_stride`, where the stride is the hidden size | tokens x hidden > 2^31 | | `_weighted_restore_backward_kernel` (same file) | `token * grad_out_stride` | tokens x hidden > 2^31 | For example, in the SwiGLU kernels: ```python pid = tl.program_id(axis=0) # int32 offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # wraps negative at pid = 1,048,576 mask = offsets < n_elements # a negative offset passes this check ``` ## When it is reached **SwiGLU** runs in `GroupedExperts` on the gate projection, which has shape `[rows received by this rank, moe_intermediate_size]`. With balanced routing, it faults when `tokens per rank x top_k x moe_intermediate_size > 2^31`: | model | top_k | moe_intermediate_size | faults above, tokens per rank | |---|---|---|---| | Qwen3.5-397B-A17B | 10 | 1024 | 209,715 | | DeepSeek-V3 | 8 | 2048 | 131,072 | These rows are arithmetic, not runs. Unbalanced routing reaches the limit sooner, because one rank can receive up to `expert-parallel size x tokens per rank x top_k` rows. We hit it on Qwen3.5-397B-A17B with 8,192-token sequences on 4 nodes x 8 H200. Every token had been routed to one expert-parallel rank of 32, so SwiGLU received 2,621,568 x 1,024 elements (1.25 x 2^31). **The fused restore** is opt-in (`combine_impl="fused_weighted_sum"`). It faults above 524,288 tokens per rank at hidden size 4096, or above 299,593 at hidden size 7168. ## The change Cast the program id to int64 in all four kernels, so every offset product that uses it is computed in int64: ```python # swiglu_triton.py, both kernels pid = tl.program_id(axis=0).to(tl.int64) # autoep_fused_token_ops.py, both _weighted_restore_*_kernel token = tl.program_id(0).to(tl.int64) ``` Each line has a short comment explaining why, because the mask makes the code look safe without it. Two other `program_id` products in `autoep_fused_token_ops.py` are left as they are: - `_invert_index_kernel` multiplies by a block of 256 over `tokens x top_k` indices. - The hidden-dimension tile in the forward kernel is at most the hidden size. ## Testing One H200, torch 2.8.0+cu126, Triton 3.4.0, `CUDA_LAUNCH_BLOCKING=1`, each size in a fresh process. **Existing unit tests:** `tests/unit/v1/ops/triton_ops/test_swiglu_triton.py` and `tests/unit/v1/ops/triton_ops/test_autoep_fused_token_ops.py`, 86 passed. **Above the limit.** Error is the largest absolute difference from a float32 reference: | kernel | size | `master` | this PR | |---|---|---|---| | SwiGLU, bf16 `[rows, 1024]` | 2,149,580,800 elements (1.001 x 2^31) | illegal memory access | forward 0.0132, backward 0.0077 / 0.0077 | | SwiGLU, bf16 `[rows, 1024]` | 2,684,485,632 elements (1.25 x 2^31) | illegal memory access | forward 0.0147, backward 0.0078 / 0.0075 | | fused restore, top_k 2, hidden 4096 | 524,800 tokens (tokens x hidden = 1.001 x 2^31) | illegal memory access | forward 0.0077, row gradient 0.0075, score gradient 7.6e-6 | For comparison, `master`'s SwiGLU errors below the limit are the same size: forward 0.0144 and backward 0.0076 / 0.0075. They come from rounding the float32 result to bf16. **Below the limit, results are bit-identical to `master`.** The sha256 of every output and gradient matches: | kernel | size | tensors compared | |---|---|---| | SwiGLU | 671,612,928 elements (0.31 x 2^31) | output, gate gradient, up gradient | | fused restore | 524,000 tokens (0.9995 x 2^31) | output, row gradient, score gradient | **No time cost.** SwiGLU, median of 50 calls at 671,612,928 elements: | | forward | backward | |---|---|---| | `master` | 0.962 ms | 1.634 ms | | this PR | 0.962 ms | 1.634 ms | Both run at about 4.2 TB/s forward and 4.1 TB/s backward, so memory traffic sets the time. **No new unit test.** A test needs more than 2^31 elements: 12.9 GB for SwiGLU in bf16, and 25.8 GB for the fused restore forward and backward. I can add one with a skip for GPUs that have less free memory, if you want it in CI. ## Reproducer ```python import torch import torch.nn.functional as F from deepspeed.ops.triton_ops.swiglu_triton import swiglu gate = torch.randn(2_099_200, 1024, dtype=torch.bfloat16, device="cuda") # 1.001 x 2**31 elements up = torch.randn(2_099_200, 1024, dtype=torch.bfloat16, device="cuda") out = swiglu(gate, up) torch.cuda.synchronize() # master: "an illegal memory access was encountered" print((out[-1].float() - F.silu(gate[-1].float()) * up[-1].float()).abs().max().item()) ``` On this PR it prints a last-row error of about 0.01. Signed-off-by: pengdurice <pengduhit@gmail.com>
Author
Parents
Loading