Skip to content

MXU 延迟:GF (6acc60406)

本页中的所有地址都适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(build libtpu_lts_20260413_b_RC00,BuildID md5 89edbbe81c5b328a958fe628a9f2207d)。该二进制文件 strip。section 映射:.text/.rodata VMA == 文件偏移;.data.rel.ro VMA − 0x200000 == 文件偏移;.data VMA − 0x400000 == 文件偏移。所有 cell 整数都直接从 GF MxuLatencyTable 构造函数读取。

摘要

本页转储 6acc60406(v7,“GF”)MxuLatencyTable:按 modifier × MxuResource 的 reservation 矩阵,用于给每个 MXU op 持有各内部子单元的时长定价。它是 Viperfish array<int,19> 和 Ghostlite array<int,11> 矩阵的 6acc60406 兄弟,由 MXU 延迟概览 中记录的字节相同模型读取。6acc60406 和 Ghostlite 共享形状array<int,11>、相同 modifier key 类型、相同 GainLatchMode key helper),但不是同一个实例:两个代际由不同构造函数构建(GlcCycleTable vs GfcCycleTable),携带不同的基础 op 延迟成本,并通过不同 accessor 读取 cell。下面的所有内容都是 GF 实例,它由 GfcCycleTable 分配,并由 CHECK 字符串命名 mxu_latency_table_gf.cc 的构造函数构建。

两个事实使 6acc60406 数字值得固定。第一,在 256×256 systolic array 上,每个 matmul 的 hold 是 Viperfish 的一半:bf16 matmul 持有 accumulate 端口 4 个周期(VF:8),因此连续的 bf16 matmul 流以 VF 两倍的 issue 速率流水化。第二,GF lookup 直接按 MxuResource 索引读取 reservation array,即 matmul 吞吐为 array[3]、matpush 吞吐为 array[8]没有 Ghostlite 风格的 default-seed remap。该 lookup 还带有 fp8-fnuzF8E4M3FnF8E5M2)GLM transform 路径,会把 fp8 latch mode 路由到它们自己的 reservation bucket。

本页最后完整反编译 windowing_util::ComputeDmaLevels,因为 6acc60406 matmul 成本会乘以一个 DMA 效率因子,而该因子的输入就是此函数计算的 descriptor-fragment 数。fragment 数会落入 {1.6, 1.3, 1.1, 1.05} 之一(或者对单个连续传输乘积 > 31 使用 1.0),bucket 逻辑按字节精确。

对于重新实现,契约是:

  • GF MxuLatencyTable 对象布局(0xa0 字节,map 位于 this+0x00/0x20/0x40/0x60/0x80),以及带 < 11 kNumMxuResources 边界的 array[res & 0xf] = cycles 插入机制。
  • 16 行 matmul 和 16 行 matpush reservation,逐 cell 对应,再加两行 Vlxmr 和 MatmulDataFormat → base-latency 成本 map(kF32/kBf16 = 211,kF8E5M2/kF8E4M3Fn = 204)。
  • GetResourceUsage opcode 分派(matmul 289/295/301/307;matpush 324–327,带 ^0xB/|0x30/|0x32 GLM 预变换)、直接 array[3]/array[8] 读取,以及按 dtype 的吞吐数字。
  • ComputeDmaLevelsDmaLevel struct、contiguity-break level-build 循环、opcode_produced_register_type ∈ {2,4} merge gate,以及 fragment-count → efficiency-multiplier 表。
