Conversation
for more information, see https://pre-commit.ci
|
| set(CMAKE_CUDA_ARCHITECTURES 70 80 89 90) | ||
| endif() | ||
| endif() | ||
| find_package(MUSA REQUIRED) |
There was a problem hiding this comment.
MUSA Replaces Supported Builds
The common build now always requires MUSA, MUSAToolkit, and MCCL from a hard-coded /usr/local/musa installation. Because setup.py adds this common extension for core-only, CUDA PyTorch, and JAX installations, supported non-MUSA builds fail during CMake configuration instead of using the existing CUDA build path.
Knowledge Base Used: Build, extensions, and packaging
| import torch | ||
| import torch.utils | ||
| import torch.utils.data | ||
| import torch_musa |
There was a problem hiding this comment.
Root initialization now imports torch_musa before the existing optional-framework error handling. In core-only, JAX-only, or ordinary CUDA installations without torch_musa, import transformer_engine therefore fails even though MUSA is not a common runtime dependency.
Knowledge Base Used:
| torch.cuda.is_available = torch.musa.is_available | ||
| torch.cuda.current_device = torch.musa.current_device | ||
| torch.cuda.device_count = torch.musa.device_count | ||
| torch.cuda.set_device = torch.musa.set_device | ||
| torch.cuda.DoubleTensor = torch.musa.DoubleTensor | ||
| torch.cuda.FloatTensor = torch.musa.FloatTensor | ||
| torch.cuda.LongTensor = torch.musa.LongTensor | ||
| torch.cuda.HalfTensor = torch.musa.HalfTensor | ||
| torch.cuda.BFloat16Tensor = torch.musa.BFloat16Tensor | ||
| torch.cuda.IntTensor = torch.musa.IntTensor | ||
| torch.cuda.synchronize = torch.musa.synchronize | ||
| torch.cuda.get_rng_state = torch.musa.get_rng_state | ||
| torch.cuda.set_rng_state = torch.musa.set_rng_state | ||
| torch.cuda.synchronize = torch.musa.synchronize | ||
| torch.cuda.empty_cache = torch.musa.empty_cache | ||
| torch.Tensor.cuda = torch.Tensor.musa | ||
| torch.cuda.manual_seed = torch.musa.manual_seed | ||
| torch.cuda.Event = torch.musa.Event | ||
| torch.cuda.Stream = torch.musa.Stream | ||
| torch.cuda.current_stream = torch.musa.current_stream | ||
| torch.cuda.set_stream = torch.musa.set_stream | ||
| torch.cuda.get_device_properties = torch.musa.get_device_properties | ||
| # add torch.musa.current_devce() to activate torch.musa.default_generators | ||
| d = torch.musa.current_device() |
There was a problem hiding this comment.
Import Globally Rewrites PyTorch
Importing Transformer Engine now replaces process-wide PyTorch CUDA, tensor-construction, distributed, device, and autocast APIs with MUSA implementations and immediately initializes a MUSA device. As a result, unrelated PyTorch code in the same process receives altered CUDA behavior merely because the root package was imported.
Knowledge Base Used: PyTorch runtime and public API
| // m.def("fa_prepare_fwd", &transformer_engine::pytorch::fa_prepare_fwd, | ||
| // "Prepare QKV for Flash Attention", py::call_guard<py::gil_scoped_release>()); | ||
| // m.def("fa_prepare_bwd", &transformer_engine::pytorch::fa_prepare_bwd, | ||
| // "Backward of QKV preparation for Flash Attention", | ||
| // py::call_guard<py::gil_scoped_release>()); | ||
| m.def("fused_attn_fwd", &transformer_engine::pytorch::fused_attn_fwd, | ||
| "Fused Attention FP8/BF16/FP16 FWD with separate Q, K and V"); | ||
| m.def("fused_attn_bwd", &transformer_engine::pytorch::fused_attn_bwd, | ||
| "Fused Attention FP8/BF16/FP16 BWD with separate Q, K and V"); | ||
| m.def("copy_to_kv_cache", &transformer_engine::pytorch::copy_to_kv_cache, | ||
| "Copy new KV tokens to KV cache", py::call_guard<py::gil_scoped_release>()); | ||
| m.def("convert_thd_to_bshd", &transformer_engine::pytorch::convert_thd_to_bshd, | ||
| "Convert a tensor from THD to BSHD", py::call_guard<py::gil_scoped_release>()); | ||
| m.def("convert_bshd_to_thd", &transformer_engine::pytorch::convert_bshd_to_thd, | ||
| "Convert a tesnor from BSHD to THD", py::call_guard<py::gil_scoped_release>()); | ||
| // m.def("copy_to_kv_cache", &transformer_engine::pytorch::copy_to_kv_cache, | ||
| // "Copy new KV tokens to KV cache", py::call_guard<py::gil_scoped_release>()); | ||
| // m.def("convert_thd_to_bshd", &transformer_engine::pytorch::convert_thd_to_bshd, | ||
| // "Convert a tensor from THD to BSHD", py::call_guard<py::gil_scoped_release>()); | ||
| // m.def("convert_bshd_to_thd", &transformer_engine::pytorch::convert_bshd_to_thd, | ||
| // "Convert a tesnor from BSHD to THD", py::call_guard<py::gil_scoped_release>()); |
There was a problem hiding this comment.
Active Extension Bindings Removed
These bindings are removed while active Python paths still call them. KV-cache inference calls tex.copy_to_kv_cache, THD attention calls the conversion functions, and interleaved FlashAttention calls the preparation functions. Those paths now raise AttributeError instead of running the required native operation.
Knowledge Base Used: PyTorch runtime and public API
| fused_attention_backend = _attention_backends["fused_attention_backend"] | ||
| use_unfused_attention = _attention_backends["use_unfused_attention"] | ||
|
|
||
| use_flash_attention = True # TODO:huang.huang set fa manually now! |
There was a problem hiding this comment.
FlashAttention Is Always Forced
Setting use_flash_attention after backend selection discards all capability and configuration checks. When FlashAttention is missing, disabled, unsupported for the inputs, or intentionally superseded by fused or unfused attention, execution still enters self.flash_attention instead of the selected fallback, causing unsupported execution or a runtime failure.
Knowledge Base Used: PyTorch runtime and public API
| @@ -100,7 +101,7 @@ | |||
| fa_utils.is_installed = True | |||
There was a problem hiding this comment.
FlashAttention Detection Is Fabricated
Hard-coding the detected FlashAttention version to 2.5.0 can mark it installed even when the package is absent or a different version is present. On a compatible device, initialization then imports flash_attn.flash_attn_interface unconditionally, so an environment that should fall back cleanly can fail during import or call APIs incompatible with its actual installation.
Knowledge Base Used: PyTorch runtime and public API
| check_cuda_runtime(cudaEventCreateWithFlags(&context->copy_done_event, cudaEventDisableTiming), | ||
| "cudaEventCreateWithFlags(remap-and-copy)"); | ||
| context->slot_done_events.resize(slots.size(), nullptr); | ||
| for (auto &event : context->slot_done_events) { | ||
| check_cuda_runtime(cudaEventCreateWithFlags(&event, cudaEventDisableTiming), | ||
| "cudaEventCreateWithFlags(remap-slot)"); |
There was a problem hiding this comment.
In a multi-device process where the current device, slot device, or stream device differ, these completion events are created on the calling thread's ambient device before slot devices are examined. The worker later switches to each slot's device and records the events on the supplied stream, causing cross-context event recording or waiting to fail and leaving the activation reload unsynchronized. Events must be created for the slot device, and mixed-device batches or mismatched streams should be rejected.
Description
Add a fixed-VA CUDA VMM activation slot so a Graph-captured tensor address can keep its virtual address while physical backing is released and remapped outside Graph replay. This is the TE primitive behind local whole-layer CUDA Graph + fine-grained CPU activation offload.
Fixes # (issue)
Local CUDA Graph and CPU offload currently conflict on pointer semantics:
A naive
empty()reload allocates a new pointer. Capture recordedsrc.data_ptr(), so replay either reads stale storage or silently aliases an allocator reuse of that VA. Related: NVIDIA/Megatron-LM#3697.This PR does not implement Megatron scheduling. It exposes the VMM slot, resident workers, and deferred H2D so a caller (Megatron
fine_grained_activation_offload) can:slot.tensor.data_ptr()(stable for the run).CUDA driver names stay CUDA-shaped in source. On MUSA they map at compile time through
musify.h(cuMemMap→muMemMap,cudaMemcpyAsync→musaMemcpyAsync, …). Python keeps CUDA names (CUDAActivationVMMAllocation,cuda_stream); a MUSA runtime still works whentorch.cudais aliased, anddevice.type in ("cuda", "musa")covers TE tests that import this module withoutmusa_patch.Type of change
Changes
transformer_engine/pytorch/csrc/extensions/vmm_activation.cppand bind it frompybind.cpp/extensions.h.transformer_engine/pytorch/vmm_activation.py.tests/pytorch/test_vmm_activation.py(async reload / host-callback lifetime).musify.hVMM aliases used by this file (cuMemAddressReserve,cuMemCreate,cuMemMap,cuMemSetAccess,cuMemUnmap,cuMemRelease,cuMemGetInfo, …).Not in this PR’s intended review surface: GEMM / DPA /
cpu_offload.pyMegatron handler changes that happen to sit on the same working branch.Why a TE primitive (not a Megatron allocator wrapper)
Graph capture records a device pointer. CPU offload must drop physical pages. Those two facts only compose if the Graph sees a VA that outlives the pages mapped under it:
The captured Graph never sees a new pointer. Physical handles may change; the VA must not.
CUDAActivationVMMAllocationconstructs the slot, wraps a non-owningfrom_blobview, and assertstensor.data_ptr() == info["address"]after construct / remap / adopt. Using that view while the slot is unmapped is a hard page fault — that is intentional; it turns the silent allocator-reuse bug into a crash.Slot lifecycle
Synchronous
unmap_and_release()/create_and_remap()remain for tests and fallback. Training uses the async path.Address stability check after every remap / adopt:
Python API
transformer_engine.pytorch.vmm_activation:CUDAActivationVMMAllocation(shape, stride, dtype, device)release_hooks_after(allocs, stream)remap_hooks_after(allocs, stream)remap_and_copy_after/remap_and_copy_slot_afterremap_only_slot_afterlaunch_remap_slot_h2d(ctx, i, stream)remapped, submit H2D onstreamwait_remap_slot_on_stream(ctx, i, stream)event_recorded,streamwaits on slot eventwait_until_remap_slot_submittedwait_remap_copy_on_stream/enqueue_remap_copy_waitvmm_driver_memory_info()cuMemGetInfofree/total (caching-allocator stats do not covercuMemCreatepages)vmm_enable_trace/vmm_initialize_workers/vmm_set_serial_driver_workersStream handles:
stream.cuda_stream, withmusa_streamfallback.Safety checks on remap submit: unique slots, reserved VA, no in-flight remap, mapped slot must already have a pending release, host tensor pinned / offset 0 / large enough. Adopted mappings are never unmapped by context cleanup.
Debug:
MEGATRON_VMM_REMAP_DEBUG=1prints per-call remap/release stages (cuMemUnmap,cuMemSetAccess, remap wait, H2D launch wait).Files
Checklist:
Suggested coverage:
data_ptr()unchanged across unmap/remap and deferred H2D.release_hooks_after+remap_and_copy_afterrepeated reload (test_repeated_async_reload_releases_context_off_host_callback): no host-callback deadlock, values match, contexterror==0.remap_only_slot_afterdoes not submit H2D;launch_remap_slot_h2ddoes; compute stream waits on the slot event.cuMemGetInfo(not PyTorchmemory_allocated) when claiming physical pages returned.