Repository navigation
[CUDA/CPU] Add AVX2 INT4 cold-expert kernel and zero-cold doorbell short-circuit for QMoE offload - #33624
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The tests need reliable AVX2 selection and direct coverage of vectorized partial-range writes.
2 open issues
What changed in this PR
This PR extends QMoE CPU/GPU offload with faster INT4 CPU execution on AVX2 hosts and an opt-in shortcut for decode calls whose selected experts are all GPU-resident.
Changes:
- Add AVX2 INT4 kernels, FP16/BF16 conversions, and runtime capability detection.
- Add configurable zero-cold doorbell bypass, sequence retirement, and telemetry.
- Expand numerical and decode regression tests and correct the INT3 SwiGLU fixture.
| File | Description |
|---|---|
onnxruntime/test/python/transformers/test_qmoe_shared_layout.py |
Add scalar/SIMD INT4 parity coverage. |
onnxruntime/test/python/test_qmoe_offload_v4.py |
Test bypass parity, replay, transitions, and policy handling. |
onnxruntime/test/contrib_ops/qmoe_shared_layout_test.cc |
Expand conversion, range, and expert parity tests. |
onnxruntime/test/contrib_ops/moe_test.cc |
Correct the INT3 fixture for SwiGLU. |
onnxruntime/core/framework/moe_offload_plan.h |
Parse and validate the bypass policy. |
onnxruntime/contrib_ops/cuda/moe/qmoe_offload.h |
Add telemetry configuration state. |
onnxruntime/contrib_ops/cuda/moe/qmoe_offload.cc |
Integrate bypass handling, sequence retirement, and telemetry. |
onnxruntime/contrib_ops/cuda/moe/qmoe_kernels.h |
Declare telemetry and extended classification parameters. |
onnxruntime/contrib_ops/cuda/moe/qmoe_kernels.cu |
Complete zero-cold requests without host payload staging. |
onnxruntime/contrib_ops/cpu/moe/qmoe_shared_layout.h |
Expose AVX2 capability queries. |
onnxruntime/contrib_ops/cpu/moe/qmoe_shared_layout.cc |
Implement AVX2 kernels, conversions, and dispatch. |
include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h |
Declare the bypass session option. |
docs/contrib_ops/cuda/moe_qmoe.md |
Document the option and its applicability. |
Review effort: Balanced
8388bec to
3cf5336
Compare
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
Thanks for the PR and for the detailed benchmark. The AVX2 cold-expert path is a clear win for AVX2-only hosts (25% residency: 54.46 to 22.01 ms/token), and I did not find a blocking correctness issue in the CPU or CUDA changes. Verdict: COMMENT (draft, stacked on #33575). The main open item is justifying the opt-in zero-cold bypass with repeatable measurements.
What I checked at 3cf5336 (AMD EPYC 7V12, Zen 2: AVX2+FMA+F16C, no AVX-512)
- AVX2/F16C float to half, half to float and float to bf16 converters vs the scalar ones on all 2^32 float bit patterns and all 65,536 half values: bit-exact for every non-NaN input (NaN payloads differ, see the inline comment on the test).
Avx2Int4GemmRangevs scalar: randomized fuzz (prepacked/raw weights, block-major/row-major scales, fp16/bf16 scales and bias, partial ranges, strides), about 19M outputs under ASAN+UBSAN: no out-of-bounds access or padding clobber, max |diff| 7.6e-6 (summation order only).ReorderedPositiontable vs the old loop for k in [-200, 200): identical.python3 -m unittest test_qmoe_shared_layouton this AVX2-only host: 21 run, 5 skipped (AVX-512-only), OK.- The CUDA classify/bypass/doorbell changes were reviewed by reading only; I did not run the GPU tests or the C++ gtests.
Suggestions
- Benchmark: the bypass shows no measurable latency gain (details below).
qmoe_kernels.cu: telemetry read-modify-writes on pinned host memory sit in front of thedone/reqstores (inline).qmoe_shared_layout.cc:getenvon everyCpuHasAvx512()call (inline).moe_qmoe.md: the AVX2 fallback and the new env vars are undocumented, and AVX-512-only wording is stale (inline).qmoe_shared_layout_test.cc: the fp16 converter test covers 10 of 18 patterns (inline).
Nitpicks / questions
6. qmoe_offload.cc: the CPU-side zero-cold shortcut in ServiceDoorbell looks unreachable (inline).
7. moe_offload_plan.h: getenv is called twice (inline).
8. PR description accuracy (see the last section).
Benchmark (skip=0 vs skip=1, ms/token, from the PR description)
| Residency | skip=0 | skip=1 | diff | Doorbell wakes/token with skip=1 |
|---|---|---|---|---|
| 25% | 22.01 | 22.07 | +0.06 | 40.23 / 48 |
| 50% | 10.64 | 10.67 | +0.03 | 11.35 / 48 (-76%) |
| 75% | 8.76 | 9.04 | +0.28 | 1.02 / 48 (-98%) |
| 100% | 7.65 | 7.55 | -0.10 | 0 (no doorbell traffic) |
- Wakeups drop a lot, but latency does not move. The 100% row runs the same path in both columns, so its 0.10 ms (1.3%) gap is a rough single-sample noise floor: the 25% and 50% diffs are inside it, and the 75% diff (+0.28 ms) is larger than it, in the wrong direction.
- The 75% skip=1 cell (9.04 ms, 110.6 tok/s) is identical to the #33575 baseline cell (9.04 ms), so it may be a copy slip.
- A plausible reason for no gain: the CPU doorbell service already overlaps the GPU resident GEMVs, so a zero-cold wake is hidden. I have not verified that.
- Please add repeated runs (mean +/- stdev) and a telemetry-off A/B (see the inline comment on
qmoe_kernels.cu), or reframe the bypass as a PCIe / CPU-wakeup reduction (for example under CPU contention) if latency is not the goal. With the extra GPU to host handshake path and theRetirePendingaccounting, the justification matters.
PR description accuracy
- The speedup column uses skip=0 for the 25-75% rows (2.47x = 54.46 / 22.01) but skip=1 for the 100% row (8.31 / 7.55 = 1.10x; with skip=0 it is 1.09x).
- 'Exact' optimizations: the AVX2 path is not bit-identical to scalar (different FP32 summation order; the table shows a min logit cosine of 0.99956 at 25% residency), so 'numerically equivalent to about 1e-5' is more accurate. The bypass itself is bitwise identical per the table.
onnxruntime/core/framework/session_state.ccis listed as modified but is not in the diff.
The two open bot-review threads on qmoe_shared_layout_test.cc (AVX2 vector interior not exercised; AVX2-named tests may run AVX-512 code) look addressed at this head, so I did not repost them.
…urce doorbell bypass, cached AVX-512 env check, and test/doc updates - Move QMoEOffloadTelemetry from pinned host memory to device scratch memory and copy back once in ~QMoEOffload() to avoid PCIe read-modify-write round trips on the doorbell path. - Remove unreachable CPU-side zero-cold shortcut in ServiceDoorbell() and cold_count_host so QMoEOffloadClassifyKernel is the single source of truth. - Cache ORT_QMOE_CPU_DISABLE_AVX512 once inside CpuHasAvx512() static initializer and add qmoe_shared_set_disable_avx512_for_testing C ABI helper. - Read ORT_QMOE_SKIP_ZERO_COLD_DOORBELL once into a local variable in MoeOffloadSessionPolicy::FromConfig. - Expand Avx2BFloat16AndHalfConversionMatchScalarIncludingNaNs to cover all 36 FP16 patterns and all 65,536 half bit patterns. - Update docs/contrib_ops/cuda/moe_qmoe.md for AVX-512 -> AVX2+FMA+F16C -> scalar dispatch and new environment variables.
Thanks for the review and fuzzing tests Tianlei Wu (@tianleiwu)! Addressed all 6 inline comments in e7e20f7 and updated the PR description with 5-trial numbers (mean ± stdev) with ORT_QMOE_OFFLOAD_TELEMETRY=0. With telemetry off on an idle GPU, single-stream latency for skip=0 and skip=1 is within noise (10.80 ± 0.10 ms vs 10.66 ± 0.23 ms at 50% residency), while skip=1 still cuts CPU doorbell wakeups by 76.4% at 50% residency and 97.9% at 75% residency. |
05605d2 to
a5af696
Compare
|
Hi BBaditi — we just updated the base QMoE offload PR stack (
When you rebase this branch onto the updated |
60d8510 to
f8e4146
Compare
e7e20f7 to
67fccef
Compare
…hed AVX-512 env check, and test/doc updates - Remove unreachable CPU-side zero-cold shortcut in ServiceDoorbell() and cold_count_host so QMoEOffloadClassifyKernel is the single source of truth. - Cache ORT_QMOE_CPU_DISABLE_AVX512 once inside CpuHasAvx512() static initializer and add qmoe_shared_set_disable_avx512_for_testing C ABI helper. - Read ORT_QMOE_SKIP_ZERO_COLD_DOORBELL once into a local variable in MoeOffloadSessionPolicy::ParseFromConfigOptions. - Expand Avx2PartialInt4RangesPreserveOtherColumns and Avx2BFloat16AndHalfConversionMatchScalarIncludingNaNs to cover AVX2 interior ranges, all 36 FP16 patterns, and all 65,536 half bit patterns. - Update docs/contrib_ops/cuda/moe_qmoe.md and docs/moe-cpu-offload/qmoe_offload.md for AVX-512 -> AVX2+FMA+F16C -> scalar dispatch and new environment variables. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
a682b56 to
cc21d19
Compare
…hed AVX-512 env check, and test/doc updates - Remove unreachable CPU-side zero-cold shortcut in ServiceDoorbell() and cold_count_host so QMoEOffloadClassifyKernel is the single source of truth. - Cache ORT_QMOE_CPU_DISABLE_AVX512 once inside CpuHasAvx512() static initializer and add qmoe_shared_set_disable_avx512_for_testing C ABI helper. - Read ORT_QMOE_SKIP_ZERO_COLD_DOORBELL once into a local variable in MoeOffloadSessionPolicy::ParseFromConfigOptions. - Expand Avx2PartialInt4RangesPreserveOtherColumns and Avx2BFloat16AndHalfConversionMatchScalarIncludingNaNs to cover AVX2 interior ranges, all 36 FP16 patterns, and all 65,536 half bit patterns. - Update docs/contrib_ops/cuda/moe_qmoe.md and docs/moe-cpu-offload/qmoe_offload.md for AVX-512 -> AVX2+FMA+F16C -> scalar dispatch and new environment variables. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
67fccef to
2e4ffe0
Compare
Rebased onto tlwu/20261026/qmoe-offload-avx2-int3-s8-opt (#33629) and scoped this branch down to the remaining ORT_QMOE_CPU_DISABLE_AVX512 test toggle, single-source skip_zero_cold_doorbell cleanup, and unit test/doc updates. |
cc21d19 to
389f012
Compare
|
Thanks for the AVX2 path. The kernel work looks solid, but I have two questions about the offload change, and a few things in the tests and dispatch I'd like to tighten before this goes in. Correctness
Performance and design
Tests
Items 1-4 are the ones I'd like resolved before approving. 5-9 are suggestions. |
d1f2adb to
8fa3930
Compare
4e06c6b to
a4ebaa9
Compare
…hed AVX-512 env check, and test/doc updates - Remove unreachable CPU-side zero-cold shortcut in ServiceDoorbell() and cold_count_host so QMoEOffloadClassifyKernel is the single source of truth. - Cache ORT_QMOE_CPU_DISABLE_AVX512 once inside CpuHasAvx512() static initializer and add qmoe_shared_set_disable_avx512_for_testing C ABI helper. - Read ORT_QMOE_SKIP_ZERO_COLD_DOORBELL once into a local variable in MoeOffloadSessionPolicy::ParseFromConfigOptions. - Expand Avx2PartialInt4RangesPreserveOtherColumns and Avx2BFloat16AndHalfConversionMatchScalarIncludingNaNs to cover AVX2 interior ranges, all 36 FP16 patterns, and all 65,536 half bit patterns. - Update docs/contrib_ops/cuda/moe_qmoe.md and docs/moe-cpu-offload/qmoe_offload.md for AVX-512 -> AVX2+FMA+F16C -> scalar dispatch and new environment variables. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2e4ffe0 to
71ca154
Compare
a4ebaa9 to
88ff5bb
Compare
Thanks mirounga, I pushed a fix for all of these. In qmoe_offload.cc, DoorbellService and ServiceDoorbell now check doorbell_done_host >= req directly without re-checking skip_zero_cold_doorbell on the host, and call invocation_rec->MarkCpuDone() when retiring zero-cold requests. In qmoe_shared_layout.cc and moe_qmoe.md, CpuHasAvx512 now caches ORT_QMOE_CPU_DISABLE_AVX512 and CPUID on the first call before checking the test toggle, and I updated the comments and docs to note that NVFP4/MXFP4 fall back to scalar while INT4/INT3 and FP16/BF16 use AVX2. I also added a short note in qmoe_shared_layout.h on why the test toggle is exposed for the C++ and ctypes Python tests. For the tests, I updated test_qmoe_shared_layout.py to pass real BF16 bit patterns when is_bf16=1, check that the output is non-zero, and use a small helper for the two contribution calls. Since the full-expert helper requires hidden and inter to be multiples of 32 along K, I expanded Avx2PartialInt4RangesPreserveOtherColumns in qmoe_shared_layout_test.cc up to 25 columns to cover the 16 + 8 + tail path, and guarded the AVX-512 disable block with CpuHasAvx512. |
71ca154 to
5a2cb8a
Compare
…hed AVX-512 env check, and test/doc updates - Remove unreachable CPU-side zero-cold shortcut in ServiceDoorbell() and cold_count_host so QMoEOffloadClassifyKernel is the single source of truth. - Cache ORT_QMOE_CPU_DISABLE_AVX512 once inside CpuHasAvx512() static initializer and add qmoe_shared_set_disable_avx512_for_testing C ABI helper. - Read ORT_QMOE_SKIP_ZERO_COLD_DOORBELL once into a local variable in MoeOffloadSessionPolicy::ParseFromConfigOptions. - Expand Avx2PartialInt4RangesPreserveOtherColumns and Avx2BFloat16AndHalfConversionMatchScalarIncludingNaNs to cover AVX2 interior ranges, all 36 FP16 patterns, and all 65,536 half bit patterns. - Update docs/contrib_ops/cuda/moe_qmoe.md and docs/moe-cpu-offload/qmoe_offload.md for AVX-512 -> AVX2+FMA+F16C -> scalar dispatch and new environment variables. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
88ff5bb to
089bae5
Compare
…hort-circuit, and S=8 streaming optimizations (#33629) ### Description Stacked on #33622 (with CPU AVX2 INT3 kernels moved to #33450): 1. **CUDA INT3 8-Route Grouped Offload Prefill (`qmoe_kernels.{h,cu}`, `qmoe_offload.cc`)**: Adds `BuildInt3ExpertTilesKernel`, `FusedFc1GroupedFastKernel`, and `Fc2GroupedFastKernel` in `LaunchInt3Typed` and wires `int3_grouped_ws` in `QMoEOffload::ComputeShared` for `total_routes >= 32`. 2. **Zero-Cold Doorbell GPU Short-Circuit (`qmoe_kernels.{h,cu}`, `qmoe_offload.cc`)**: Adds `skip_zero_cold_doorbell` GPU short-circuit and device-memory `QMoEOffloadTelemetry` (co-authored with `@BBaditi`, #33624). 3. **Multi-Threaded Pinned Wave Staging (`qmoe_offload.cc`)**: Adds parallel `SpinPool` host-to-pinned wave staging (`wave_w1_pinned`, `wave_w2_pinned`, `wave_s1_pinned`, `wave_s2_pinned`) and overlaps `run_cpu_tail_experts()` with GPU streaming waves. ### PR stack Merge in this order: 1. #33575 - INT4/INT3 QMoE CPU/GPU offload (base: `main`) 2. #33622 - INT4 offload prefill/decode and S=8 doorbell optimizations (base: #33575) 3. #33629 - Grouped INT3 offload prefill and S=8 streaming optimizations (base: #33622) 4. #33731 - Tensor-core INT3/INT4 offload decode/prefill (base: #33629) Prerequisites #33452, #33450, #33689, and #33650 have merged into `main`. Retarget each dependent PR to `main` after its predecessors merge. --------- Co-authored-by: Bhuvaneswar Baditi <bbaditi@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hed AVX-512 env check, and test/doc updates - Remove unreachable CPU-side zero-cold shortcut in ServiceDoorbell() and cold_count_host so QMoEOffloadClassifyKernel is the single source of truth. - Cache ORT_QMOE_CPU_DISABLE_AVX512 once inside CpuHasAvx512() static initializer and add qmoe_shared_set_disable_avx512_for_testing C ABI helper. - Read ORT_QMOE_SKIP_ZERO_COLD_DOORBELL once into a local variable in MoeOffloadSessionPolicy::ParseFromConfigOptions. - Expand Avx2PartialInt4RangesPreserveOtherColumns and Avx2BFloat16AndHalfConversionMatchScalarIncludingNaNs to cover AVX2 interior ranges, all 36 FP16 patterns, and all 65,536 half bit patterns. - Update docs/contrib_ops/cuda/moe_qmoe.md and docs/moe-cpu-offload/qmoe_offload.md for AVX-512 -> AVX2+FMA+F16C -> scalar dispatch and new environment variables. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
5a2cb8a to
cf5c50f
Compare
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
Re-reviewed the current head and resolved my six addressed threads: cached environment selection/test toggle, device-side telemetry, redundant CPU shortcut removal, single environment lookup, SIMD documentation, and expanded conversion coverage. The AVX2 tests now force AVX2 and include vectorized interior plus scalar-tail checks; the completion paths retire pending requests and mark CPU completion consistently. No additional actionable findings.
Current-head CI is green across the complete rollup (one neutral check); I did not repeat runtime validation locally. This looks ready for manual approval. No approval submitted.
…33699) ### Mechanism In bounded host-cache offload mode (#33622), QMoEOffload::RunColdExperts fuses per-expert chunked demand loads (FileExpertStore::LoadExpertChunk and FinishLoadExpert) with FC1 and FC2 tile execution in a single parallel dispatch. Previously, the FC1 and FC2 stages indexed group slots in raw 0 .. group_size - 1 order, so a demand-missing slot (slot_ready == 0) at a lower slot index caused worker threads to spin on its readiness barrier before reaching already-cached ready slots (slot_ready == kReadyDoneVal). This change adds BuildReadyFirstSlotOrder and RunReadyFirstFusedColdExpertGroup in qmoe_shared_layout.h to stably partition group slots before parallel dispatch so initially ready cached slots execute before demand-loading slots in FC1 and FC2. Each slot retains its original activation input, lease binding, intermediate buffer, output buffer, and reduction order. When miss_load_count == 0 (all cache hits), reordering is bypassed. Pre-dispatch handling of in-flight prefetches (BeginLoadExpert returning -1) and lease cleanup behavior are unchanged. ### Base & Tested Configuration - **Base branch**: Applies cleanly on top of #33622 (commit 5855bd4); ready-first slot ordering is independent of CPU SIMD ISA. - **Tested configuration on Zen 2**: Both baseline and candidate benchmark builds merged #33622 with #33624 (commit 2ced7eaf86764c6cf25e38c2ba083c54251e9a45) so cold-expert INT4 GEMMs on the AVX2-only AMD EPYC 7V12 host executed Avx2Int4GemmRange rather than ScalarInt4GemmRange. AVX2 was part of both benchmark builds, not a functional requirement of ready-first scheduling. ### Scoped Benchmark Summary Autoregressive greedy single-token decode on Qwen3-30B-A3B INT4 (full_benchmark.onnx, 48 layers, 128 experts/layer, top_k=8, hidden=2048, intermediate=768, block_size=32), 1x NVIDIA A100-SXM4-80GB + AMD EPYC 7V12 (8 SpinPool CPU threads), tail_substitution_threshold=0.0, skip_zero_cold_doorbell=0. Evaluated across N = 8 balanced pairs (2 complete 4x4 cyclic Latin squares across 4 prompts x 32 generated tokens = 124 post-first tokens/run, counterbalanced across AB/BA orders = 32 paired requests/condition): | Condition (N = 8 Latin-Square pairs, 124 post-first tokens/run) | Baseline Decode Wall (ms, mean ± SD) | Candidate Decode Wall (ms, mean ± SD) | Paired Wall Diff (Cand - Base, mean ± 95% CI) | Baseline Sustained (tok/s, mean ± SD) | Candidate Sustained (tok/s, mean ± SD) | Paired Throughput Diff (mean ± 95% CI) | |---|---:|---:|---:|---:|---:|---:| | **Primary**: adaptive_promo_on, gpu_slots=32, host_cache_slots=16 (2.04 GB), prewarm=1 | 3592.00 ± 72.82 | 3533.61 ± 61.54 | **-58.38 ± 27.70 ms** (-1.62% ± 0.74%, 95% CI [-2.36%, -0.88%], p = 0.0016, 8/8 faster) | 34.534 ± 0.690 | 35.101 ± 0.613 | **+0.567 ± 0.256 tok/s** (**+1.65% ± 0.77%**, 95% CI [+0.88%, +2.42%], median +1.43%) | | **Cache-Hit Control**: static, gpu_slots=32, host_cache_slots=95 (12.11 GB), prewarm=1 | 3470.58 ± 60.32 | 3452.16 ± 39.59 | -18.42 ± 30.80 ms (-0.52% ± 0.88%, 95% CI [-1.40%, +0.37%]) | 35.738 ± 0.614 | 35.924 ± 0.410 | +0.186 ± 0.316 tok/s (+0.53% ± 0.89%, 95% CI [-0.36%, +1.42%]) | ### Output Parity & Validation - **Output parity**: Across all 64 paired requests (2,048 generated token steps), generated token sequences and final-step float32 logits SHA-256 hashes matched between baseline and candidate. - **Cache-hit control**: No statistically detectable regression in the tested cache-hit control (host_cache_slots=95, paired decode wall-time difference -0.52% ± 0.88%, 95% CI [-1.40%, +0.37%]). - **Unit & regression tests**: Added QMoESharedLayoutTest.ReadyFirstFusedColdExpertGroupCoversMixedHitsMissesDuplicatesAndFailures (13/13 non-AVX512 C++ tests in QMoESharedLayoutTest.* on the pure submission branch, 15/15 on the combined AVX2 branch, and all non-skipped Python tests in test_qmoe_offload_v4.py and test_qmoe_shared_layout.py pass). ### Limitations - Reorders compute slots within mixed ready + demand-loading groups only; synchronous LoadExpertChunk I/O (pread + TransposeInt4Scales) still executes on the same SpinPool CPU worker threads, yielding 1.65% higher throughput (95% CI [+0.88%, +2.42%]) in the tested Qwen INT4/A100/Zen 2 configuration. - Benchmark timings above were collected on the minimal ready-first binary (175145b1...); subsequent review-only hygiene edits (MSVC intrin.h guard, group_size <= MaxGroupSlots bounds check, test placement for clean apply on #33622, and reverting an unrelated 2-line GroupLeaseGuard edit for scope isolation) were validated with the C++ and Python regression suites without re-running the timed benchmark campaign. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Motivation & Summary
Builds on #33575 (
[CUDA/CPU] Add INT4 and INT3 QMoE CPU/GPU offload support) with two decode-path optimizations (numerically equivalent to within~1e-5for the AVX2 cold-expert kernel, and bitwise identical for the zero-cold doorbell short-circuit) and one C++ test fixture fix:AVX2 + FMA + F16C cold-expert INT4 kernel ($O(1)$ lookup table in
qmoe_shared_layout.cc):On x86-64 hosts with
AVX2+FMA+F16Cthat do not supportAVX-512(such as AMD EPYC Zen 2 on Azure NDv4 A100 VMs and Intel Haswell–Alder Lake),CpuHasAvx512()returnsfalseand cold-expert INT4 dequant-GEMM and FP16/BF16 row conversions fall back to scalar C++ loops (46.36 ms/tokof CPU compute at25%GPU residency on 48-layerQwen3-30B-A3B). This PR adds runtimeCPUID+XGETBVdetection (CpuHasAvx2FmaF16c()) and anAVX2+FMA+F16Cimplementation for FP16/BF16 row conversions andAvx2Int4GemmRange(supporting both prepacked SM80 INT4 layout and raw INT4 layout, FP16 and BF16 scales/biases), plus anReorderedPosition.Zero-PCIe doorbell short-circuit ($M \le 4$ ), when all $M \times k$ selected experts for a layer are already GPU-resident (
session.moe.offload.skip_zero_cold_doorbell):During partial-residency hybrid decode (
cold_count == 0), the default doorbell path still copies the activation tile and expert indices to pinned host memory and wakes the CPU doorbell worker thread. Whensession.moe.offload.skip_zero_cold_doorbell="1"(orORT_QMOE_SKIP_ZERO_COLD_DOORBELL=1) is enabled under static placement without cross-layer promotion,QMoEOffloadClassifyKernelcounts cold selections via__syncthreads_count(is_cold)before issuing any host writes and, whencold_count == 0, advancesdoorbell_done_hostanddoorbell_req_hostdirectly on the GPU (__threadfence_system()) soQMoEWaitAndAccumulateKernelproceeds without PCIe host staging or CPU worker wakeups.INT3 CUDA test fixture update (
moe_test.cc):Updates
MoETest.QMoECudaInt3TiledLogHasOneCounterUpdateto match the SwiGLUQMoEcontract introduced in [CUDA] Add INT4 and INT3 QMoE CPU/GPU offload support #33575 (activation_type = "swiglu",swiglu_fusion = 1,fc1rows= 2 * inter_size, and unsigned INT3 zero-point code4).Key Changes
onnxruntime/contrib_ops/cpu/moe/qmoe_shared_layout.{h,cc}:CpuHasAvx2FmaF16c(),CompilerHasAvx2(), and C ABIqmoe_shared_cpu_has_avx2()/qmoe_shared_set_disable_avx512_for_testing().Avx2HalfBitsToFloatRow,Avx2FloatToHalfBitsRow,Avx2BFloat16BitsToFloatRow,Avx2FloatToBFloat16BitsRow,Avx2RoundFp16InPlace, andAvx2Int4GemmRange.ReorderedPositionwith a 32-entry constant lookup table.onnxruntime/contrib_ops/cuda/moe/qmoe_kernels.{h,cu},onnxruntime/contrib_ops/cuda/moe/qmoe_offload.{h,cc},onnxruntime/core/framework/moe_offload_plan.h,include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h:kOrtSessionOptionsMoeOffloadSkipZeroColdDoorbell(session.moe.offload.skip_zero_cold_doorbell, default"0").cold_count == 0and cross-layer promotion is inactive; retire pre-completed sequence numbers cleanly inDoorbellService.QMoEOffloadTelemetry(ORT_QMOE_OFFLOAD_TELEMETRY=1) in device scratch memory and copy back once in~QMoEOffload().onnxruntime/test/contrib_ops/moe_test.cc,onnxruntime/test/contrib_ops/qmoe_shared_layout_test.cc,onnxruntime/test/python/transformers/test_qmoe_shared_layout.py,onnxruntime/test/python/test_qmoe_offload_v4.py:MoETest.QMoECudaInt3TiledLogHasOneCounterUpdate.AVX2+FMA+F16Cvs. scalar numerical parity (max_abs_diff < 1e-5, including all 65,536 FP16 half patterns and partial-range vector interiors) and zero-cold doorbell bitwise parity, sequence counter transitions, and cross-layer promotion fallback.docs/contrib_ops/cuda/moe_qmoe.md:AVX-512 -> AVX2+FMA+F16C -> scalarcold-expert dispatch,session.moe.offload.skip_zero_cold_doorbell, and environment variables (ORT_QMOE_SKIP_ZERO_COLD_DOORBELL,ORT_QMOE_OFFLOAD_TELEMETRY,ORT_QMOE_CPU_DISABLE_AVX512).Verification & Benchmarks
Unit & Provider Tests
onnxruntime_test_all --gtest_filter="*MoETest*:*QMoE*": 6 / 6 passedonnxruntime_provider_test --gtest_filter="QMoESharedLayoutTest.*:MoETest.*": 91 / 91 passedpytest onnxruntime/test/python/test_qmoe_offload_v4.py onnxruntime/test/python/transformers/test_qmoe_shared_layout.py onnxruntime/test/python/transformers/test_qmoe_cuda.py: 153 / 153 passedEnd-to-End 48-Layer
Qwen3-30B-A3B(QMoEINT4,block_size=32, weights_prepacked=1) Decode Latency (ORT_QMOE_OFFLOAD_TELEMETRY=0, 5 Trialsmean ± stdev)Measured on 1x NVIDIA A100-SXM4-80GB + AMD EPYC 7V12 (Zen 2,$M = 1$ , $k = 8$ , 128 experts/layer) with
AVX2+FMA+F16C, noAVX-512), 5 independent trials of 10 warmup + 30 decode steps (ORT_QMOE_OFFLOAD_TELEMETRY=0(and a separateORT_QMOE_OFFLOAD_TELEMETRY=1pass to record wakeup/short-circuit counts):skip_zero_cold=0, ms/tok)skip_zero_cold=1, ms/tok)skip=0/skip=1)skip=1)skip=0skip=1)(Note:
skip_zero_cold_doorbell="1"is 100% bitwise identical (max_abs_diff = 0.0,cosine = 1.000000) toskip_zero_cold_doorbell="0"across all residencies. On an uncontended host, single-stream decode latency forskip=0vs.skip=1is within noise because GPU resident GEMVs overlap the zero-cold doorbell wakeup, whileskip=1eliminates the 4 KB/layer PCIe host staging writes and reduces CPU doorbell wakeups by 76.4% at 50% residency and 97.9% at 75% residency.)