GF/6acc60406 MxuLatencyTable(符号误标;由 GfcCycleTable 分配)
对象0xa0 B,位于 GfcCycleTable this+0x18;值类型为 array<int,11>
Ctor@0x1c8bb1c0 — 填充 Matpush(+0x00)、Matmul(+0x20)、Vlxmr(+0x40)、MatmulDataFormat→cost(+0x80
Lookup@0x1c8bdb20 — 直接 array[3](matmul)/ array[8](matpush);无 remap
Insert helpersmatpush @0x1c8bc2e0、matmul @0x1c8bc540、vlxmr @0x1c8bc760array[res & 0xf] = cy
CT dispatchGfcCycleTable::GetCyclesForThroughputHelper @0x1c89f400
资源数MxuResource::kNumMxuResources = 11 — CHECK 锚定(> 0xA → fatal)
吞吐(bf16 / fp8)matmul array[3] = 4 / 8 · matpush array[8] = 2 / 4
基础 op 延迟kBf16/kF32 = 211 · kF8E5M2/kF8E4M3Fn = 204
ComputeDmaLevels@0x1c86a9e0 · efficiency bucket {1.6, 1.3, 1.1, 1.05}1.0 默认(也用于乘积 > 31

对象布局与构建

目的

GF 表在 GfcCycleTable 构造时构建一次。cycle table 分配两个堆对象:一个 0x30 字节的 GhostlitePerformance 表,位于 this+0x10Performance: GF 的按 opcode 网格),以及一个 0xa0 字节的 MxuLatencyTable,位于 this+0x18。两者都由各自的构造函数填充;本页覆盖第二个。

c
// GfcCycleTable::GfcCycleTable @0x1c89eec0
v4 = operator new(0x30u); sub_1C8D3740(v4);   // GhostlitePerformance grid → this+0x10
v6 = operator new(0xA0u); sub_1C8BB1C0(v6);   // MxuLatencyTable          → this+0x18
```text

### 结构

构造函数 `@0x1c8bb1c0` 对五个 Abseil flat-hash-map 和一个 count word 做零初始化,然后插入每一行:

```text
MxuLatencyTable (0xa0 bytes, at GfcCycleTable + 0x18)
  this + 0x00   flat_hash_map<MatpushModifier,     array<int,11>>   ── latch / matprep
  this + 0x20   flat_hash_map<MatmulModifier,      array<int,11>>   ── matmul
  this + 0x40   flat_hash_map<VlxmrModifier,       array<int,11>>   ── vector-latch-into-MRB
  this + 0x60   (Matres map slot — NOT populated by the GF ctor)
  this + 0x80   flat_hash_map<MatmulDataFormat, int>                ── base op latency; count word = 1

注意 — 与 Ghostlite 构造函数(@0x1c8b2920,data-driven SetReservations<Modifier> + insert_range)不同,GF 构造函数通过三个 direct-write helper 内联写入 reservation array,并且没有构建单独的 Matres map(对 Matres helper 的调用为零)。+0x60 slot 被清零并保持为空。照搬 GL build path 的重新实现会发出 GF 从未拥有的 Matres map。

一行如何构建

每个 map value 都是一个 array<int,11>,对每个 (resource, cycles) 对执行 array[resource & 0xf] = cycles 填充,并且 resource 被硬性限制在 kNumMxuResources

c
// matpush insert @0x1c8bc2e0 / matmul insert @0x1c8bc540 (identical mechanism)
function insert(key, pairs[]):
    array<int,11> res_vector = {0};                 // zero-init all 11 slots
    for (resource, cycles) in pairs:
        if (resource > 0xA)                          // > 10 → fatal
            LogFatal("resource_index < to_underlying(MxuResource::kNumMxuResources)");
        res_vector[resource & 0xF] = cycles;
    target_map.try_emplace(key, res_vector);
```text

`> 0xA` 边界就是宽度为 `11` 的 CHECK;同一个 `< 11` guard 会在读取侧(如下)于 `mxu_latency_table_gf.cc:415` 再次出现。一行未命名的任何 resource 都持有 0 个周期。

---

## Matmul Reservation 行

### 目的

matmul 家族由 32 位 `MatmulModifier` 作为 key,其中 `byte[0]` 是 `MatmulDataFormat` code(`1`、`2`、`9`、`0xa`),`byte[1]` 是 transpose flag,`byte[2]` 是 high-variant bit。构造函数插入 16 个 key;`GetResourceUsage` 读取 cell `array[3]`(matmul-accumulate 吞吐端口)。

### 各行(全部 16 个 key,逐 cell)

reservation triple 对格式 1/2 是 `{res3, res9, res2}`,对格式 9/0xa 是 `{res3, res9}`,已在 `@0x1c8bb7c9..0x1c8bbb31` 的构造函数中按字节确认。

| `MatmulModifier` key | format | `res2`(issue 阶段) | `res3`(**thru**| `res9`(acc latch) |
|---|---|---|---|---|
| `0x00000001` `0x00000101` `0x00010001` `0x00010101` | 1 (bf16) | 16 | **4** | 3 |
| `0x00000002` `0x00010002` | 2 (bf16-alt) | 20 | **8** | 7 |
| `0x00000102` `0x00010102` | 2, transpose | 16 | **4** | 3 |
| `0x00000009` `0x00010009` | 9 (F8E5M2) || **8** | 7 |
| `0x00000109` `0x00010109` | 9, transpose || **2** | 1 |
| `0x0000000a` `0x0001000a` | 0xa (F8E4M3Fn) || **8** | 7 |
| `0x0000010a` `0x0001010a` | 0xa, transpose || **2** | 1 |

`res3` 是 `GetResourceUsage` 对 matmul 返回的值;`res2` 是更大的 issue-stage hold(16/20 cy),会门控 issue cursor;`res9` 是次级 accumulate latch。

> **怪异点 —** fp8 格式的 transpose 变体(`...0109`、`...010a`)把 matmul hold 降到 `{res3:2, res9:1}`,这是表中*最便宜*的 matmul 行:转置 fp8 matmul 复用已加载的 staging 路径,定价几乎免费。非 transpose fp8 行最贵(`res3:8`)。

---

## Matpush Reservation 行

### 目的

matpush(latch / matprep)家族由 `MatpushModifier` 作为 key,其低三字节为 `{[0]=GainLatchModeToMatmulDataFormat(mode), [1]=transpose, [2]=1}`,高字节携带 MSR(matrix-staging-register)变体(`0x01` vs `0x03`)。构造函数插入 16 个 key;`GetResourceUsage` 读取 cell `array[8]`。

### 各行(全部 16 个 key)

reservation 会随格式变宽,落入三组值:`narrow` `{res5/4:1, res7/6:1, res8:2, res10:7}`、`mid` `{:3, :2, res8:4, res10:9}`、`wide` `{:7, :6, res8:8}`,已在 `@0x1c8bb28d..0x1c8bb737` 按字节确认。

| `MatpushModifier` key | variant | staging A · B | `res8`(**thru**| `res10`(latch) | width |
|---|---|---|---|---|---|
| `0x..010001` | v1/v3 | 1 · 1 | **2** | 7 | narrow |
| `0x..010101` | v1/v3, xpose | 3 · 2 | **4** || mid |
| `0x..010002` `0x..010009` `0x..01000a` | v1/v3 | 3 · 2 | **4** | 9 | mid |
| `0x..010102` `0x..010109` `0x..01010a` | v1/v3, xpose | 7 · 6 | **8** || wide |

> **陷阱 —** staging-port *索引*取决于构建该行所用的 MSR 变体。构造函数为一个 MSR 选择 `res4`/`res6`,为另一个选择 `res5`/`res7`(`mxu_latency_table_gf.cc:94` 处有按变体的 `Unexpected MSR` CHECK 守护该选择器)。lookup 读取的吞吐 cell 始终是 `res8`;移动的是 staging hold。把 staging 索引视为跨变体固定会把 hold 放错位置。

---

## 按格式的吞吐 — Conv `R[0]`/`R[1]` 输入

`GetResourceUsage` 在构建 lookup key 时强制 transpose 为 0,因此卷积成本模型读取非 transpose 行。`GfcCycleTable::GetCyclesForThroughputHelper` `@0x1c89f400` 把每个 cost-table `Instruction`(CT ordinal)绑定到一个 LLO opcode、一个 resource index 和一个 transpose flag:

| CT | LLO opcode | family | key fmt | res read | cycles |
|---|---|---|---|---|---|
| 0 | 289 (`0x121`) | matmul | 1 (bf16) | `array[3]` | **4** |
| 1 | 295 (`0x127`) | matmul | 2 | `array[3]` | **8** |
| 2 / 4 | 301 (`0x12d`) | matmul | 9 (F8E5M2) | `array[3]` | **8** |
| 3 | 307 (`0x133`) | matmul | 0xa (F8E4M3Fn) | `array[3]` | **8** |
| 5 | 324 (`0x144`) | matpush | 1 (bf16, direct GLM) | `array[8]` | **2** |
| 6 | 326 (`0x146`) | matpush | GLM `^0xB` (transpose flip) | `array[8]` | per fmt |
| 7 / 9 | 327 (`0x147`) | matpush | GLM `\|0x30` → fmt 9 | `array[8]` | **4** |
| 8 | 325 (`0x145`) | matpush | GLM `\|0x32` → fmt 0xa | `array[8]` | **4** |

matpush transpose CT(1115)以 `sub_1C8BDB20(a1, op, opcode, 8, 1)` 的形式重新进入 `sub_1C8BDB20`,即 resource `8`、`latch_mode = 1`,从而选择转置(`wide`)值集(`res8 = 8`)。matmul CT(04)的 CT→opcode 绑定是根据 helper 传入的 resource index(`3`)和 transpose(`0`),以及分派消费的 opcode 推断;matpush opcode(324327)在 helper 和 `sub_1C8BDB20` 中均按字节确认。

> **6acc60406 vs VIPERFISH —** 6acc60406 thru(matmul) bf16 = **4**,fp8 = **8**thru(matpush) bf16 = **2**,fp8 = **4**。Viperfish(见 [VF](mxu-latency-vf.md))是 matmul bf16 = 8 / int8 = 32,matpush 2 / 86acc60406 matmul hold 是 VF 的一半:256×256 systolic array 使每周期 MAC 速率翻倍,因此同一个 op 占用 accumulate 端口的周期更少。matpush narrow hold 不变(2),wide hold(8)等于 VF int8-wide cell。

基础 op 延迟是位于 `this+0x80` 的单独 `MatmulDataFormat → int` map,由构造函数的四个 `try_emplace` CHECK 字符串命名:

| `MatmulDataFormat` | 基础延迟 |
|---|---|
| `kF32`, `kBf16` | **211** |
| `kF8E5M2`, `kF8E4M3Fn` | **204** |

这些是 v7 按格式的 op 延迟;v6e(Ghostlite)构造函数为同样格式存储 192/182,这是证明两个表是不同实例的最清晰单个数字。

---

## Lookup

### 算法

`GetResourceUsage` `@0x1c8bdb20`(误标为 `raw_hash_map::at`)是读取路径。它边界检查 resource,按 opcode 分派到一个 family,构建 key,查找行,并读取 cell:

```c
function GF_MxuLatencyTable_GetResourceUsage(out, this, instr, res, latch_mode):  // @0x1c8bdb20
    if res > 0xA:                                      // mxu_latency_table_gf.cc:415
        LogFatal("mxu_resource_idx < to_underlying(MxuResource::kNumMxuResources)")
    switch (instr.opcode):
        case 289: key = MatmulModifier{fmt=1};  map = this+0x20      // matmul
        case 295: key = MatmulModifier{fmt=2};  map = this+0x20
        case 301: key = MatmulModifier{fmt=9};  map = this+0x20
        case 307: key = MatmulModifier{fmt=10}; map = this+0x20
        case 324: key = MatpushKey(latch_mode);          map = this+0x00  // matpush
        case 325: key = MatpushKey(latch_mode | 0x32);   map = this+0x00  // → fp8-fnuz (F8E4M3Fn)
        case 326: key = MatpushKey(latch_mode ^ 0x0B);   map = this+0x00  // transpose flip
        case 327: key = MatpushKey(latch_mode | 0x30);   map = this+0x00  // → fmt 9
        default:  return MakeErrorImpl("Unsupported opcode: ...")   // :455
    entry = map.find(key)
    if not entry: throw out_of_range                   // raw_hash_map::at
    array<int,11> res_vector = entry.value             // vmovups: [rdx+8] matmul / [rdx+4] matpush
    out = res_vector[res]                              // DIRECT — no remap

matmul key 是 {fmt, 0, 0, 0}(transpose 强制为 0)。matpush key 由三个共享 helper 组装:GainLatchModeToMatmulDataFormat @0x1d629260(byte[0])、LatchModeIsTranspose @0x1d628ea0(byte[1])和 LatchOpcodeToMsr(0x91) @0x1c8a1300(byte[3]);byte[2] 是常量 1。对 opcode 0x91LatchOpcodeToMsr 返回 0。

与 GHOSTLITE 对比 — GF 读取是对 CT helper 传入的 resource 直接执行 array[res](matmul 为 3,matpush 为 8),没有变换。Ghostlite lookup @0x1c8b7560 则会在读取前播种默认值(res4 → idx3res9 → idx9)。两个代际共享构造函数形状和 key helper,但读取索引和 seed remap 不同,必须按代际绑定。这就是 res-remap 3/8 区别:GF 在索引 3 和 8 上是 direct。

matpush 的 vmovups [rdx+4] 与 matmul 的 vmovups [rdx+8] 反映了不同 key 宽度(MatpushModifier 的 value 前有 4 字节,MatmulModifier 前有 8 字节)。整个 array<int,11> 会先复制到栈上再索引。

Vlxmr 行

this+0x40 处的 Vlxmr map 携带两行,由 sub_1C8BC760(0, &unk_B43CD6C, …)sub_1C8BC760(257, &unk_B43CD74, …) 插入(key 0x00x101,已在 ctor 中按字节确认)。insert helper 遍历 (int resource, int cycles) pair stream,并应用同样的 resource < 11 边界(mxu_latency_table_gf.cc:50 处 CHECK)。@0xb43cd6c 的第一个 pair 读取为 {res0:2}(按字节确认)。key 0x101 的完整逐行 pair 集在此处未验证,因为 iterator 边界无法仅从 rodata 静态固定;兄弟 Vlxmr 形状见 MXU Latency: GL (Ghostlite)

函数映射

函数地址作用
GfcCycleTable::GfcCycleTable0x1c89eec0分配 0x30 perf + 0xa0 MxuLatency
GF MxuLatencyTable ctor0x1c8bb1c0填充 16 matpush + 16 matmul + 2 vlxmr + cost map
GF MxuLatencyTable::GetResourceUsage0x1c8bdb20opcode 分派 + find + 直接 array[res]
matpush insert helper0x1c8bc2e0array[res & 0xf] = cy< 11 边界
matmul insert helper0x1c8bc540同上,MatmulModifier map
vlxmr insert helper0x1c8bc760来自 rodata pair 的 VlxmrModifier 行
GfcCycleTable::GetCyclesForThroughputHelper0x1c89f400CT → opcode/res/transpose 分派
GainLatchModeToMatmulDataFormat0x1d629260matpush key byte[0]
LatchModeIsTranspose0x1d628ea0matpush key byte[1]
LatchOpcodeToMsr0x1c8a1300matpush key byte[3];(0x91) → 0

ComputeDmaLevels — 绑定到 Matmul 成本的 Fragment 数

目的

卷积 / windowed-transfer 成本路径会把其带宽项乘以一个 DMA 效率因子,该因子的输入是窗口分解出的 descriptor fragment 数。windowing_util::ComputeDmaLevels @0x1c86a9e0 计算该分解。结果供给 WindowCyclesGenericTargetAgnostic @0x14552180,后者把 fragment 数转换为效率乘数。

DmaLevel struct 与 level-build 循环

ComputeDmaLevels 返回一个 std::vector<DmaLevel>,每个元素 24 字节:

c
struct DmaLevel {                  // 24 B; vector stride lea[rax+rax*2]<<3
    long axis;                     // +0x00  first window-axis index of this level
    long count;                    // +0x08  init 1, then count *= window_stride[axis] per merged axis
    long flags;                    // +0x10  zero-init; level marker, not read by the cost path
};
```text

该函数首先 CHECK 窗口的逐轴 inline vector(`window_bounds`、`pad_high`、`pad_low`,以及 dynamic-base 第二数组)都具有相同 rank,然后当窗口的 `+0x338` 配置标量为 `≥ 2` 时裁剪连续 minor dim(row-major 最内层 run 不计作自己的 level):

```c
rank = window_bounds.size()
CHECK(base_bounds.size() == rank && pad_high.size() == rank && pad_low.size() == rank)
N = rank - (WD[+0x338] >= 2 ? 1 : 0)                 // minor-dim trim

level-build 循环从每个 axis 开始一个新的 DmaLevel,并在所有连续性条件都成立时把后续 axis 合并进去:

c
for axis in 0 .. N:                                  // outer: start a new level
    level = {axis, count=1, flags=0}
    for r in axis+1 .. N:                            // inner: try to merge axis r
        if elemental_stride[r] != 1: break
        operand = stride_level_operand[r]            // WD + 0xe0 [r], an LloValue*
        if operand:
            if operand.opcode >= 0x1cd: fatal()
            rt = opcode_produced_register_type[operand.opcode]
            if rt != 2 && rt != 4: fatal()           // ProducesSreg() || ProducesVreg()
            contiguous = llo::KnownEq(window_stride[r], operand)
        else:
            contiguous = (window_stride[r] == base_bounds[r])
        if not contiguous: break
        if pad_low[r] != 0 || dilation[r] != 0: break
        level.count *= window_stride[r]              // "access_multiplier"
    emit(level)                                      // a finalized contiguity break
```text

> **注意 —** `+0xe0` 逐轴第二数组(`stride_level_operand`)是 DMA-level *descriptor* operand,不同于 `window_stride`(后者驱动字节数)。它的 register class 会门控 merge:LLO value 必须产生 **Scalar**(`opcode_produced_register_type == 2`,`ProducesSreg()`)或 **Vector**(`== 4`,`ProducesVreg()`);predicate(1)、mask(3)或无结果 op(0)会使门控失败并强制产生 contiguity break。`llo_util.h:35` 处的 CHECK(`value->ProducesSreg() || value->ProducesVreg()`)准确命名了这一点。规范卷积 stride operand(`kScalarAddressCalculation` `@0x86` 等)全都是 Scalar,因此可合并;mask-typed stride operand 会使 DMA fragment 化。

发出的 `DmaLevel` 数等于 contiguity break 数;它们的 `count` 字段乘积就是 fragment 数。

### fragment 数 → 效率乘数

`WindowCyclesGenericTargetAgnostic` `@0x14552180` 读取 level vector,并按字节精确地把 fragment 乘积 `v18`(merge 循环中的运行中 `access_multiplier`)分 bucket:

| fragment product `v18` | 乘数 | rodata |
|---|---|---|
| `≤ 1` level(无 fragment) | **1.0** | `@0xa2df230` |
| `== 1` | **1.6** | `@0xa2d71a0` |
| `∈ [2, 3]` | **1.3** | `@0xa2d71a8` |
| `∈ [4, 7]` | **1.1** | `@0xa2df4c8` |
| `∈ [8, 31]` | **1.05** | `@0xa2df2a8` |
| `> 31` | **1.0**(落到默认值) | `@0xa2df230` |

四个读取的 double 都已在 `.rodata` 中按字节确认:`1.0`(`@0xa2df230`)、`1.6`(`@0xa2d71a0`)、`1.3`(`@0xa2d71a8`)、`1.1`(`@0xa2df4c8`)、`1.05`(`@0xa2df2a8`)。`{1.6, 1.3}` 对是 `@0xa2d71a0` 处的 2 项表,由 `setge(v18 ≥ 2)` 索引;`1.1` 和 `1.05` 是独立常量。返回的带宽放入值是 `(byte_count · elem_size / bytes_per_cycle) · multiplier + residual`。

> **陷阱 —** fragment 乘积 `> 31` 不会收到惩罚乘数:该分支(`if (v18 > 31)`)跳过 bucket 表,并保留 `1.0` 默认值。`@0xa2df1b0` 处的 `2003.0` 常量(按字节确认)**不是** fragment-count bucket;它在函数入口由*元素类型*谓词选择(window descriptor operand 存在且 `(*(int16*)(op+11) & 0x7C) == 0x10`),独立于 `ComputeDmaLevels`,并且缩放的是 `bytes_per_cycle` 除数,而不是 fragment 乘数。不要把 `2003.0` 绑定到 fragment 数。
>
> **怪异点 —** 单个连续传输(`≤ 1` level)的乘数是 `1.0`,但恰好为 `1` 的 fragment 乘积(一个 level,其合并后的 element-count 乘积为 1)是 `1.6`。这是两条不同路径:`≤ 1`-level 默认值(`v60 <= 1`)会在 bucket 表之前短路;`v18 == 1` bucket 只有在 level vector 非平凡时才到达。重新实现必须保留两者。

---

## Worked Example — 6acc60406 bf16 Conv Deposit

对于 6acc60406 bf16 卷积,按格式的 MXU reservation 读取现在是数值化的:

- `R[0]` matpush = matpush_count · thru[CT5](bf16) = matpush_count · `array[8]` = matpush_count · **2**
- `R[1]` matmul = op_count · thru[CT0](bf16) · 0.5 / Target / EPF = op_count · `array[3]` · 0.5 /= op_count · **4** · 0.5 / …。
- `R[2]` Xlu = `4` · ChunksPerTile · rem(`GhostlitePerformance` 网格 cell,[Performance: GF](performance-gf-ghperf.md))。

对于 fp8(`F8E5M2`/`F8E4M3Fn`),`R[0]` = 4 且 `R[1]` = 8,是 bf16 hold 的两倍。若卷积输入窗口 fragment 成 2 个 DMA level,并且每 level element 乘积为 3(具有非 unit stride 的 3-tap kernel axis),`ComputeDmaLevels` 返回 2 个 level,`v18 = 3`,带宽放入值乘以 `1.3`;完全连续的 load 乘以 `1.0`;fragment 乘积 `> 31` 也回退到 `1.0`(无 fragment-count 惩罚,避开重度 strided 布局来自更大的字节数,而不是乘数)。

---

## 相关组件

| 名称 | 关系 |
|---|---|
| `mxu-latency-overview` | 共享模型、key scheme,以及 `GetResourceUsage` 读取路径 |
| `mxu-latency-gl` | Ghostlite `array<int,11>`,相同形状、不同 ctor、192/182 基础延迟 |
| `mxu-latency-vf` | Viperfish `array<int,19>`,matmul hold 翻倍 |
| `matmul-mode-modifiers` | modifier ordinal、format code、GLM transform |
| `performance-gf-ghperf` | 携带 conv `R[2]` Xlu cell 和相同按 dtype 量级的 GF 网格 |
| `window-description-cost` | 消费 `ComputeDmaLevels` 的 windowed-transfer 成本路径 |

---

## 交叉引用

- [MXU 延迟概览](mxu-latency-overview.md) — `MxuResource` 模型、key 构造,以及字节共享的 `GetResourceUsage`
- [MXU Latency: GL (Ghostlite)](mxu-latency-gl.md) — v6e `array<int,11>`;GF 缺少的 `res4→3`/`res9→9` seed remap
- [MXU Latency: VF](mxu-latency-vf.md) — Viperfish `array<int,19>`;half-VF matmul hold 对比
- [MatmulMode & Modifiers](matmul-mode-modifiers.md) — `MatpushModifier`/`MatmulModifier` ordinal,以及 `GainLatchMode` → format jump table
- [Performance: GF (GhPerf 465×31)](performance-gf-ghperf.md) — 6acc60406 occupancy 网格和 conv `R[2]` Xlu = 4 cell
- [MxuOpHoldIssues Stall Recurrence](mxu-opholdissues-stall.md) — reservation array 如何变成 issue stall
- [Resource Enum (23-slot)](resource-enum.md) — 更高层的 `ResourceVector`,不同于 MXU 内部的 `MxuResource`
- [Window Description Cost](window-description-cost.md) — 调用 `ComputeDmaLevels` 的 windowed-transfer 成本路径
- [MXU Slot](../isa/slot-mxu.md) — 这些 op 定价的 LLO MXU instruction slot
- [Matprep / IAR / Latch](../isa/slot-matprep-iar-latch.md) — matpush 家族背后的 matprep / latch op