第一部分:量化算法总览

1.1 对称量化 (Symmetric Quantization)

核心原理:零点固定为 0,仅需 scale 因子。

量化:   x_quant = round(x_fp32 / scale)
反量化: x_fp32 = x_quant × scale
  • scale: 通常取 max(|x|) / (2^(bit-1) - 1)
  • INT8 对称范围: [-128, 127] (S8)
  • 优势: 实现简单,硬件友好,zero-point 无需存储
  • 劣势: 对偏置分布的数据(如 ReLU 激活值)有较大精度损失

1.2 非对称量化 (Asymmetric Quantization)

核心原理:引入非零零点 (zero-point),覆盖偏置分布。

量化:   x_quant = round(x_fp32 / scale) + zero_point
反量化: x_fp32 = (x_quant - zero_point) × scale
  • scale: (max(x) - min(x)) / (2^bit - 1)
  • zero_point: round(-min(x) / scale),范围 [0, 2^bit-1]
  • UINT8 范围: [0, 255],zero_point 覆盖偏置
  • 劣势: 矩阵乘需要额外 ZP 校正项: (W-ZP_w)(X-ZP_x) = WX - W·ZP_x - ZP_w·X + ZP_w·ZP_x

1.3 Per-Tensor vs Per-Channel vs Per-Group 量化

概念辨析
张量类型ShapePer-Channel 的轴参数数量典型场景
权重 W (Linear/Conv)[C_out, C_in] 或 [C_out, C_in, H, W]C_out 轴 (输出通道)C_out 个 scale + C_out 个 ZP最常见:每个输出通道独立量化
激活值 X[N, C_in] 或 [N, C_in, H, W]C_in 轴 (输入通道)C_in 个 scale较少见:输入通道间动态范围差异大时使用
激活值 X (Transformer)[N, SeqLen, C]C 轴 (隐藏维度)C 个 scaleLLM 量化常用
偏置 B[C_out]C_out 轴C_out 个 scale配合 W 的 per-channel

关键结论:Per-Channel 不是 N 轴(batch)的量化。N 轴的"per-element"量化几乎不用——batch 内每个样本分布不同,无法用固定 scale。

参数数量对比
粒度Scale 参数数量ZP 参数数量总参数 (INT8)存储位置
Per-Tensor1 个1 个2PBUF 1 槽位
Per-Channel (C_out=64)64 个64 个128PBUF 64 槽位 (vector mode)
Per-Channel (C_in=128)128 个128 个256PBUF 128 槽位
Per-Group MX (block=32)K/32 × M/16 个0K/32 × M/16MX_MEM 8KB
Per-Group MX (block=64)K/64 个0K/64MX_MEM
Per-Channel 的轴对齐约束
FIXP VREQ8 (vector mode) 的 per-channel 沿 C0 方向:
  NZ layout 最内层是 C0 (典型 C0=16)
  → PBUF 存 C 个 scale (按 C0 stride 索引)
  → 硬件自动每读 C0 个元素后切换到下一组 scale

例如: C=64, C0=16:
  C1=0: C0=0..15  使用 scale[0..15]
  C1=1: C0=16..31 使用 scale[16..31]
  C1=2: C0=32..47 使用 scale[32..47]
  C1=3: C0=48..63 使用 scale[48..63]
  
  共 64 个 scale 参数, 不是 1 个!
各粒度适用场景
粒度描述精度硬件代价映射
Per-Tensor整个张量共享 1 组 (scale, zp)最低FIXP scalar mode (1 槽位)
Per-Channel每个通道独立 (scale, zp), C_out 或 C_in 轴中 (C 个参数)FIXP vector mode (C/C0 组槽位)
Per-Group / Block-wise每 N 元素一组 scale(如 32/64/128)最高CUBE MX_MEM 8KB

1.4 动态量化 vs 静态量化

模式scale/zero_point 计算时机典型场景映射
动态量化每次推理时在线计算激活值量化 (activation 分布未知)VEC 在线计算 scale → PBUF → FIXP REQ
静态量化离线标定后固化权重量化 (权重离线可得分布)软件预计算 scale 写入 L1 → MOV_L1_TO_FB
QAT (量化感知训练)训练时插入 fake-quant 节点最高精度SIMD VSELRV2 (4→8/16bit LUT) 模拟;SIMD convert 指令支持

1.5 Fake Quantization

原理:训练时模拟量化误差(forward: 量化+反量化,backward: STE 直通),使模型适应低精度。

forward:  x_out = dequant(quant(x_in, scale, zp), scale, zp)  // 模拟量化噪声
backward: ∂L/∂x_in ≈ ∂L/∂x_out                              // STE 梯度近似

