Repository navigation
migrated DeepGEMM to torch stable ABI (unchecked) - #8
cleonard530 wants to merge 5 commits into
Conversation
| // Build integer indices entirely on-device so layout conversion remains graph-capturable. | ||
| inline torch::stable::Tensor arange(int64_t end, torch::stable::Device device) { | ||
| auto ones = torch::stable::full({end}, 1, torch::headeronly::ScalarType::Long, std::nullopt, device); | ||
| std::array<StableIValue, 3> stack{ | ||
| torch::stable::detail::from(ones), torch::stable::detail::from(int64_t{0}), | ||
| torch::stable::detail::from(std::optional<torch::headeronly::ScalarType>{})}; | ||
| detail::call_dispatcher("aten::cumsum", "", stack.data()); | ||
| const auto indices = torch::stable::detail::to<torch::stable::Tensor>(stack[0]); | ||
| return torch::stable::subtract(indices, ones); | ||
| } |
There was a problem hiding this comment.
This is inefficient, we will need to find a a better way of doing this
b201a73 to
dec2595
Compare
9a86ae2 re-ported the SM90 paged MQA kv_block=32/next_n=4 support from deepseek-ai/DeepGEMM's nv_dev branch onto the 26/09 release base. nv_dev carried four guards for zero context lengths that the 26/09 base never had, and the re-port dropped all of them. With every context length zero, `sum` is zero, so the binary search in `sm90_paged_mqa_logits_metadata` finds no prefix sum greater than `seg_starts` and returns `lo == batch_size`. Using that unclamped as `q_idx` reads `prefix_sum[batch_size]`, which is one past the end of a shared buffer sized to exactly `align(batch_size, 32)` ints. When `batch_size` is already 32-aligned the read is out of range and the kernel traps: CUDBG_EXCEPTION_WARP_OUT_OF_RANGE_ADDRESS (5) #0 deep_gemm::sched::sm90_paged_mqa_logits_metadata<1024u, 256u, 132u, false> vLLM hits this on every DeepSeek V4 startup: the DSA indexer warmup runs a dummy batch of max_num_seqs (default 1024) rows with all context lengths zero. Restore the four guards: - `total == 0` early-returns with one-past-the-end sentinels, avoiding the `prefix_sum[batch_size]` read and keeping a trailing SM off a synthetic zero-KV task. - `q_idx` is clamped to `batch_size - 1` before indexing prefix_sum. - The scheduler constructor checks `exist_q_atom_idx` before `refresh_num_kv_and_advance` dereferences context_lens/indices, and initializes current_advance/current_num_kv/last_advance for the empty range. - `fetch_next_task` skips a zero-context request's atoms rather than exposing an empty task, for the case where traversal crosses one between two non-empty requests. The scheduler is now behaviorally identical to the nv_dev version it was ported from. tests/test_attention.py: `test_paged_mqa_logits` draws context lengths around a positive average and so never produces an empty request, which is why this regressed silently. Add `test_paged_mqa_logits_zero_context` covering all-zero and interleaved-zero context lengths over block_kv 32/64, next_n 1/2/4, and batch sizes 32 and 1024 (both 32-aligned, so `prefix_sum[batch_size]` is exactly one past the end). Signed-off-by: Yongye Zhu <zyy1102000@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
27cc188 to
496fd0b
Compare
99b10e5 to
5fefca5
Compare
| TORCH_LIBRARY(deep_gemm, m) { | ||
| // Register JIT objects | ||
| m.class_<deep_gemm::JitRuntimeHandle>("Runtime"); | ||
| m.def("get_jit() -> __torch__.torch.classes.deep_gemm.Runtime", []() { return c10::make_intrusive<deep_gemm::JitRuntimeHandle>(); }); | ||
| STABLE_TORCH_LIBRARY(deep_gemm, m) { | ||
| m.def("_ensure_jit() -> ()"); | ||
| } | ||
| STABLE_TORCH_LIBRARY_IMPL(deep_gemm, CompositeExplicitAutograd, m) { | ||
| m.impl("_ensure_jit", TORCH_BOX(&deep_gemm::ensure_jit)); |
There was a problem hiding this comment.
JitRuntimeHandle was removed because the PyTorch stable ABI does not support registering custom C++ classes. The Python Runtime wrapper preserves the existing get_jit() API while _ensure_jit() still initializes DeepJIT; no runtime methods are lost because DeepJIT’s Python binding did not expose any.
| class Runtime: | ||
| """Opaque compatibility type formerly returned by the pybind custom class.""" | ||
|
|
||
|
|
||
| def get_jit() -> Runtime: | ||
| """Initialize and return the opaque runtime handle kept by the legacy API.""" | ||
| _torch_ops._ensure_jit() | ||
| return Runtime() |
There was a problem hiding this comment.
The wrapper to preserve the existing get_jit() API.
The Python Runtime placeholder preserves the legacy API’s surface semantics, but not the original Torch custom-class type identity. It is intentionally empty because DeepJIT exposed no runtime methods through Python.
9560d13 to
45d9a65
Compare
5fefca5 to
f07c95c
Compare
| values = _torch_ops._get_bf16_mega_gate_config(num_tokens, hidden, num_routed_experts, num_topk) | ||
| return dict(zip(('block_tokens', 'num_mma_ctas', 'num_split_k', | ||
| 'num_expert_groups', 'num_gate_warpgroups', 'num_sms'), values)) |
There was a problem hiding this comment.
STABLE_TORCH_LIBRARY cannot register a dict, so a list was registered instead and this wraps the dict around the list values.
|
|
||
| // Create the cuBLASLt handle ourselves | ||
| cublasLtHandle_t cublaslt_handle; | ||
| bool use_pytorch_managed_cublaslt_handle; |
There was a problem hiding this comment.
PyTorch does not have a stable ABI equivalent of at::cuda::getCurrentCUDABlasLtHandle() so we remove this option. I have not been able to find any test that use this and by default it is not used, so I don't know of any major impact, but this should be looked at closely.
| static std::unordered_map<c10::cuda::CUDAStream, torch::Tensor> workspaces; | ||
| const auto stream = torch_compat::stream_key(reference); | ||
|
|
||
| static std::map<torch_compat::StreamKey, torch::stable::Tensor> workspaces; |
There was a problem hiding this comment.
std::pair<int32_t, cudaStream_t> (StreamKey) does not have hashing support.
| DG_HOST_ASSERT(not disable_ue8m0_cast); | ||
| const auto broadcasted = gran_mn == 1 ? sf : | ||
| sf.index_select(-2, torch::arange(mn, at::TensorOptions().device(sf.device())).floor_divide_(gran_mn)); | ||
| torch_compat::index_select(sf, -2, torch_compat::floor_divide(deep_gemm_arange_int64(mn, sf.device()), gran_mn)); |
There was a problem hiding this comment.
There is no stable shim for torch arange, so we implemented a deep_gemm kernel for this. This now uses DeepJIT for compilation on the first use.
| static c10::Dict<std::string, int64_t> get_bf16_mega_gate_config(const int& num_tokens, const int& hidden, | ||
| static std::vector<int64_t> get_bf16_mega_gate_config(const int& num_tokens, const int& hidden, |
There was a problem hiding this comment.
c10::Dict<std::string, int64_t> cannot be used in a stable operator schema. The stable ABI supports int[], so the C++ implementation returns std::vector<int64_t>. The Python wrapper converts that vector back into the public dictionary
| TORCH_LIBRARY_IMPL(deep_gemm, CatchAll, m) { | ||
| m.impl("get_bf16_mega_gate_config", TORCH_FN(deep_gemm::torch_registration::get_bf16_mega_gate_config)); | ||
| STABLE_TORCH_LIBRARY_IMPL(deep_gemm, CompositeExplicitAutograd, m) { | ||
| m.impl("_get_bf16_mega_gate_config", TORCH_BOX(&deep_gemm::torch_registration::get_bf16_mega_gate_config)); | ||
| } |
There was a problem hiding this comment.
CompositeExplicitAutograd behaves the same as Catchall for get_bf16_mega_gate_config, which has no tensor arguments.
Also, Added underscore (_) to torch ops registration name to indicate difference in return type between it and python wrapper (List vs. dict)
| DG_HOST_ASSERT(c10::cuda::currentStreamCaptureStatusMayInitCtx() == c10::cuda::CaptureStatus::None); | ||
| workspace = torch::zeros({kNumWorkspaceBytes}, options.dtype(torch::kByte)); | ||
| DG_HOST_ASSERT(not torch_compat::is_capturing(stream.second)); | ||
| workspace = torch::stable::new_zeros(reference, {kNumWorkspaceBytes}, torch::headeronly::ScalarType::Byte); | ||
| } |
There was a problem hiding this comment.
The new not torch_compat::is_capturing(stream.second) check is more explicit and stable-ABI compatible: it checks capture status on the exact CUDA stream associated with the reference tensor’s device. The previous c10::cuda::currentStreamCaptureStatusMayInitCtx() == c10::cuda::CaptureStatus::None call used an unstable PyTorch API and queried PyTorch’s current stream instead.
f07c95c to
57a5da2
Compare
Co-authored-by: codex Signed-off-by: Chris Leonard <chleonar@redhat.com>
Co-authored-by: codex Signed-off-by: Chris Leonard <chleonar@redhat.com>
…table arange method. Fixed merge issues. removed unnecessaary torch_compat::from_blob and megred 3 namespace blocks into one (they were probbaly disjoint before due to Agent iteration) Co-authored-by: codex Signed-off-by: Chris Leonard <chleonar@redhat.com>
…ion. Co-authored-by: codex Signed-off-by: Chris Leonard <chleonar@redhat.com>
57a5da2 to
4bba562
Compare
…M100, and SM120 heuristics to avoid repeated stable ABI lookups. Co-authored-by: codex Signed-off-by: Chris Leonard <chleonar@redhat.com>
migration to stable abi (new with deep_jit)