[PyTorch] Fix FP8 illegal memory access in single-process multi-GPU execution - #3469
[PyTorch] Fix FP8 illegal memory access in single-process multi-GPU execution#3469SuperGoodGame wants to merge 1 commit into
Conversation
|
da96593 to
dc980e4
Compare
…xecution Single-process multi-GPU execution (e.g. accelerate.dispatch_model or plain device_map placement) leaves the current CUDA device different from a module's device. TE resolves the kernel-launch context, the runtime-compiled kernel cache and recipe-state allocation from the current device, so a module placed off the current device crashes with an illegal memory access, and the delayed-scaling amax reduction concatenates buffers that live on different devices. - Pin the current device to the input's device for the duration of a module forward (prepare_forward / end_forward; zero cost when they already match) and of a fusible-ops fuser call. Autograd already runs backward nodes on the gradient's device. - Allocate recipe state (scale, amax_history) on the module's own device, derived from its parameters/buffers, instead of the current device. The fusible ops construct their recipe state after registering the weight for the same reason. - Quantize weights on their own device when they are created under quantized_model_init. - Finalize the delayed-scaling amax buffer per device. Single-device buffers take the existing path. Multi-device buffers without a distributed reduction are updated locally per device; with a distributed reduction the entries are gathered to the first-registered device in registration order (one copy per device), reduced with the same single all-reduce as before, and scattered back, so the collective count, order and size are unchanged. Fixes NVIDIA#3124 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: SuperGoodGame <985236470@qq.com>
d574681 to
8adc2fb
Compare
|
Rebased onto current Re-verified on current @timmoon10 @ksivaman could you take a look when you have a chance and trigger |
Summary
Fixes #3124.
Single-process multi-GPU execution (for example
accelerate.dispatch_modelor plaindevice_mapplacement) triggers a CUDA illegal memory access during FP8 forward when a TE module lives oncuda:1but the current device iscuda:0. The same model works on a single GPU.TE resolves three things from the current CUDA device instead of from the tensors it is given: the kernel-launch context (including the runtime-compiled kernel cache), recipe-state allocation, and the delayed-scaling amax reduction. All three are fixed here in the Python layer.
Changes
prepare_forwardpins the current device to the input's device for the duration of a module forward andend_forwardrestores it; the pin is also released if preparation raises. When the devices already match this is one integer compare. The fusible-opsOperationFuser.__call__runs under the input's device the same way. Backward needs nothing: autograd already runs each node on its gradient's device.RecipeState.createnow receives the module's own device, derived from its parameters/buffers (get_module_device), instead of defaulting to the current device. The fusible ops construct their recipe state after registering the weight for the same reason. Weights created underquantized_model_initare quantized on their own device.torch.catis not valid. Single-device buffers (checked in O(1) via a per-key device set maintained at registration) take the existing path unchanged. Multi-device buffers are grouped by device, preserving registration order, and updated on each device. With a distributed reduction, the groups are gathered to the first-registered device in registration order (one copy per device), reduced with the same single all-reduce as before, and scattered back, so the collective count, order and size are unchanged.Testing
tests/pytorch/test_multi_device_fp8.py(needs 2 GPUs, never callstorch.cuda.set_device, asserts device placement rather than "no exception" because peer access masks the crash):quantized_model_initprepare_forwardraisesops.BasicLinearon a non-current deviceAll 7 fail on unmodified
mainand pass with this PR.Also run locally on
main@ 224f6ec (H200 sm_90, 2.10 torch, CUDA 12.8):test_sanity.py: 8812 passed.test_fusible_ops.py: 1629 passed.test_custom_recipe.py,test_recipe.py: passed.test_numerics.py -k "fp8 or checkpoint or recompute",test_cuda_graphs.py,test_torch_compile.py: failure sets byte-identical to unmodifiedmain(pre-existing attention mismatches from cuDNN 9.10 in this environment).reduce_amax=True, 10 forward/backward iterations; both ranks produce the same output/state hash.Out of scope / follow-ups
quantizer(tensor)/tensor.dequantize()calls on a tensor that is not on the current device still launch on the current device. The thorough fix is a device guard in the C++ bindings; this PR covers the module, ops and construction paths.get_dummy_wgrad, the ALiBi cache,skip_fp8_weight_update_tensor).