SIMD VSELRV2 指令 — 4bit 输入 → 8/16bit LUT 查找表输出,支持硬件加速 fake-quant 前向。

1.6 Microscaling (MX) — 块级共享 Exponent

原理(OCP Microscaling Format):每 N 个低 bit 元素共享一个 8-bit scale (2^N 形式),Scale = (要素值 × 2^scale) 恢复实际值。

MX 格式元素 bitblock sizescale bit额外共享 bit
MX-FP4 (E1M2/E2M1)4328 (2^N)
MX-FP8 (E4M3/E5M2)8328 (2^N)
HiF44648 (2^N)E1_8 + E1_16 (每 8/4 元素共享 1 bit exponent)

内积公式result = Σ (a_i × 2^(scale_a)) × (b_i × 2^(scale_b)) = 2^(scale_a+scale_b) × Σ a_i × b_i

CUBE MMAD_MX 指令,8KB MX_MEM 每 L0A/L0B,硬件自动读取+叠 scale 到 exponent。

1.7 非对称混合精度量化 (Asymmetric Dtype)

原理:Activation 和 Weight 使用不同 bit width 或不同格式。

模式ActivationWeight典型场景
A16W8FP16/BF16 (16bit)FP8/S8 (8bit)W 量化到 8bit,A 保持 FP16
A16W4FP16/BF16 (16bit)MX-FP4/S4 (4bit)W 量化到 4bit
A8W4MX-FP8 (8bit)MX-FP4/HiF4 (4bit)A/W 同时低 bit

CUBE 原生支持 A16W8/A16W4/A8W4 模式,硬件自动将低 bit 矩阵转为高 bit 等价表示。

1.8 线性量化中的偏置校正 (Bias Correction)

量化后的偏置项需要额外校正:

不带 ZP:   bias_quant = bias_fp32 / scale_out
带 ZP:     bias_quant = (bias_fp32 - ZP_w × ΣX - ZP_x × ΣW + |W|·ZP_w·ZP_x) / scale_out

CUBE 内置 BiasBuf — L0C 可从 BiasBuf 读取 bias 初值,硬件完成累加;ZP 校正项需 VEC 软件补偿(CUBE 无原生 ZP 支持)。

1.9 量化算法适用场景对比

算法精度损失存储压缩算力提升硬件支持度
INT8 对称 per-tensor4× (vs FP32)2× (vs FP16)★★★★★ CUBE+FIXP 原生
INT8 非对称 per-channel中低★★★★ FIXP vector mode; CUBE 需 ZP 补偿
FP8 (E4M3/E5M2)★★★★★ CUBE+SIMD 原生
MX-FP4★★★★★ CUBE+MX_MEM 原生
A16W8低 (A 无损)2× (W 压缩)★★★★★ CUBE 原生
A16W4中 (A 无损)4× (W 压缩)★★★★★ CUBE 原生
A8W44~8×★★★★★ CUBE 原生
QAT (低 bit 训练)最低同量化同量化★★★★ SIMD VSELRV2 + convert
Per-Group (Microscaling)最低略增 (scale 存储)同 dtype★★★★★ MX_MEM 原生

第二部分:Dtype 维度

2.1 CUBE 矩阵核支持的数据类型谱系

类别数据类型位宽Fractal Size (A×B)相对 FP16 算力倍数
普通浮点FP16 / BF1616(16,16)×(16,16) (基线)
普通浮点TF3219(16,8)×(8,16)0.5×
普通浮点FP3232(16,1)×(1,16)1/16×
低 bitINT8 (S8×S8)8(16,32)×(32,16)
低 bitHiF88(16,32)×(32,16)
低 bitFP8 (E4M3/E5M2)8(16,32)×(32,16)
低 bitS8×S48/4(16,32)×(32,16)
低 bitMX-FP4 (E1M2/E2M1)4(16,64)×(64,16)
低 bitHiF44(16,32)×(32,16)
非对称A16W816/8(16,16)×(16,16)
非对称A16W416/4(16,16)×(16,16)
非对称A8W48/4(16,32)×(32,16)

关键差异:FP4/E4M3 不支持 NaN/Inf,硬件转为 NaN→Zero / Inf→±MAX sat mode。与 IEEE 754 不一致,需软件感知。


第三部分:Layout 维度

3.1 数据排布总览

排布维度全称使用场景硬件支持路径
NZ (NC1HWC0)5DN-C1-H-W-C0CUBE 计算内部标准排布默认(L0A/L0B/L0C)
ND (NHWC)4DN-H-W-C推理输出、标准框架FIXP NZ2ND
DN (NCHW)4DN-C-H-W训练、标准输入FIXP NZ2DN

