ONNX Runtime CUDA MatMulNBits 算子深度解析:权重量化矩阵乘的分派链路、GEMV 内核与 fpA_intB 路径
【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime
导读
本文基于 ONNX Runtime 官方算子文档 docs/contrib_ops/cuda/matmul_nbits.md,系统剖析com.microsoft::MatMulNBits在 CUDA Execution Provider 中的完整实现:从算子 Schema 与权重内存布局,到"fpA_intB CUTLASS → 融合 GEMV 快速路径 → 反量化 + cuBLAS GEMM 回退"的分层分派链,再到 MoE Router 特化内核、bias 处理与全部可调环境变量。读完本文,你将能够理解 weight-only 量化线性层(含 MoE 路由与 LM head)在 GPU 上如何被高效执行,掌握权重预打包(prepacking)两种布局的选择依据,并能据此调优推理路径与排查性能瓶颈。
MatMulNBits 计算Y = A · dequant(B)ᵀ (+ bias),其中B是N × K的 4/8 bit 权重量化矩阵,采用 block-wise(分组)scale 与可选的 zero point。它是 weight-only 量化线性层的基础算子,也是 MoE Router、LM Head 等大模型组件的性能关键。
1. 算子 Schema:属性与输入
1.1 属性表
| Attribute | 含义 |
|---|---|
K | 输入特征维度(A的列数,即逻辑B的列数)。 |
N | 输出特征维度(逻辑B的行数)。 |
bits | 量化位宽:4或8。 |
block_size | 沿K方向的量化组大小(16 / 32 / 64 / 128),每组一个 scale(以及可选的 zero point)。 |
accuracy_level | 内部处理A时的最低精度级别;默认0表示未设置。 |
weight_prepacked | CUDA fpA_intB 权重布局选择器(详见 §2.1):0(默认)表示B为标准 MatMulNBits 布局、运行时可预打包;1表示B已按 CUDA SM80 fpA_intB 布局预打包;2表示B已按 CUDA SM90(Hopper)fpA_intB 布局预打包,由原生 SM90 内核消费(要求 SM90 设备且block_size ∈ {64, 128})。原生 SM90 内核在 Windows/MSVC 构建中不编译(CUDA 13 host stub 的 over-aligned TMA 参数触发 MSVCC2719,详见 moe_qmoe.md §14.1);此类构建下默认的0/1布局在 Hopper 上运行 SM80 兼容内核。 |
1.2 输入表
| Input | Index | Notes |
|---|---|---|
A | 0 | Activations,FP16 / BF16 / FP32,形状[M, K]。 |
B | 1 | Packed 4/8-bit weights。 |
scales | 2 | 每组一个 scale,元素类型与A相同。 |
zero_points | 3 | 可选。打包整数(对称量化默认)或与A相同类型。 |
g_idx/reorder_idx | 4 | 可选的 group/reorder index(act-order 量化)。 |
bias | 5 | 可选的[N]bias,加到输出上。 |
M是(展平后的)token 数:M = 1即 decode / GEMV 场景,正是快速内核的目标。
从源码看,构造时这些属性在 matmul_nbits.h 中通过info.GetAttr<int64_t>("K", &K_)等调用强制读取,并校验block_size_ > 0;has_zero_points_/has_g_idx_/has_bias_则根据输入数量与InputDefs()是否存在来判断,同时记录zero_points是否与 scale 同类型(is_zero_points_scale_same_type_)。算子注册(matmul_nbits.cc)覆盖三个 T1 变体:float、MLFloat16、BFloat16,且 CUDA EP 额外注册了 FP32 变体(但 FP32 永远不会被判定为 fpA_intB 兼容)。
2. 权重格式
对于 4-bit,B以[N, ceil(K / block_size), block_size / 2]字节存储:每个 expert/输出行n是连续的K/2字节(每个字节打包两个 4-bit 权重),其前在概念上对应scales张量中的K / block_size个 scale。量化默认是列向分组(column_wise_quant_blk_ = true);行向布局把K的块交错在N之间,无法沿N切分(这会禁用分块回退路径)。
对称 4-bit 权重存储值0..15,反量化为(q − 8) · scale;当没有zero_points输入时,快速内核硬编码 zero point 为8。这一默认值同样体现在 fpA_intB 预打包逻辑中(matmul_nbits.cc 定义了kDefaultZeroPoint4Bit = 8.0f与kDefaultZeroPoint8Bit = 128.0f)。
2.1 CUDA fpA_intB 预打包布局(weight_prepacked)
weight_prepacked=1表示输入B的张量形状与字节数与标准 MatMulNBits 的B完全相同,但字节已被重排为 CUDA fpA_intB SM80 weight-only 布局。在 ORT 预打包阶段,CUDA EP 会把这些字节直接交给 fpA_intB 内核而不做额外的 GPU 拷贝:
- 当
B已驻留设备(例如已固定到 GPU 的 constant initializer)时,整个预打包步骤被跳过(PrePack_B中is_packed = false直接返回); - 当
B驻留主机时照常传输到设备,但不再执行运行时权重转置 / mixed-GEMM 预处理步骤。
这一点在 matmul_nbits.cc 中有明确实现:预打包权重会先做字节数校验,若 tensor 已在 GPU 上则跳过拷贝;否则仅做一次cudaMemcpyAsync即完成,不经过unpack_uint4_transposed_to_int8_direct_cuda/transpose_uint8_matrix_and_convert_to_int8+preprocess_weights_for_mixed_gemm_cuda的运行时重排流水线。
通过 Python 暴露的离线 CUDA 打包器可产生该布局:
from onnxruntime.capi import onnxruntime_cuda_quant_preprocess as _cuda_quant prepacked_flat = _cuda_quant.pack_weights_for_cuda_mixed_gemm( q_weight.reshape(N, -1), N, K, bits, 80 ) prepacked_b = np.asarray(prepacked_flat, dtype=np.int8).view(np.uint8).reshape(q_weight.shape)最后一个参数是目标打包架构:
80→ SM80 布局(由 SM80 CUTLASS 内核消费,包括在新 GPU 上通过兼容路径消费),节点上设置weight_prepacked=1;90→ 原生 SM90(Hopper)布局,节点上设置weight_prepacked=2。
weight_prepacked=2选择原生 SM90(Hopper TMA/WGMMA)mixed-GEMM 内核及其 Hopper 权重布局。它要求计算能力 9.0 的设备且block_size ∈ {64, 128}(SM90 内核要求group_size是 64 元素 Hopper K tile 的倍数,因此block_size=32走非 Hopper 内核)。在 SM90 设备上,运行时预打包(weight_prepacked=0)与 SM80 预打包(weight_prepacked=1)的权重继续路由到 SM80 CUTLASS 内核/布局。
值得注意的实现细节:原生 SM90 内核是否被编译由COMPILE_HOPPER_TMA_GEMMS宏决定(Windows/MSVC 构建未定义),而ValidateSm90PrepackedWeightSupport(sm, block_size)被刻意提取为纯 host 函数(见 matmul_nbits_sm90_validation.h),可在无 Hopper GPU 的机器上用合成(sm, block_size)做单元测试。IsNativeSm90FpAIntBGemmCompiled()则以非 inline 单一定义放在matmul_nbits.cc中,避免各翻译单元因宏不一致产生 ODR 冲突。
3. 分派链(Dispatch Chain)
MatMulNBits<T>::ComputeInternal总是先尝试最廉价且适用的路径,失败后逐级回退到更通用的实现:
从 matmul_nbits.cc 的ComputeInternal源码可以精确对应这张图:
- BF16 门槛:BFloat16 在计算能力 < 8.0 的设备上直接返回
NOT_IMPLEMENTED; - fpA_intB 路径(仅在
USE_FPA_INTB_GEMM构建下):如果构造期判定has_fpA_intB_gemm_,先经 profiler 挑选最优 tactic(见 §6),命中后直接返回; - 融合快速路径门槛:仅当
reorder_idx == nullptr且zero_points(若有)为打包整数(非T类型)时,才尝试TryMatMulNBits; - bias 兜底重试:若快速路径因 bias 失败,则以
bias = nullptr重试,成功后用独立的LaunchMatMulNBitsBiasAdd内核补加 bias; - 反量化 + GEMM:都不满足时(如
M > 1、存在reorder_idx、typed zero points、形状不支持),执行反量化后接 cuBLAS GEMM,最后同样补加 bias。
快速融合路径的尝试条件可以总结为一句:没有reorder_idx,且zero_points(若有)是打包整数。这与文档和源码中的(reorder_idx_data == nullptr) && (!zero_points || !zero_points->IsDataType<T>())判定完全一致。
4. 快速路径 —— 融合 GEMV
TryMatMulNBits(matmul_nbits.cuh)按位宽分派:
bits == 8→TryMatMul8Bits(不支持 bias;若设置 bias 直接返回false);bits == 4→TryMatMul4Bits。
TryMatMul4Bits(matmul_4bits.cu)首先应用所有融合内核共用的守卫条件:
n % kColsPerThreadBlock (8) == 0 and k % 8 == 0 and m <= 16即融合路径只处理单个 token 或小批量。守卫通过后,按M=1内核(§4.1)、小 M 批量内核(§4.2)、Router 特化(§4.3)依次选择。
4.1 通用 4-bit M=1 GEMV 内核
MatMulFloat4BitsKernelM1<T, block_size, has_zero_point>实现在 matmul_4bits_m1_impl.cuh,由 dtype 专属的翻译单元实例化(matmul_4bits_half.cu/matmul_4bits_bfloat16.cu/matmul_4bits_float.cu):
- Launch:grid
(ceil(N / 8), 1),block(warpSize, 8)—— 每个输出列一个 warp(每 block 8 个 warp,即kColsPerThreadBlock = 8)。 - Scales / zero points:每个线程块仅一次载入共享内存,若总需求超过每块共享内存上限,launcher 返回
false。 - 内层循环:每个 lane 每次迭代消费 8 个打包 int4 权重,随后做 warp 归约(三级 unroll 16/4/1 加 remainder,最后 warp shuffle 归约)。
- 支持的
block_size:16 / 32 / 64 / 128。 - Bias:不支持 —— 当
bias != nullptr时内核向TryMatMul4Bits返回false(bias 的处理见 §7)。
4.2 小 M 批量 GEMV(2 ≤ M ≤ 16)
当2 <= M <= 16时,TryMatMul4Bits先尝试寄存器分块的 half/BF16 批量内核(TryMatMulBatched4Bits,源自matmul_4bits_batched.cuh),再尝试共享内存版MatMulFloatInt4KernelSmallM。这些路径将每个打包权重字只反量化一次,并在多个激活行之间复用。
源码注释揭示了设计动机(matmul_4bits.cu):单行 M1 内核每个输出行启动一个 block,各行会独立地重复读取并反量化全部B,权重流量与反量化工作量随M线性放大;而对2 <= M <= cap(kSmallMMax = 16),每个反量化权重可对寄存器中CtaM行激活做累加,把权重流量削减到ceil(M/CtaM)分之一 —— 这与 TensorRT-LLMweightOnlyBatchedGemv/ AWQ / llama.cpp MMVQ 在小批量下的设计一致。half/bf16 走寄存器分块内核(CtaM x CtaNtile,2 宽累加器),float 没有张量核 GEMM 回退,因此同一范围内使用共享内存小 M 内核。
4.3 Router GEMV 特化
MatMulFloatInt4RouterKernel<T, BlockSize>是面向 MoE-Router GEMV(output(1, N) = A(1, K) · dequant(B(N, K)) + bias(N))的特化内核,由IsSupportedRouterGemvShape选中,条件为:
zero_points == nullptr(对称量化)、M == 1;block_size ∈ {32, 64}且K % block_size == 0;(N, K)精确匹配 gated router 形状:
| Model | N(experts) | K(hidden size) |
|---|---|---|
| gpt-oss-20b | 32 | 2880 |
设计要点(均可在 matmul_4bits.cu 源码中核实):
- 每个 expert 列一个 warp,每 block 8 个 warp(
kColsPerThreadBlock = 8),grid 为(N / 8, 1)。N通过 grid 在运行时传入、K作为运行时 kernel 参数传入,因此每个(T, BlockSize)只需一次实例化即可服务所有 router 形状;只有BlockSize是模板参数(它决定 scale 步长)。 - 不使用共享内存:scale 直接从全局内存读取(这些极小尺寸下天然 L2 驻留),省去了通用内核的 staging
__syncthreads。 - Bias 融合:lane 0 在 store 前加上
bias[n_id]。 - 组大小 32 vs 64:两者都支持,因为
kPerIter = 256可被两者整除,保证每迭代的 scale 步长精确。相对 32,64 把 scale 加载次数减半;按模型精度/延迟权衡选择量化粒度即可。(每个 expert 一个 scale 的 per-row / per-column 量化不是受支持的 MatMulNBits 布局,被刻意排除。) - 与通用内核保持相同的不变式:三级 unroll(16/4/1)+ remainder、warp-shuffle 归约。
若要新增另一个 router,只需在IsSupportedRouterGemvShape中加入它的(N, K);只要N % 8 == 0且K % block_size == 0,无需改动内核。该特化可通过环境变量ORT_DISABLE_QMOE_ROUTER_GEMV_SPECIALIZATION关闭(见 §8)。
5. 回退路径 —— 反量化 + GEMM
当 fpA_intB 路径与融合 GEMV 都不适用时(例如M > 1、存在reorder_idx、typed zero points 或不支持的形状),算子将B反量化到 scratch buffer,再执行稠密 cuBLAS GEMM:
DequantizeBlockwise4b/DequantizeBlockwise8b(或列向专用 helper)把打包权重展开为T,写入N × K_padded的 scratch buffer,其中K_padded = ceil(K / block_size) · block_size;- 随后单个 cuBLAS
GEMM(transb = true)产出Y。
分块变体(Chunked)。当N很大时,物化完整的N × K_padded反量化矩阵可能主导显存。实现在以下条件同时满足时沿N把反量化+GEMM 切成多块:
- 列向量化(column-wise)且无 `reorder_idx,且
force_chunked_被设置,或scratch> 256 MB且N > 2 × chunk_target_rows。
chunk_target_rows默认为 32768(可配置,见 §8)。源码注释给出的设计理由(matmul_nbits.cc)很清晰:256 MB 是 scratch 预算,超过后完整N*K_paddedbuffer 会在 8–12 GB 消费级 GPU 上主导显存并带来 OOM 风险;N > 2*chunk_target_rows(默认 65536)保证至少两块,摊薄每块 cuBLAS 调用开销;而 32768 行/块足以打满 SM,同时保持 scratch ≲128 MB。分块循环中,每块用cublasGemmHelper计算C[:, n_start:n_end] = A[M,K] @ B_chunk[chunk_n, K]^T,输出按完整Ldc = N步长写入(matmul_nbits.cc)。
6. fpA_intB_gemm 路径(CUTLASS weight-only)
onnxruntime_USE_FPA_INTB_GEMM是主构建开关,CUDA 构建默认ON。其默认内核集合刻意保持精简,排除原生 Hopper 内核,覆盖 SM80 布局的 RC 模型契约:
- FP16 activations + INT4 或 INT8 weights;
- scale-only 量化,
block_size=32,无 zero-point、bias、g_idx; N % (bits==8 ? 32 : 64) == 0、K % block_size == 0、sm_ >= 75;- 未打包权重或
weight_prepacked=1(SM80 布局)。
设置onnxruntime_USE_FPA_INTB_GEMM_FULL=ON可构建遗留的完整内核矩阵。完整模式额外支持 BF16、block_size 64 与 128、zero-point、bias,以及原生 SM90 布局(weight_prepacked=2);原生 SM90 内核只支持block_size ∈ {64, 128}(见 §2.1)。
CheckFpAIntBEligibility(matmul_nbits.cc)是 fpA_intB / CUTLASS weight-only-GEMM 资格判定的唯一权威来源,构造器与 Level-1 workspace 估算共用,二者永不冲突。其核心逻辑:
- compact 构建仅接受 FP16 输入(
input0_elem_type == FLOAT16),完整构建接受 FP16/BF16;FP32 变体永远不兼容; - 预打包权重强制开启 fpA_intB 路径(
prepacked || (fpa_intb_option != 0)); - compact 模式
base_ok:block_size == 32 && (nbits == 4 || nbits == 8) && !has_zero_points && !has_g_idx && !has_bias && weight_prepacked != 2 && N % (nbits==8 ? 32 : 64) == 0 && K % block_size == 0 && device_sm >= 75; - 完整模式放宽为
block_size ∈ {32,64,128}、允许 zero-point/bias,但依旧排除g_idx; weight_prepacked=2额外要求device_sm == 90 && block_size ∈ {64, 128}。
当通过ORT_FPA_INTB_GEMM启用后,符合条件的 MatMulNBits 节点使用源自 TensorRT-LLM 的 CUTLASS weight-only 内核。weight 与 scale 输入(完整模式下还有 zero-point)必须是 ORT 可预打包的 constant initializer。运行时由 profiler 挑选最优 tactic:小M可能使用专用 CUDA GEMV 内核(bestTactic->enableCudaKernel),否则用 CUTLASS grouped GEMM。compact fpA_intB GEMV 不支持 zero-point 或 bias。此路径激活时优先于 §3 中除它之外的所有路径。
weight_prepacked=0时,CUDA EP 在 ORT 预打包阶段把标准 MatMulNBits 权重 initializer 预处理为 fpA_intB 布局;weight_prepacked=1时 initializer 视为已预处理,仅做字节数检查后直接拷贝(对应 matmul_nbits.cc 的PrePack_B实现)。
预打包权重是严格约束的:
- 若 ORT 未以
onnxruntime_USE_FPA_INTB_GEMM=ON构建,任何非零weight_prepacked值都会在 kernel 构造时抛出异常; - 任何非零
weight_prepacked都强制启用 fpA_intB 路径,因此启用开关(session configep.cuda.fpa_intb_gemm或环境变量ORT_FPA_INTB_GEMM)对预打包权重被忽略—— 布局选择在导出时已固定,运行时无法关闭; - 非零
weight_prepacked要求输入A为 FP16(compact 默认构建)或 FP16/BF16(完整构建),因为只有 CUDA fpA_intB 路径消费该布局; weight_prepacked必须与所选内核期望的布局一致:1是 SM80 布局,2是原生 SM90(Hopper)布局;2额外要求计算能力 9.0 设备与block_size ∈ {64, 128},否则被拒绝。
另一个易踩的坑(源码注释特别强调,matmul_nbits.cc):GEMV 内核必须用打包时所针对的架构(FpAIntBPackingSmForKernel())而非裸设备 SM 启动,因为 GEMV 的 interleave 布局与架构相关 —— 架构在 [90,100) 使用ColumnMajorInterleavedForHopper,而 SM80 打包使用ColumnMajorInterleaved。若在 SM90 上误传设备 SM,会按 Hopper interleave 读取 SM80 打包权重,产生错误结果。FpAIntBPackingSmForKernel()复用EffectiveFpAIntBWorkspaceSm()(matmul_nbits.h):仅当device_sm == 90 && weight_prepacked == 2时为 90,否则为 80,保证 workspace 估算与运行时布局选择永远一致。
CUDA graph 捕获下也有专门处理:捕获期间禁止惰性 profiling(profiling 会启动内核、记录/同步事件、分配/释放 scratch),ComputeInternal通过isCapturing(stream)检测后改查已 profiling 的 bucket(捕获前的 warmup 会填充这些 bucket);若找不到可用 tactic 会明确报错提示"捕获前先做一次 warmup 推理"。
7. Bias 处理
只有 Router 特化(§4.3)在 GEMV 内部融合 bias。对其他所有快速路径形状,TryMatMul4Bits/TryMatMul8Bits在存在 bias 时返回false。随后ComputeInternal按两步处理(对应 matmul_nbits.cc):
- 以
bias = nullptr重试TryMatMulNBits;成功后用独立的MatMulNBitsBiasAdd内核(LaunchMatMulNBitsBiasAdd)补加 bias,累加在 float 中以保证 half/bfloat16 精度; - 若快速路径仍不适用,落入反量化+GEMM 回退(§5,其本身忽略 bias),随后用同一个 bias-add 内核补加。
bias-add 内核本身是一个 grid-stride 循环(matmul_4bits.cu):output[idx] = output[idx] + bias[col],其中col = idx % n,并以 float 中间量累加后转回T;grid 上限被钳制在 CUDAgridDim.x上限(2³¹−1)以内,超出部分由 grid-stride 循环覆盖。
8. 环境变量
| Variable | Type / default | Effect |
|---|---|---|
ORT_DISABLE_QMOE_ROUTER_GEMV_SPECIALIZATION | bool,0 | 禁用 Router GEMV 特化(§4.3);相关形状回退到通用 GEMV / 反量化路径。适合 A/B 基准对比。 |
ORT_FPA_INTB_GEMM | int/string,0 | 启用 CUTLASS weight-only 路径(§6)。0或off禁用,其他值启用。 |
ORT_MATMULNBITS_FORCE_CHUNKED | int,0 | 无视尺寸启发式,强制使用分块反量化+GEMM 回退(§5)。 |
ORT_MATMULNBITS_CHUNK_SIZE | int64,32768 | 分块回退中每块的目标行数。值< 1时重置为默认值。 |
环境变量通过 ORT 跨平台的
ParseEnvironmentVariableWithDefaulthelper 读取(Windows 上安全),而非std::getenv。
此外,matmul_nbits.h 中还定义了一组session config键,它们优先于同名环境变量("config wins"),并且同时被内置 CUDA EP 与 CUDA plugin EP 读取:
ep.cuda.fpa_intb_gemm↔ORT_FPA_INTB_GEMM(0/off关闭,其余开启;解析逻辑见ParseFpAIntBEnabled,不区分大小写且空串/"0"/"off"视为关闭);ep.cuda.fpa_intb_profile_m↔ORT_FPA_INTB_PROFILE_M(初始 profile M buckets,空则用 profiler 默认 bucket 集)。
还有两个调试/诊断开关:ORT_FPA_INTB_DEBUG=1会在运行时打印所选 tactic、kernel 路径(GEMV CUDA 内核 vs CUTLASS GEMM)、权重格式(runtime-prepacked-SM80 / offline-prepacked-SM80 / offline-prepacked-SM90 / raw)、设备与打包 SM,用于追踪 SM90 布局选择问题(matmul_nbits.cc);ORT_LLM_VERBOSE > 1时还会输出每个(m, n, k, group_size)的最佳 tactic。
9. 测试
CUDA EP 内部测试通过
CUDA_EP_Unittest运行(见 cuda_provider_test.cc)。从onnxruntime_provider_test运行:./onnxruntime_provider_test --gtest_filter=CUDA_EP_Unittest.*该 wrapper 执行内部 CUDA-UT 共享库,覆盖 fpA_intB_gemm_kernel_test.cc 下的 fpA_intB / MatMulNBits groupwise GEMM 测试,以及 matmul_nbits_sm90_validation_test.cc 下的 SM90 校验测试(后者利用
ValidateSm90PrepackedWeightSupport是纯 host 函数的特点,可在无 Hopper GPU 的机器上以合成(sm, block_size)验证拒绝逻辑)。Python 算子测试:
onnxruntime/test/python/transformers(含 QMoE / GEMV profiling 辅助脚本,如profile_qmoe_gemv.sh)。CUDA 预打包权重一致性测试:test_op_matmulnbits_prepacked_cuda.py。这些测试用
onnxruntime_cuda_quant_preprocess.pack_weights_for_cuda_mixed_gemm(..., 80)生成weight_prepacked=1的 initializer,并与运行时 fpA_intB 预打包在 int4/int8、GEMV/GEMM 形状的M下做输出对比。不支持的预打包配置的构造失败测试位于 matmul_4bits_test.cc。
GEMV profiling 的基线与方法论记录在 qmoe_gemv_experiments.md。
对比 Router 特化与通用路径:对同一模型分别以
ORT_DISABLE_QMOE_ROUTER_GEMV_SPECIALIZATION=1与默认值运行即可。
修改内核后的重建提醒:修改任何.cu内核后需重建 CUDA provider(ninja onnxruntime_providers_cuda)并重跑相关测试;注意仓库构建说明中 nvcc 增量构建的注意事项。
总结
MatMulNBits 在 CUDA EP 上的实现是一套典型的"分层分派 + 专用内核 + 兜底回退"设计:构造期依据属性与设备确定 fpA_intB 资格(含预打包布局的严格校验),运行期按fpA_intB GEMV/GEMM → 融合 4/8-bit GEMV(M1 / 小 M 批量 / Router 特化)→ 反量化 + cuBLAS GEMM(全 N 或分块)的优先级逐级尝试。理解这一链路后,你可以通过weight_prepacked提前固定布局、通过ORT_FPA_INTB_GEMM/ep.cuda.fpa_intb_gemm控制 CUTLASS 路径、通过ORT_MATMULNBITS_*系列环境变量调节分块回退行为,并用ORT_FPA_INTB_DEBUG与ORT_DISABLE_QMOE_ROUTER_GEMV_SPECIALIZATION做可量化的 A/B 排查 —— 这些能力共同支撑起大模型 weight-only 量化推理在 CUDA 上的高效落地。
【免费下载链接】onnxruntimeONNX Runtime: cross-platform, high performance ML inferencing and training accelerator项目地址: https://gitcode.com/GitHub_Trending/on/onnxruntime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考