onnxruntime
e9157780 - Add FP8 GEMV KSplit32 scheduling for the 48-SM SM121 GPU (#32409)

Commit
4 days ago
Add FP8 GEMV KSplit32 scheduling for the 48-SM SM121 GPU (#32409) Add a KSplit32 tensor-core GEMV specialization for `MatMulBlockQuantizedFp8Weight` and select it only for qualified low-M decode shapes on the 48-SM SM121 GB10 GPU. The final selector preserves the generic schedule unless all of these conditions hold: - compute capability is exactly 12.1; - the device has exactly 48 SMs; - the original request has M at most 8; - the shape is either: - at least 1,024 output blocks and 80 64-element K windows; or - at least 320 output blocks and 128 K windows. Each FP8 GEMV output block owns 16 columns, so the thresholds use `ceil(N / 16)` rather than raw N. This gives shapes with identical launch geometry the same SM121 override: the wide boundary starts at N=16369, and the long-reduction boundary starts at N=5105. The generic selector is unchanged, preserving behavior on other devices. SM120, other SM12x minors, other SM counts, M above 8, attention-sized projections, small outputs, short reductions, and shapes immediately below both output-block boundaries retain the generic selector. KSplit32 dispatches directly to the only qualified launch, `<KSplit=32, MTiles=1>`. This avoids instantiating unreachable `<32,2>` and `<32,4>` kernels; `<32,4>` would require 64 KiB of static shared memory. Requests above M=8 are recursively tiled with GB10 KSplit32 tuning disabled, so the original request size cannot be lost during dispatch. ## Kernel performance Measured on RTX Spark (SM121, 48 SMs) as end-to-end ORT `Run()` latency with an explicit shared CUDA stream. Each final-selector cell used three alternating process rounds, with 100 warmups and 1,000 measured calls per arm per round. | Shape | M | Generic | KSplit32 | Latency reduction | |---|---:|---:|---:|---:| | 17408x5120 gate/up | 1 | 0.3674 ms | 0.3611 ms | 1.7% | | 17408x5120 gate/up | 8 | 0.4901 ms | 0.4792 ms | 2.3% | | 5120x17408 down | 1 | 0.3771 ms | 0.3684 ms | 2.3% | | 5120x17408 down | 8 | 0.3771 ms | 0.3696 ms | 2.0% | | 32768x5120 wide output | 1 | 0.6704 ms | 0.6552 ms | 2.2% | | 32768x5120 wide output | 8 | 0.7986 ms | 0.7729 ms | 3.2% | The review specifically raised the possibility that KSplit32 could regress already-saturated, vocabulary-sized output grids. Additional three-round GB10 negative-control measurements instead showed that KSplit32 remained beneficial as N increased through the Qwen 3.8 lm-head dimension: | Shape (M=1, K=5120) | Output blocks | Output waves on 48 SMs | KSplit8 | KSplit32 | Latency reduction | |---|---:|---:|---:|---:|---:| | N=32769 | 2,049 | 42.7 | 0.6836 ms | 0.6559 ms | 3.95% | | N=65536 | 4,096 | 85.3 | 1.3174 ms | 1.2693 ms | 3.63% | | N=131072 | 8,192 | 170.7 | 2.6234 ms | 2.5102 ms | 4.32% | | N=248320 lm head | 15,520 | 323.3 | 5.2056 ms | 4.8773 ms | 6.54% | Every cell used three alternating process rounds, 100 warmups, and 1,000 CUDA-event-timed ORT calls per arm. Full outputs matched with `atol=0.5` and `rtol=1e-3`. A final-selector smoke run confirmed that the natural selector matched forced KSplit32 at N=248320 (4.8339 ms natural versus 4.8683 ms forced). Because the proposed regression did not reproduce and the benefit persisted through more than 323 output waves, no arbitrary upper N cap is applied. Exact boundary cases were also positive across M=1, 2, 4, and 8: | Boundary | Latency reduction | |---|---:| | 1,024 output blocks (measured at `N=16384`), 80 K windows | 1.2% to 2.5% | | 320 output blocks (measured at `N=5120`), 128 K windows | 3.2% to 5.9% | The broad initial heuristic was rejected after finding regressions at M=16/32, on attention and small-output shapes, at 32 K windows, and around N=4096. The final selector excludes all of those regimes. Negative controls at 1,023 output blocks (`N=16368`), 79 windows, 319 output blocks (`N=5104`), and 127 windows retain the generic schedule. No-override runs followed the expected schedule for every selected and excluded case. Randomized activations and varying block scales matched the generic output in every compared full tensor. ## Whole-model performance The exact final CUDA plugin was compared with GB10 tuning disabled versus enabled. Each value is the median paired gain from three alternating process pairs. The code-copy workload keeps generated tokens, speculative acceptance, tokens per target forward, and target-forward counts identical between arms. | Qwen 3.8 27B DFlash2 workload | Decode improvement | |---|---:| | FP16 KV, batch 1, 4K | 1.19% | | FP16 KV, batch 1, 16K | 1.03% | | INT8 KV, batch 1, 4K | 1.73% | | INT8 KV, batch 1, 16K | 1.05% | | INT8 KV, batch 4, 4K | 0.16% | Batch 4 is effectively neutral, with paired changes from -0.17% to +0.41%, while every batch-1 pair improved. Median TTFT did not regress in these matched-acceptance comparisons. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Author
Parents
Loading