[CPU] Add opt-in MatMulNBits FP32 throughput mode - #31723
[CPU] Add opt-in MatMulNBits FP32 throughput mode#31723Mustapha Jaber (mustjab) wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds an opt-in “adaptive compute” mode for CPU MatMulNBits (accuracy-level-4) that dynamically selects between existing CompInt8 and CompFp32 MLAS paths based on total row count (M * batch_count), aiming to improve throughput on large square W4/block-32 workloads while preserving current behavior by default.
Changes:
- Added a new session config key
mlas.enable_qnbit_adaptive_computeand plumbed it into the CPUMatMulNBitskernel to retain both prepacked layouts and switch compute type at runtime. - Extended prepacked-weight sharing to include the additional
CompFp32packed-B buffer when adaptive mode is enabled. - Added/updated unit tests to cover the adaptive boundary behavior, dynamic scales/zero-points metadata, and cross-session prepack sharing with adaptive enabled.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| onnxruntime/test/contrib_ops/matmul_nbits_prepack_sharing_test_util.h | Extends the sharing-test helper signature to allow enabling adaptive mode. |
| onnxruntime/test/contrib_ops/matmul_nbits_prepack_sharing_test_util.cc | Enables the adaptive session option in sharing tests when requested. |
| onnxruntime/test/contrib_ops/matmul_4bits_test.cc | Adds adaptive compute tests (boundary + dynamic metadata) and an adaptive prepack-sharing test; allows scales to be non-initializers. |
| onnxruntime/contrib_ops/cpu/quantization/matmul_nbits.cc | Implements adaptive mode gating, dual-layout prepack + sharing, and runtime compute-type selection. |
| include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h | Adds the public session option key and documentation for adaptive compute. |
be39345 to
7c90a8c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (4)
onnxruntime/test/contrib_ops/matmul_4bits_test.cc:507
- The skip message mentions "Adaptive QNBit compute", but this test is validating the
mlas.qnbit.force_fp32session option (dynamic scales/zero points). Updating the message makes it clearer why the test is skipped on non-x86/x64 builds.
#if !defined(MLAS_TARGET_AMD64_IX86)
GTEST_SKIP() << "Adaptive QNBit compute is currently supported only on x86/x64.";
#else
onnxruntime/test/contrib_ops/matmul_4bits_test.cc:746
- The skip message mentions "Adaptive QNBit compute", but this test is specifically about forced CompFp32 + prepacked-weight sharing via
mlas.qnbit.force_fp32. Adjusting the message avoids implying adaptive/shape-dependent selection.
#if !defined(MLAS_TARGET_AMD64_IX86)
GTEST_SKIP() << "Adaptive QNBit compute is currently supported only on x86/x64.";
#else
onnxruntime/contrib_ops/cpu/quantization/matmul_nbits.cc:124
- When both
mlas.qnbit.force_fp32=1andmlas.use_lut_gemm=1are set,prefer_lut_gemm_can still be enabled and the kernel will take the LUT prepack/compute path, effectively bypassing the forced-CompFp32 intent for eligible accuracy-level-4 nodes. This makes the new session option behavior dependent on an unrelated LUT setting and can defeat the “force fp32” guarantee.
compute_type_{info.GetConfigOptions().GetConfigEntry(kOrtSessionOptionsMlasQNBitForceFp32) == "1" &&
kQNBitForceFp32Supported && std::is_same_v<T1, float> &&
nbits_ == 4 && block_size_ == 32 &&
info.GetAttr<int64_t>("accuracy_level") == static_cast<int64_t>(Level4) &&
MlasIsQNBitGemmAvailable(nbits_, block_size_, SQNBIT_CompFp32)
onnxruntime/test/contrib_ops/matmul_4bits_test.cc:488
- The skip message mentions "Adaptive QNBit compute", but this test is exercising the session-wide
mlas.qnbit.force_fp32mode. The message should describe the actual feature being skipped to avoid confusion when diagnosing CI skips.
This issue also appears in the following locations of the same file:
- line 505
- line 744
#if !defined(MLAS_TARGET_AMD64_IX86)
GTEST_SKIP() << "Adaptive QNBit compute is currently supported only on x86/x64.";
#else
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Suppressed comments (1)
onnxruntime/contrib_ops/cpu/quantization/matmul_nbits.cc:120
- This disables LUT GEMM for every float MatMulNBits node whenever the session option is set, even though the option only applies to eligible W4/block-32/level-4 nodes. LUT currently supports W2 nodes, so a mixed throughput session will turn an explicitly requested LUT node into the unpacked dequantization fallback. Gate this suppression with the same eligibility predicate used for
compute_type_(or leave LUT enabled for ineligible nodes).
info.GetConfigOptions().GetConfigEntry(kOrtSessionOptionsMlasQNBitForceFp32) != "1" &&
Description
Adds an opt-in, session-wide CPU
MatMulNBitsthroughput mode.Set
mlas.qnbit.force_fp32=1to make eligible x86/x64 float-input, 4-bit, block-size-32, accuracy-level-4 nodes use the existing CompFp32 path instead of CompInt8 for the entire session.Motivation and Context
For Marian translation, CompInt8 provides strong single-sentence latency, but CompFp32 scales much better at large batches. Shape-dependent switching improved throughput but changed the numerical path between serial and batched execution, increasing token mismatches. This version intentionally selects CompFp32 once at session construction so all batch sizes use the same numerical path.
The intended deployment model is two sessions:
mlas.qnbit.force_fp32=1session for throughput/batched requests.End-to-end validation
Rebuilt ONNX Runtime GenAI from landed main commit
53f2379f(PR #2368), including Extensions pin60687b12, against this ORT build.Five alternating 97-sentence baseline/throughput process pairs used the same GenAI 0.16 binary, ORT DLL, graphs, corpus, and seed:
Batch-32 scaling improves from 1.92x to 3.81x E2E and from 2.38x to 4.04x generation. Serial/batch parity improves from the existing 2/97 level-4 mismatches to 0/97 at every batch size in FP32 mode.
The tradeoff is explicit: FP32 mode is about 1.7x slower at batch 1, so it is intended for a separate throughput-oriented session rather than as a default.
Isolated encoder-session private memory decreased from approximately 89.3 MB to 60.6 MB because only the CompFp32 packed layout is retained.
Validation
onnxruntime_provider_test.exe --gtest_filter=MatMulNBits.*unit_tests.exeon four existing logging symbols.git diff --checkpasses.