3.2 低 bit 量化场景 Layout 差异

维度普通 FP16低 bit 量化软件负担
L0A/L0B 排布FMIX: K1M1M0K0 4 BankFP4 需 6 Bank,HiF4 分两个 Fractal (E1_8/E1_16) — 硬件自动
MX Memory不存在独立 8KB MX_MEM (L0A/L0B 各),每 32/64 元素共享 scale — 需编排 MX 值搬运
输出 Layout 转换VEC 手动 NZ→ND/DNFIXP NZ2ND/NZ2DN 随路转换,支持全低 bit dtype减负 — 省 VEC pass
INT4 存储不适用S4 存于 L1 时需关注 2 元素/byte 的打包排布略有 — 但 MTE/FIXP 透明

3.3 FIXP Layout 转换 — NZ2ND vs NZ2DN

特性NZ2ND (5D→ND)NZ2DN (5D→DN)
源数据L0C 内 NC1HWC0 (5D)
目标格式NHWC (ND)NCHW (DN)
C0-stride不支持支持(并行度降至 C0/2)
支持 dtypeS32/FP32/FP16/BF16/FP8/HiF8/B8/S4
写 OUT/L1 带宽128B/cyc128B/cyc
写 UB 带宽256B/cyc128B/cyc
行合并优化N==loop2_dst_stride 条件触发M==loop1_dst_stride 条件触发

NZ2ND 地址计算(4 层嵌套循环):软件通过 loop_src_stride / loop_dst_stride 控制每层维度步长,数据块内部必须连续。

3.4 MX_MEM Layout(低 bit 量化特有)

dtypeblock sizeMX 值粒度MX_MEM 地址
FP4 / FP832 元素2^N scaleBase + 32B×⌈K/64⌉×⌊m/16⌋ + 32B×⌊k/64⌋
HiF464 元素2^N + E1_8 + E1_16Base + 64B×⌈K/64⌉×⌊m/16⌋ + 64B×⌊k/64⌋

HiF4 MX 值结构(64bit):

[63:56] Scale (8bit, 2^N)
[55:24] Reserved
[23:16] E1_8  (8bit, 每8元素的共享 exponent bit)
[15:8]  E1_16 (8bit, 每16元素中后8元素的共享 exponent bit, 上半部=第一个8)
[7:0]   Reserved

第四部分:转换(Conversion)维度

4.1 SIMD VEC 转换指令矩阵

转换方向指令精度模式
FP32 → FP16VCVTRNE
FP32 → BF16VCVTRNE / Round-to-Odd ( 新增)
FP32 → FP8 (E4M3/E5M2)VCVTRNE
FP32 → HiF8VCVTRNE
BF16 → FP8VCVTRNE
BF16 → E8M0VCVT
4bit → 8/16bitVSELRV2LUT 查表
FP16 → (VEC internal)RNE

4.2 Round-to-Odd 算法 — 低 bit 转换的精度保障

问题:FP32 → FP8 无直接指令时,需双步转换:FP32 → BF16(RNE) → FP8(RNE),但 RNE 两次舍入会产生 double rounding 误差。

Round-to-Odd 解决方案

FP32 mantissa LSB 全部 OR-reduce → 1 bit → 填入 BF16 mantissa LSB
效果:不执行任何 rounding up/down,等价于生成 sticky bit
然后 BF16 → FP8 只需一次 round,误差等同于一次 FP32 → FP8

** 新增**:VCVT 指令的 round_to_odd 模式,专为多级转换链设计。这是低 bit 量化特有的精度 key feature

第六部分:端到端量化工作流 — 映射

6.1 权重离线量化 (Weight-Only Quantization)

训练完模型
  → 离线: 统计 W per-channel max → scale_w (per-channel 非对称可选 zp_w)
  → 离线: W_fp32 → W_int8/W_fp8/W_fp4 (软件量化)
  → 离线: 存入 memory (S8/S4 打包, FP8 直接)
  → 推理时: MTE L2Prefetch→L1→L0A/L0B (dtype 透明搬运)
  → CUBE MMAD_MX / mmad a16w4 / mmad a16w8

6.2 激活值在线量化 (Activation Quantization with FIXP)

CUBE 计算 → L0C (FP32/S32)
  → MOV_L1_TO_FB(PBUF) {scale_a, zp_a, relu_param} ← 离线/VEC 在线计算的 scale
  → FIX_L0C_TO_OUT(REQ8, NZ2ND) {自动: quant+relu+layout convert}
  → OUT (S8, ND layout)

对比普通流程:省掉 VEC 一次 LD→MUL→ADD→ROUND→CLIP→ST→MTE NZ2ND 完整 pass。

