mirror of
https://github.com/deepseek-ai/DeepGEMM.git
synced 2026-11-05 00:47:26 +00:00
[PR #393] Migrate pybind11 to TORCH_LIBRARY #411
Labels
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
deepseek-ai/DeepGEMM#411
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/deepseek-ai/DeepGEMM/pull/393
Author: @cleonard530
Created: 7/27/2026
Status: 🔄 Open
Base:
nv_dev← Head:migrate_pybind_to_torch_library📝 Commits (10+)
a3e7b0emigrated off of pybind and onto TORCH_LIBRARYc91c209added #if DG..._COMPATABLE guards to the function schema registers to match what was in the legacy code926d993updated init.py import to align with legacy code. Removed pybind helper code from generate_pyi.pyc38efbbgenerate_pyi.py stopped creating the function type hints in the past updates so I updated it to inlcude the type hints2630e0aupdated generate_pyi.py to align more with legacy code796a48eused _C.py to generate default argumements in gererate_pyi.py since pybind default arguments are no longer there0749569reverted back to using TORCH_LIBRARY schema to generate pyi filefd57d1cmade some updates to the generate_pyi.py file to remove aliases and other minor formatting issues. Also added comments with examples to help describe each step. These will be removed (as well as main()) in a followup commit, but wanted them here for reference2a1da8bremoved verbose comments and main function from generate_pyi.py4aff592added c_py_path for generate_pyi_file in setup.py📊 Changes
40 files changed (+2255 additions, -1183 deletions)
View changed files
📝
.github/workflows/publish.yml(+3 -6)📝
csrc/apis/attention.hpp(+125 -34)📝
csrc/apis/einsum.hpp(+41 -12)📝
csrc/apis/gemm.hpp(+304 -118)📝
csrc/apis/hyperconnection.hpp(+28 -5)📝
csrc/apis/layout.hpp(+98 -26)📝
csrc/apis/mega.hpp(+400 -96)📝
csrc/apis/runtime.hpp(+59 -36)📝
csrc/apis/sm90_mega.hpp(+215 -50)📝
csrc/jit/device_runtime.hpp(+2 -0)📝
csrc/jit_kernels/impls/runtime_utils.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm100_bf16_gemm.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm100_bf16_mega_moe.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm100_bmk_bnk_mn.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm100_fp4_fp4_mega_moe.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm100_fp8_fp4_gemm_1d1d.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm100_fp8_fp4_mega_moe.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm100_tf32_hc_prenorm_gemm.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm120_bf16_gemm.hpp(+1 -1)📝
csrc/jit_kernels/impls/sm120_bmk_bnk_mn.hpp(+1 -1)...and 20 more files
📄 Description
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_extensionand built as a limited-API (abi3) extension.Also updates
.pyistub generation to readTORCH_LIBRARYschemas fromcsrc/and overlay defaults/types from_C.py, and splits mega MoE symm-buffer sizing/slicing into two separate ops (replacing the old pybind callback return value).Note: Bumped the abi3 floor to Python 3.10 which is supported by every torch release already in the build matrix (2.4–2.8), and it gets ahead of a floor bump we'll need once DeepGEMM moves to the LibTorch Stable ABI, which requires torch 2.10+ and Python 3.10+.
PR https://github.com/vllm-project/vllm/pull/48962 is used to test this on vLLM.
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. This breaks backwards compatibility forget_symm_buffer_size_for_mega_moe, but this method is not re-exported fromdeep_gemm/__init__.py(only used internally bySymmBuffer.__init__), and a quick look on GitHub shows no other public repo calls this function directly either.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, and guarded op binding for#if DG_*_COMPATIBLEbuild variantsPreviously
_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.hpp
build_symm_buffer_layout, which computes the fullSymmBufferLayoutInfo(every sub-buffer's offset/size) once.get_symm_buffer_size_for_mega_moeandfp8_fp4_mega_moe/bf16_mega_moecall it instead of duplicating layout math — the kernel entry points build the layout and slice the buffer directly rather than going through a second registered op.SymmBufferLayoutInfoalso gainedto_int_list/from_int_listso the layout computed byget_symm_buffer_size_for_mega_moecan be flattened into a plainint[]and handed to_slice_symm_buffer_for_mega_moe, instead of that op recomputing the same layout (including thenum_sms-dependent ring sizing) from scratch.get_symm_buffer_size_for_mega_moeused to return (int64_t,std::function<...>) under pybind;TORCH_LIBRARYcan't return closures. It now returns the int plus the computed layout as anint[], and slicing moved to a separate op,_slice_symm_buffer_for_mega_moe(Tensor buffer, int[] layout_info)(leading underscore since it's an internal implementation detail — onlydeep_gemm/mega/__init__.py'sSymmBuffercalls it, it isn't part of the public API).csrc/python_api.cpp
Replaces the old
PYBIND11_MODULE(...)entry point. Ops now self-register viaTORCH_LIBRARY_FRAGMENT/STABLE_TORCH_LIBRARY_IMPLstatic initializers inside eachcsrc/apis/*.hpp, which run automatically when the.sois loaded — so this file no longer needs to call per-headerregister_apis(m)functions. It just#includeseveryapis/*.hpp(to trigger their static registration) and defines an emptyPyInit_symbol via theREGISTER_EXTENSIONmacro, purely so the compiled.sois still a valid, importable Python extension module — no actual bindings go through the Python C API anymore.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
smoke_test_all_ops.py, that performs a minimal call to every op and verifies that any tensor inputs expected to be immutable remain bit-for-bit identical before and after the call.Checkout commit
github.com/deepseek-ai/DeepGEMM@a813b41a1bto see thesmoke_test_all_ops.pyscript that exercises every TORCH_LIBRARY op. I added the results in the comment at the bottom of the commit (they all passed or were skipped because I didn't have the right SM).I also compared the generated .pyi file before (using C++ signatures directly) and after (using TORCH_LIBRARY schemas) to make sure types are mapped appropriately. The differences can be found here https://github.com/cleonard530/DeepGEMM/pull/5 .
All test pass.
Test that require Blackwell GPUs have not been tested yet. These include
and a few test in
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.