-
-
Notifications
You must be signed in to change notification settings - Fork 20.5k
[Kernel] Add PDL support for DeepGEMM kernel #42996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
acd0be9
dc16cc6
8d0fbff
ba65085
32c7420
391ed5c
a0c5370
0021ff1
4aa1a89
273eb8b
9043f86
3097386
3d53b4f
156d52e
59eaf97
203e124
6ca6bc4
4456370
a08797a
c6f0812
b05ba8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -287,9 +287,17 @@ __global__ void per_token_group_quant_8bit_packed_register_kernel( | |
| const int sf_k_idx = blockIdx.x * kGroupsPerBlockX + sf_k_local; | ||
| const int mn_idx = blockIdx.y * kRowsPerBlock + row_local; | ||
|
|
||
| #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900)) | ||
| asm volatile("griddepcontrol.wait;"); | ||
| #endif | ||
|
|
||
| if (mn_idx >= tma_aligned_mn) { | ||
| #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900)) | ||
| asm volatile("griddepcontrol.launch_dependents;"); | ||
| #endif | ||
| return; | ||
| } | ||
|
|
||
| const bool is_valid_group = (mn_idx < mn) && (sf_k_idx < groups_per_row); | ||
|
|
||
| // Load 16 input elements (32 B) into registers as two adjacent uint4 | ||
|
|
@@ -392,6 +400,10 @@ __global__ void per_token_group_quant_8bit_packed_register_kernel( | |
| static_cast<int64_t>(mn_idx) * groups_per_row * GROUP_SIZE + | ||
| sf_k_idx * GROUP_SIZE + lane_id * VEC_SIZE; | ||
| *reinterpret_cast<uint4*>(group_output) = packed_out; | ||
|
|
||
| #if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 900)) | ||
| asm volatile("griddepcontrol.launch_dependents;"); | ||
| #endif | ||
| } | ||
|
|
||
| // Public entry point: register-resident packed quant kernel. | ||
|
|
@@ -472,20 +484,29 @@ void per_token_group_quant_8bit_packed(const torch::stable::Tensor& input, | |
|
|
||
| #define LAUNCH_REG_KERNEL_INST(T, DST_DTYPE, KX, RY) \ | ||
| do { \ | ||
| dim3 grid(static_cast<unsigned int>(blocks_x), \ | ||
| static_cast<unsigned int>(blocks_y)); \ | ||
| dim3 block(num_threads); \ | ||
| per_token_group_quant_8bit_packed_register_kernel<T, DST_DTYPE, 128, KX, \ | ||
| RY> \ | ||
| <<<grid, block, 0, stream>>>( \ | ||
| static_cast<const T*>(input.data_ptr()), output_q.data_ptr(), \ | ||
| reinterpret_cast<unsigned int*>(output_s_packed.data_ptr()), \ | ||
| static_cast<int>(padded_groups_per_row), \ | ||
| static_cast<int>(groups_per_row), static_cast<int>(mn), \ | ||
| static_cast<int>(output_q_mn_extent), \ | ||
| static_cast<int>(tma_aligned_mn), num_scale_elems, \ | ||
| static_cast<float>(eps), static_cast<float>(min_8bit), \ | ||
| static_cast<float>(max_8bit)); \ | ||
| cudaLaunchConfig_t config = {}; \ | ||
| config.gridDim = dim3(static_cast<unsigned int>(blocks_x), \ | ||
| static_cast<unsigned int>(blocks_y)); \ | ||
| config.blockDim = dim3(num_threads); \ | ||
| config.dynamicSmemBytes = 0; \ | ||
| config.stream = stream; \ | ||
| cudaLaunchAttribute attrs[1]; \ | ||
| attrs[0].id = cudaLaunchAttributeProgrammaticStreamSerialization; \ | ||
| attrs[0].val.programmaticStreamSerializationAllowed = 1; \ | ||
| config.numAttrs = 1; \ | ||
| config.attrs = attrs; \ | ||
| cudaLaunchKernelEx( \ | ||
| &config, \ | ||
| per_token_group_quant_8bit_packed_register_kernel<T, DST_DTYPE, 128, \ | ||
| KX, RY>, \ | ||
| static_cast<const T*>(input.data_ptr()), output_q.data_ptr(), \ | ||
| reinterpret_cast<unsigned int*>(output_s_packed.data_ptr()), \ | ||
| static_cast<int>(padded_groups_per_row), \ | ||
| static_cast<int>(groups_per_row), static_cast<int>(mn), \ | ||
| static_cast<int>(output_q_mn_extent), \ | ||
| static_cast<int>(tma_aligned_mn), num_scale_elems, \ | ||
| static_cast<float>(eps), static_cast<float>(min_8bit), \ | ||
| static_cast<float>(max_8bit)); \ | ||
|
Comment on lines
+512
to
+534
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } while (0) | ||
|
|
||
| #define LAUNCH_REG_KERNEL(T, DST_DTYPE) \ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -175,6 +175,36 @@ def _import_deep_gemm(): | |
| return None | ||
|
|
||
|
|
||
| def _apply_pdl(enable: bool) -> None: | ||
| """Set PDL on every DeepGEMM module currently importable. | ||
|
|
||
| The external (pip-installed) ``deep_gemm`` and the vendored | ||
| ``vllm.third_party.deep_gemm`` are independent C extensions with | ||
| independent global PDL state. Apply the flag to both so model code | ||
| that imports either module sees the same setting. | ||
| """ | ||
| applied_to: list[str] = [] | ||
| for mod_name in ("deep_gemm", "vllm.third_party.deep_gemm"): | ||
| try: | ||
| mod = importlib.import_module(mod_name) | ||
| except Exception: # noqa: BLE001 | ||
| continue | ||
| set_pdl_fn = getattr(mod, "set_pdl", None) | ||
| if set_pdl_fn is None: | ||
| continue | ||
| try: | ||
| set_pdl_fn(enable) | ||
| applied_to.append(mod_name) | ||
| except Exception as e: # noqa: BLE001 | ||
| logger.warning_once("Failed to set DeepGEMM PDL on %s: %s", mod_name, e) | ||
| if applied_to: | ||
| logger.info_once( | ||
| "DeepGEMM PDL %s on %s.", | ||
| "enabled" if enable else "disabled", | ||
| ", ".join(applied_to), | ||
| ) | ||
|
|
||
|
|
||
| def _lazy_init() -> None: | ||
| """Import deep_gemm and resolve symbols on first use.""" | ||
| global _cublaslt_gemm_nt_impl | ||
|
|
@@ -217,6 +247,18 @@ def _lazy_init() -> None: | |
| if _dg is None: | ||
| return | ||
|
|
||
| # Apply DeepGEMM PDL setting once per process, before any kernel launches | ||
| # or CUDA-graph capture. PDL state is global in DeepGEMM and is read at | ||
| # each kernel launch; flipping it later would diverge from launches | ||
| # captured into CUDA graphs. | ||
| # | ||
| # NOTE: vLLM may load two independent DeepGEMM C extensions in the same | ||
| # process — the pip-installed ``deep_gemm`` and the vendored | ||
| # ``vllm.third_party.deep_gemm`` (e.g. deepseek_v4.py imports the | ||
| # vendored module directly). Each has its own global PDL state, so we | ||
| # apply set_pdl to whichever modules are already importable. | ||
| _apply_pdl(envs.VLLM_USE_DEEP_GEMM_PDL) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we only enable PDL for the imported deepgemm module? Instead of all DeepGEMM modules. Something like Btw, do we even need |
||
|
|
||
| _cublaslt_gemm_nt_impl = getattr(_dg, "cublaslt_gemm_nt", None) | ||
| _fp8_gemm_nt_impl = getattr(_dg, "fp8_gemm_nt", None) | ||
| _fp8_einsum_impl = getattr(_dg, "fp8_einsum", None) | ||
|
|
@@ -243,6 +285,15 @@ def _lazy_init() -> None: | |
| DeepGemmQuantScaleFMT.init_oracle_cache() | ||
|
|
||
|
|
||
| def configure_deep_gemm() -> None: | ||
| """Eagerly initialize DeepGEMM so process-global settings (PDL, JIT | ||
| cache dir) are applied before profile_run / warmup / CUDA graph | ||
| capture. Safe to call when DeepGEMM is unsupported — it becomes a | ||
| no-op. | ||
| """ | ||
| _lazy_init() | ||
|
|
||
|
|
||
| def get_num_sms() -> int: | ||
| _lazy_init() | ||
| dg = _import_deep_gemm() | ||
|
|
@@ -563,6 +614,7 @@ def should_use_deepgemm_for_fp8_linear( | |
|
|
||
| __all__ = [ | ||
| "calc_diff", | ||
| "configure_deep_gemm", | ||
| "DeepGemmQuantScaleFMT", | ||
| "fp8_gemm_nt", | ||
| "fp8_einsum", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The call to
griddepcontrol.launch_dependentswithin this early exit block is problematic becausemn_idxis not uniform across the CTA (it varies withrow_local). If some threads in a block exit early and signal completion while others are still performing quantization and writing to global memory, it creates a race condition where dependent kernels in the stream may start reading incomplete data. Since the grid launch logic ensures that at least one thread in every block is in bounds (blockIdx.y * ry < tma_aligned_mn), the signal at the end of the kernel (line 405) will correctly handle the CTA-level completion signal. The early exit should simply return without signaling.