6.3 MX 模式完整流程

离线: W_fp32 → W_mx_fp4 + scale_w_{per 32 elem} (2^N 编码)
  → 软件: scale_w 写入 MX_MEM (L0B 侧), W_mx_fp4 写入 L1→L0B
在线: A_fp32 → A_mx_fp8 + scale_a_{per 32 elem}
  → 软件: scale_a 写入 MX_MEM (L0A 侧), A 写入 L1→L0A
  → CUBE: MMAD_MX mx_fp4 → 硬件自动读 MX_MEM scale → 内积×(2^(scale_a+scale_b))
  → L0C (FP32)

6.4 QAT/fake-quant 训练流程

训练 forward:
  W_fp16 → VSELRV2 (4bit LUT, fake quant to S4) → MMAD (FP16 域)
  A_fp16 → VSELRV2 → MMAD

训练 backward (STE):
  ∂L/∂W_quant ≈ ∂L/∂W_fp16   // 梯度直通
  更新: W_fp16 -= lr × ∂L/∂W_quant

推理:
  离线: W_fp16 → W_int4 (最终量化)
  在线: CUBE S8×S4 / MMAD_MX fp4

第七部分:Scale 与 Data Layout — 数据演示与复杂性分析

本节通过具体数值演示各种 scale 策略与数据排布的交互,揭示量化算法从理论到硬件实现的真实复杂性。

7.1 Scale 粒度对比 — 具体数值演示

7.1.1 示例数据

假设一个 2 通道 × 2 行 × 2 列的激活值张量 (C=2, H=2, W=2),FP32:

通道 0 (C0):           通道 1 (C1):
  列0    列1              列0    列1
行0 [0.8    -1.2]    行0 [-0.15   0.25]
行1 [-0.6    0.3]    行1 [0.10   -0.05]

展平为: A = [0.8, -1.2, -0.6, 0.3, -0.15, 0.25, 0.10, -0.05]

7.1.2 Per-Tensor 对称量化到 INT8 (S8)
scale = max(|A|) / 127 = 1.2 / 127 ≈ 0.009449
所有通道共享同一个 scale

量化值:
C0: [ 0.8/scale≈85, -1.2/scale≈-127, -0.6/scale≈-64,  0.3/scale≈32 ]
C1: [-0.15/scale≈-16, 0.25/scale≈26,  0.10/scale≈11, -0.05/scale≈-5 ]

反量化的精度损失 (C1 受害最大):
C1_0: -16×0.009449 = -0.15118  (原 -0.15, 误差 0.8%)
C1_1:  26×0.009449 =  0.24567  (原  0.25, 误差 1.7%)  ← C1 小信号被粗粒度 scale 压低
C1_3:  -5×0.009449 = -0.04724  (原 -0.05, 误差 5.5%)

 映射: FIXP REQ8, scalar mode
  MOV_L1_TO_FB:  写 1 组 {scale}  到 PBUF
  FIX_L0C_TO_OUT: 读 L0C 8 个 S32 → 1 个 scale 统一量化 → 写 OUT S8

硬件复杂度: ★☆☆☆☆ — PBUF 仅 1 个 scale 槽位,指令配置最简单
精度风险:   ★★★★☆ — 跨通道 scale 差异大时 C1 等小信号通道精度严重损失
7.1.3 Per-Channel 对称量化到 INT8 (S8)
通道 0: scale_0 = max(|0.8,-1.2,-0.6,0.3|) / 127 = 1.2/127 ≈ 0.009449
通道 1: scale_1 = max(|-0.15,0.25,0.10,-0.05|) / 127 = 0.25/127 ≈ 0.001969

量化值:
C0: [85, -127, -64, 32]  (同上)
C1: [-76, 127, 51, -25]  ← 精度大幅提升!

反量化精度:
C1_0: -76×0.001969 = -0.14964  (原 -0.15, 误差 0.24%)  ← 精度提升 3.3×
C1_3: -25×0.001969 = -0.04923  (原 -0.05, 误差 1.5%)   ← 精度提升 3.7×

 映射: FIXP VREQ8, vector mode
  MOV_L1_TO_FB:  写 C=2 组 {scale_c}  到 PBUF (每 C0 通道 1 组)
                 写入量: 2×4B = 8B (vs scalar 的 4B)
  FIX_L0C_TO_OUT: 读 L0C → per-C0 取出对应 scale → 量化 → 写 OUT

硬件复杂度: ★★☆☆☆ — PBUF 需存 per-C0 scale,但 C0 维度是 NZ 最内层,硬件自然对齐
精度收益:   ★★★☆☆ — C0 方向对齐好时几乎无额外开销
7.1.4 Per-Group 量化 — MX 模式 (FP4 + Microscale)

