Migrate DeepGEMM off of pybind11 and on to TORCH_LIBRARY - #15
Draft
cleonard530 wants to merge 10 commits into
Draft
cleonard530 wants to merge 10 commits into
cleonard530 wants to merge 10 commits into
Conversation
cleonard530
force-pushed
the
migrate_pybind_to_torch_library_dev
branch
from
September 24, 2026 12:56
b201a73 to
dec2595
Compare
… branch. Will need to figure out how to handle DeepJIT. Signed-off-by: Chris Leonard <chleonar@redhat.com>
…nd for GIL release. We will need to come back and address this later Signed-off-by: Chris Leonard <chleonar@redhat.com>
…eplacing _C with _C_extension Signed-off-by: Chris Leonard <chleonar@redhat.com>
…hon_api.cpp like it was before, added some checks to mega_moe.hpp to keep the new registration inline with the pybind registration. Signed-off-by: Chris Leonard <chleonar@redhat.com>
Signed-off-by: Chris Leonard <chleonar@redhat.com>
Signed-off-by: Chris Leonard <chleonar@redhat.com>
…hpp to disable to gil.hpp in DeepJIT, this is just temporary and will need to be changed upstream Signed-off-by: Chris Leonard <chleonar@redhat.com>
…of doing it itslef (requires changes to DeepJIT) Signed-off-by: Chris Leonard <chleonar@redhat.com>
cleonard530
force-pushed
the
migrate_pybind_to_torch_library_dev
branch
from
September 24, 2026 12:59
dec2595 to
66d8928
Compare
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
Migrates DeepGEMM's C++ extension from pybind11 (
PYBIND11_MODULE+ per-headerregister_apis) toTORCH_LIBRARY/TORCH_LIBRARY_IMPLregistration. Ops are now registered astorch.ops.deep_gemm.*.To preserve the existing Python API (
import deep_gemm; deep_gemm._C.fp8_gemm_nt(...)), a newdeep_gemm/_C.pyshim loads the extension and re-exports the legacy surface. The compiled module is renamed todeep_gemm._C_extension.Updates
.pyistub generation to readTORCH_LIBRARYschemas fromcsrc/and overlay defaults/types from_C.py.JitRuntimeHandleincsrc/python_api.cpppreserves the existing_C.get_jit()API throughTORCH_LIBRARY. It wraps the existing DeepJIT runtime’s shared pointer in atorch::CustomClassHolder, allowing the dispatcher to return it as a registered custom class. It does not create a new runtime or change kernel execution.ABI3 note: This migration does not yet enable the stable CPython ABI (abi3) because DeepJIT still uses pybind11 to release the GIL. Since calls through torch.ops already release the GIL, DeepJIT’s GIL handling can be disabled for this integration, removing the remaining pybind11 dependency and allowing abi3 packaging to be restored.working around the non ABI3 stable CPython was becomming difficult upstream on vllm, so I updated the build to use CPython stable abi. This added a preprocessor macro defined
DJ_DISABLE_GILwhich is used on my DeepJIT branch deepseek-ai/DeepJIT#11 to not pull in pybind11 and disables the gil release.WARNING:
Before,
get_symm_buffer_size_for_mega_moereturned anintand afunction(std::tuple<int64_t, std::function...>) butTORCH_LIBRARYdoesn't allow returning function objects/closures. Now, it returns anintand the computed buffer layout as anint[](std::tuple<int64_t, c10::List<int64_t>>), and a separate function,_slice_symm_buffer_for_mega_moe, consumes thatint[]instead of the closure. The_C.pywrapper reconstructs the callback, preserving the existingget_symm_buffer_size_for_mega_moeAPI and backwards compatibility.New files
deep_gemm/_C.py
Python compatibility shim for the old
deep_gemm._Cmodule. Responsibilities:torch.ops.load_library(...)on_C_extension*.soat import timeRe-export
torch.ops.deep_gemm.*under the legacy_CnamesRestore pybind-era conveniences that are not expressible in
TORCH_LIBRARYschemas alone: tuple unpacking (q/kv/(weight, scale)pairs), default arguments.Previously
_Cwas the compiled.soitself; now_Cis pure Python and the.sois_C_extension.csrc/torch_library_utils.hpp
(namespace
deep_gemm::torch_utils) Conversion helpers for bridging PyTorch schema types to the C++ types the existing kernel implementations expect — e.g.c10::List<int64_t>→std::tuple<int,int,int>forrecipe/head_splits, and optional list →std::vector<int>. Without this, everyTORCH_LIBRARYwrapper would duplicate the same list/tuple parsing logic.csrc/utils/registration.h
Defines the REGISTER_EXTENSION(NAME) macro used by csrc/python_api.cpp. Under TORCH_LIBRARY, ops self-register via static initializers when the .so loads, so there's no real pybind11 module to build — this macro just emits an empty PyInit_ function so the compiled .so still satisfies Python's import machinery (which requires that symbol to exist).
Significantly changed files
csrc/apis/mega_moe.hpp
build_symm_buffer_layout, which centralizes the layout calculation and returns the offsets and dimensions needed to construct every symmetric-buffer view.get_symm_buffer_size_for_mega_moeserializes this layout throughto_int_list, while the Mega MoE kernel entry points deserialize it and slice the buffer through the shared C++ helper without dispatching through another registered operator. This preserves the original centralized layout calculation, including thenum_sms-dependent ring sizing.get_symm_buffer_size_for_mega_moereturned anint64_tand a C++ closure. BecauseTORCH_LIBRARYschemas cannot return closures, the registered operator now returns the byte count and serialized layout as anint[]._slice_symm_buffer_for_mega_moe(Tensor buffer, int[] layout_info)reconstructs the views. The compatibility wrapper in_C.pycaptures that layout in a Python closure, preserving the original public API. The slicing operator remains an internal implementation detail and is excluded from_C.__all__.csrc/python_api.cpp
Replaces the old
PYBIND11_MODULE(...)entry point. Each API header declares its schemas withTORCH_LIBRARY_FRAGMENTand registers implementations withTORCH_LIBRARY_IMPL; including those headers in this translation unit instantiates their static registrations when the shared library loads. This file also owns the primaryTORCH_LIBRARY(deep_gemm, m)block, which registers theRuntimecustom class andget_jit. Finally,REGISTER_EXTENSION(_C_extension)defines the minimalPyInit__C_extensionfunction needed to import the shared library as a Python extension. The Python C API is used only to create that empty module; operators and classes are registered through PyTorch’s library API.scripts/generate_pyi.py
Previously generated
.pyitype hints by regex-parsing the C++ function declarations incsrc/— but under pybind, the real Python-callable keyword names come frompy::arg("...")strings inPYBIND11_MODULE, which pybind doesn't require to match the C++ parameter names. That divergence caused real bugs: e.g.fp8_fp4_paged_mqa_logits's C++ parameter is namedfused_kv_cache, but it's bound aspy::arg("kv_cache"), so the old generated stub advertised a keyword (fused_kv_cache) that would actually raiseTypeErrorif used — the true kwarg iskv_cache. The script now parses TORCH_LIBRARY schema strings fromcsrc/and overlays defaults from an AST parse ofdeep_gemm/_C.py, so the stub is generated from the real registration contract and entry point instead of an incidental C++ signature.Test plan/Results
All test pass
Test that require Blackwell GPUs have not been tested yet. These include
and a few test in the other files.
Migration progress using the Audit Python extension torch-abi-audit:
This shows that the library is now on the CPython Stable ABI (ABI3)
Performance benchmarks can be found here cleonard530#9. Overall, the performance is on par with the pybind, but take a look at the 3rd case, to see one case where performance could degrade. This is a pathological case that is unlikely to happen in practice, but should be considered.
This PR assumes DeepJIT is compatable with
abi3. This is not true on its main branch, so either deepseek-ai/DeepJIT#11 needs to go through first, or minor modifications need to be made here (which would move the build away fromabi3and target specific CPython versions)