假设 K=128 维度的权重矩阵的 K 方向一行 128 个元素,用 MX-FP4(E1M2):

原始 FP32 值 (K 方向连续 128 元素):
[0.011, 0.023, -0.008, 0.045, ..., 0.031, -0.019, 0.006, 0.052]
  ↑  block 0 (32 elem)       ↑  block 1 (32 elem)     ↑ block 2  ↑ block 3

每 32 元素计算 MX scale:
block 0: max(|0.011,0.023,...,0.045|) = 0.045
         scale_0 = 2^ceil(log2(0.045)) = 2^(-5) = 0.03125  (MX 仅支持 2^N)
block 1: max(|...|) = 0.052
         scale_1 = 2^(-5) = 0.03125  (恰好相同)
block 2: max(|...|) = 0.098
         scale_2 = 2^(-4) = 0.0625
block 3: max(|...|) = 0.015
         scale_3 = 2^(-7) = 0.0078125

MX_MEM 存储 (8B/block):
block 0: scale=0xFB ( -5 的 2's complement 8bit) → 实际 scale = 2^(-5)
block 1: scale=0xFB                                   → 2^(-5)
block 2: scale=0xFC (-4)                              → 2^(-4)
block 3: scale=0xF9 (-7)                              → 2^(-7)

内积计算 (CUBE MMAD_MX):
block 0 内积: Σ(FP4_a × FP4_b) × 2^(-5-5) = Σ(FP4_a × FP4_b) × 2^(-10)
block 2 内积: Σ(FP4_a × FP4_b) × 2^(-4-4) = Σ(FP4_a × FP4_b) × 2^(-8)

复杂性揭示

  • block 0 和 block 3 指数差 3 个数量级 — 硬件需在 accumulator 中对齐 mantissa 后再加
  • MX scale 限制为 2^N 意味着 scale 可能放大 (例: 0.045→0.03125, 浪费约 30% 动态范围)
  • 128 元素需要 128×4bit(元素) + 4×8bit(scale) = 68B vs 128×16bit FP16 = 256B, 仅 26.6% 存储
7.1.5 Scale 转换的精度陷阱 — Round-to-Odd 数值演示
原始值: FP32 = 0.00097

错误路径 (双 RNE):
  Step1 FP32→BF16(RNE): 0.00097 的 BF16 最近可表示值为 0.000971 → 向上舍入了一次
  Step2 BF16→FP8(RNE):  0.000971 的 FP8 最近可表示值为 0.00095  → 又向下舍入
  最终: 0.00095, 误差 2.1%

正确路径 (Round-to-Odd):
  Step1 FP32→BF16(RTO): 0.00097 → BF16 mantissa LSB = FP32 LSBs 的 OR-reduce
                         = 0.000968 (mantissa LSB=1, 不动原值, 仅生成 sticky)
  Step2 BF16→FP8(RNE):  0.000968 → FP8 0.00095 (round to nearest, tie even)
  最终: 0.00095, 误差 2.1% — 虽然结果相同,但 round-to-odd 保证了这个 case 的正确性

更关键的 case:
  原始 FP32 = 恰好位于 BF16 的两个可表示值正中间 (0.0009766)
  双 RNE: 第一次 round up → 0.000977, 第二次 round up → 0.0010  (双舍入! 偏差 +2.3%)
  单 RTO+RNE: sticky=0 → round to even → 0.00095 (偏差 -2.7%, 但这是正确的一次舍入行为)

7.2 数据排布 (Layout) 数据演示

以 C=4, H=2, W=2, C0=2 的张量为例。

7.2.1 逻辑张量
逻辑视图 (NCHW, DN format):
N=1, C=4, H=2, W=2

通道 0:         通道 1:         通道 2:         通道 3:
[0.1  0.2]     [0.5  0.6]     [0.9  1.0]     [1.3  1.4]
[0.3  0.4]     [0.7  0.8]     [1.1  1.2]     [1.5  1.6]
7.2.2 DN Layout (NCHW) — 内存排布
内存地址递增 →
[N0,C0,H0,W0] [N0,C0,H0,W1] [N0,C0,H1,W0] [N0,C0,H1,W1]
  0.1           0.2           0.3           0.4
[N0,C1,H0,W0] [N0,C1,H0,W1] [N0,C1,H1,W0] [N0,C1,H1,W1]
  0.5           0.6           0.7           0.8
[N0,C2,H0,W0] ...
  0.9           1.0           1.1           1.2
[N0,C3,H0,W0] ...
  1.3           1.4           1.5           1.6

特征: H×W 连续 → 适合卷积 kernel 滑动窗口
      跨通道不连续 → CUBE 需要矩阵乘法时不友好
7.2.3 ND Layout (NHWC) — 内存排布
内存地址递增 →
[N0,H0,W0,C0] [N0,H0,W0,C1] [N0,H0,W0,C2] [N0,H0,W0,C3]
  0.1           0.5           0.9           1.3           ← 4 通道打包
[N0,H0,W1,C0] [N0,H0,W1,C1] [N0,H0,W1,C2] [N0,H0,W1,C3]
  0.2           0.6           1.0           1.4
[N0,H1,W0,C0] [N0,H1,W0,C1] [N0,H1,W0,C2] [N0,H1,W0,C3]
  0.3           0.7           1.1           1.5
[N0,H1,W1,C0] [N0,H1,W1,C1] [N0,H1,W1,C2] [N0,H1,W1,C3]
  0.4           0.8           1.2           1.6

特征: 同像素点跨通道连续 → 适合 1×1 Conv / GEMM 直接 load
      空间上分散 → depthwise conv 不友好
7.2.4 NZ Layout (NC1HWC0) — CUBE 标准排布 (C0=2)
C0=2 意味着每 2 个通道打一个分形块 (fractal block)

分形块 0 (包含 C0~C1, i.e. C1=0):
  C1 维度 = C // C0 = 4 // 2 = 2 个分形块
  每个分形块内部: H×W×C0 元素连续

内存地址递增 (分形块 0, C1=0):
地址 0: [C0_block0, H0, W0, c=0] = [C=0, H=0, W=0] = 0.1
地址 1: [C0_block0, H0, W0, c=1] = [C=1, H=0, W=0] = 0.5  ← C0=2 连续
地址 2: [C0_block0, H0, W1, c=0] = [C=0, H=0, W=1] = 0.2
地址 3: [C0_block0, H0, W1, c=1] = [C=1, H=0, W=1] = 0.6
地址 4: [C0_block0, H1, W0, c=0] = [C=0, H=1, W=0] = 0.3
地址 5: [C0_block0, H1, W0, c=1] = [C=1, H=1, W=0] = 0.7
地址 6: [C0_block0, H1, W1, c=0] = [C=0, H=1, W=1] = 0.4
地址 7: [C0_block0, H1, W1, c=1] = [C=1, H=1, W=1] = 0.8

分形块 1 (包含 C2~C3, i.e. C1=1):
地址 8:  [C0_block1, H0, W0, c=0] = [C=2, H=0, W=0] = 0.9
地址 9:  [C0_block1, H0, W0, c=1] = [C=3, H=0, W=0] = 1.3
地址 10: [C0_block1, H0, W1, c=0] = [C=2, H=0, W=1] = 1.0
地址 11: [C0_block1, H0, W1, c=1] = [C=3, H=0, W=1] = 1.4
地址 12: [C0_block1, H1, W0, c=0] = [C=2, H=1, W=0] = 1.1
地址 13: [C0_block1, H1, W0, c=1] = [C=3, H=1, W=0] = 1.5
地址 14: [C0_block1, H1, W1, c=0] = [C=2, H=1, W=1] = 1.2
地址 15: [C0_block1, H1, W1, c=1] = [C=3, H=1, W=1] = 1.6

特征: C0 方向是最内层连续 → CUBE L0A/L0B 直接按 row 读取
      C1 方向控制分形块切换 → 对应 CUBE fractal M/N 维度
7.2.5 Layout 转换的 FIXP 操作 — 数值演示

NZ→ND 转换 (FIXP NZ2ND, 上述 NZ tensor → NHWC)

FIXP 从 L0C 读取 NZ 数据 → 4 层嵌套循环产生 ND:

loop0 (C1=0..1):     // 最外层迭代: C1=0 → C1=1
  loop1 (H=0..1):    // 第二层: H=0 → H=1
    loop2 (W=0..1):  // 第三层: W=0 → W=1
      loop3 (C0=0..1):// 最内层: c=0 → c=1
        读取: L0C[N0, C1, H, W, c]
        写出: OUT[N0, H, W, C1*C0 + c]

NZ 输入 (内存地址 0~15, 见 7.2.4):
  addr 0: 0.1 (C1=0,H0,W0,c0) → 写到 ND 位置 (H0,W0,C0)
  addr 1: 0.5 (C1=0,H0,W0,c1) → 写到 ND 位置 (H0,W0,C1)
  addr 2: 0.2 (C1=0,H0,W1,c0) → 写到 ND 位置 (H0,W1,C0)
  ...

ND 输出 (内存地址递增):
  [H0,W0,C0]=0.1, [H0,W0,C1]=0.5, [H0,W0,C2]=0.9, [H0,W0,C3]=1.3  ← 同像素 4 通道
  [H0,W1,C0]=0.2, [H0,W1,C1]=0.6, [H0,W1,C2]=1.0, [H0,W1,C3]=1.4
  ...

NZ→DN 转换 (FIXP NZ2DN, NZ→NCHW)

loop0 (C1=0..1):
  loop1 (C0=0..1):   // C0 在 H/W 之前! (DN 的 C 连续)
    loop2 (H=0..1):
      loop3 (W=0..1):
        读取: L0C[N0, C1, H, W, c]
        写出: OUT[N0, C1*C0 + c, H, W]

DN 输出:
  C=0: [0.1, 0.2] [0.3, 0.4]   ← 通道 0 全部空间 (H×W)
  C=1: [0.5, 0.6] [0.7, 0.8]   ← 通道 1 全部空间
  C=2: [0.9, 1.0] [1.1, 1.2]   ← 通道 2
  C=3: [1.3, 1.4] [1.5, 1.6]   ← 通道 3

关键区别: NZ2DN 比 NZ2ND 多了 C0-stride 支持
  C0-stride 时并行度降为 C0/2 (从 4 通道并行降为 2 通道)

7.3 Scale × Layout 交互 — 复杂性的根源

7.3.1 Per-Channel Scale + NZ Layout 的自然对齐
NZ Layout 下 C0 是最内层维度,per-channel scale 按 C0 对齐:

NZ 内 C0 方向 (C0=2):
  [C=0, C=1] ← 两个通道在一个 C0 block 内连续

PBUF 存储 per-C0 scale:
  Scale_0 (for C=0) = 0.009449
  Scale_1 (for C=1) = 0.001969

FIXP 量化时:
  读 NZ[addr0] = 0.1 (C=0) → 用 scale_0 → quant = 11
  读 NZ[addr1] = 0.5 (C=1) → 用 scale_1 → quant = 254   ← 无缝切换 C0 内 scale!
  读 NZ[addr2] = 0.2 (C=0) → 用 scale_0 → quant = 21
  ...

C0 方向 scale 切换: 硬件每读 C0(=2) 个元素后自动切换到下一组 scale
              复杂度: 极低 — C0 内 scale 是 PBUF 的连续 entry
7.3.2 Per-Channel Scale + DN Layout — 复杂寻址
DN Layout 下, channel 在 N 和 H×W 之间:

DN 内存: [C0,H0,W0][C0,H0,W1][C0,H1,W0][C0,H1,W1][C1,H0,W0]...[C3,H1,W1]

如果要用 per-channel scale 做 NZ2DN:
  FIXP 读 L0C(NZ) → 内部缓存+HW_MRG+COL_BUF 转置 → 按 DN 写出

转置过程中 H×W 和 C 的维度交换:
  原来 L0C 中 C0 方向连续的元素, 转置后分散到不同 DN 行
  per-channel scale 需在转置前/后插入 → 转置前应用 (量化在 NZ 域做)

FIXP 处理顺序:
  1. L0C 读出 NZ 数据 (C0 连续)
  2. Pre_Proc (Quant/Relu) — 此时仍然是 NZ 域的 C0 对齐 → scale 自然对齐
  3. Fixpipe NZ2DN 转置 → 输出 DN
  4. 如果 post-quant 在转置后做 → scale 需按 C 通道随机访问

结论: FIXP 的 quant 在 pre-proc 阶段做 (NZ 域, C0 自然对齐),避免了 scale 跨 layout 的
      复杂寻址问题。这是硬件-软件协同设计的精妙之处。
7.3.3 MX Scale + CUBE L0A/L0B Layout — 分形块尺度
CUBE MMAD_MX, FP4, 16×16 PE Slice:

L0A FP4 Layout (K1M1M0K0, A matrix M×K):
  M fractal: 16 elem  (row 方向 16)
  K fractal: 64 elem  (col 方向 64, 因为 FP4 Fractal K = 64)
  
  每 32 个 K 方向元素共享 MX scale → K 方向 64 元素需要 2 组 MX scale:
  block_0: elements k=0..31  → scale_a0 = 2^(sa0)
  block_1: elements k=32..63 → scale_a1 = 2^(sa1)

L0B FP4 Layout (K1N1N0K0, B matrix K×N):
  K fractal: 64 elem
  N fractal: 16 elem

  同 K 方向 64 元素, 也需要 2 组 MX scale:
  block_0: elements k=0..31  → scale_b0 = 2^(sb0)
  block_1: elements k=32..63 → scale_b1 = 2^(sb1)

乘法过程 (1 个 fractal):
  subblock 0: Σ(A[k=0..31]  × B[k=0..31]  ) × 2^(sa0+sb0)
  subblock 1: Σ(A[k=32..63] × B[k=32..63]) × 2^(sa1+sb1)
  → 对齐 exponent 后相加 → L0C(FP32 accumulator)

关键复杂性:
  1. L0A 和 L0B 的 MX scale 需要按 {K/32} 粒度一一配对
  2. subblock 0 和 subblock 1 的 exponent 可能不同 (sa0+sb0 ≠ sa1+sb1)
     硬件需在 group summation (E2a 阶段) 对齐 exponent
  3. HiF4 更复杂: block size=64 (只有 1 组 MX scale), 但额外 E1_8/E1_16
     每 8/4 元素共享 1bit exponent → 一个 fractal 内 8 种 exponent 组合

MX scale 与数据排布的对齐关系:
  L0A K1M1M0K0 排布中, K 是第二维 (K1K0):
    - K0=64 时, K 方向的 64 元素物理连续 (便于 burst 读 MX_MEM)
    - K1 方向是 fractal 级切换 (不同 fractal 的 MX scale 在 MX_MEM 的不同 row)
  
  MX_MEM Bank 布局: FP4 → MX_MEM 4 Bank × 16B/Bank
    → PE Slice 16 个 port 同时读取 16 个 row 的 MX scale
    → 每个 PE 需要知道自己负责的 K 范围对应哪个 MX block
7.3.4 量化 + Layout 转换全链路数值演示

以一个完整算子为例: Linear(W × X + B), H=128→64, INT8 per-channel 量化。

输入:  X_fp32 (128-dim vector)  → FIXP quant to INT8
权重:  W_fp32 (64×128 matrix)   → offline quant to INT8 per-channel (C0=16)
偏置:  B_fp32 (64-dim vector)   → offline quant to INT32
输出:  Y_fp32 (64-dim vector)   ← CUBE S32 output

Step 1: X 量化 (VEC/FIXP)
  X_fp32 = [0.12, -0.34, 0.56, ...]  (128 elem)
  FIXP REQ8, per-tensor: scale_x = max(|X|)/127
  → X_int8 = [round(x/scale_x) for x in X]

Step 2: MTE 搬运权重到 L0A/L0B (ND2NZ)
  W_int8 在 memory 中 per-C0=16 打包为 NZ 格式:
  离线量化的 W_int8[64,128] → MTE ND2NZ(C0=16)
  → L0A NZ: fractal K=32, M=16 (INT8 Fractal: (16,32)×(32,16))
  
  关键问题: per-channel scale_w 的 C0=16 与 NZ 的 C0=16 对齐
  scale_w[0:15]  对应 NZ 的 C1=0 内 16 个 C0 通道
  scale_w[16:31] 对应 NZ 的 C1=1 内 16 个 C0 通道
  ... 共 64/16 = 4 组

Step 3: CUBE 计算 (INT8 fractal)
  PE 计算: S8×S8 → S32 accumulator
  L0C 输出: S32 NZ layout, C0=16

Step 4: FIXP 量化输出 + NZ2ND (随路)
  MOV_L1_TO_FB: 加载 per-channel scale_y[0..63] (VREQ8, vector mode)
                还要加载 bias_q[0..63] 到 PBUF
  FIX_L0C_TO_OUT(REQ8, NZ2ND):
    Pre_Proc:
      C0=0: (L0C_val + bias_q[0]) * scale_y[0] → S8
      C0=1: (L0C_val + bias_q[1]) * scale_y[1] → S8
      ... (64 个 C0 通道, scale_y 在 PBUF 中连续, 自动递增)
    Fixpipe:
      NZ2ND: [N=1, C1=0..3, C0=0..15, H=1, W=1] → [N=1, H=1, W=1, C=0..63]
      
全链路 Layout 变化:
  Memory: X(DN) ──(VEC)──→ X(NZ)   ──(MTE→L0B)──→ CUBE
  Memory: W(DN) ──(offline quant)──→ W(NZ) ──(MTE→L0A)──→ CUBE
  CUBE:   L0A(NZ) × L0B(NZ) → L0C(NZ, S32)
  FIXP:   L0C(NZ, S32) ──(REQ8+NZ2ND)──→ OUT(ND, S8)

Scale 的粒度取舍在链路中的权衡:
  - X 用 per-tensor scale  → 量化简单,但大动态范围通道受害
  - W 用 per-channel scale → C0=16 对齐 NZ, 量化参数 PBUF 可直接按 C0 步进
  - Y 用 per-channel scale → 同上
  - 全程 scale 转换需软件在 VEC/FIXP 间协调传递
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