MXU 延迟:VF
本页所有地址都适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(buildlibtpu_lts_20260413_b_RC00,BuildID md589edbbe81c5b328a958fe628a9f2207d)。该二进制未 strip — 下方每个符号都是 demangled C++ 名称。Section map:.text/.rodataVMA == file offset;.data.rel.roVMA − 0x200000 == file offset。
摘要
本页是 MxuLatencyTable 预留模型在 Viperfish(v5/v5e)上的具体化:实际的整数 cycle-hold 表体,用于定价一个 MXU op 如何占用脉动阵列子单元。概览记录的是跨 gen 不变的索引方案 — 四个 op family、四个 flat_hash_map、一次带逐 gen 默认 fallback 的 find → array[resource] 读取;本页则逐行 dump Viperfish 这个资源最宽世代的值。每个 VF 预留行都是定长 std::array<int,19>(符号字面上是 std::__u::array<int,19ul>,lookup 会检查 resource >= 0x13),每个 int 对应一个 MxuResource 微流水端口。
参照框架仍是专用于一个功能单元的 LLVM WriteRes/ProcResource 表:每个 MXU op 为若干周期预留 19 个 MXU 子端口中的一组,背靠背发射停顿由仍被持有且最忙的端口门控。Viperfish 表由一个约 27 KB 的构造函数(MxuLatencyTable::MxuLatencyTable @0x1c8a52c0)构建一次,它填充四张 map — matpush(latch/matprep)、matmul、matres(result read)和 vlxmr(vector-latch-into-MRB)— 通过 SetReservations<Modifier> 将小型稀疏 {MxuResource → cycles} 集合稠密化。matpush map 最丰富:32 个 key = 8 个 MatmulDataFormat dtype × {NoXpose, Xpose} × {Msr0, Msr1},除 f32 非转置半成本情形外几乎统一定价。
本页逐值覆盖四个 family 的 value matrix、19 列 MxuResource band 分配(由 pin 住特定列的 consumer accessor 锚定)、GetResourceUsage dispatch 及其逐资源 default-key 集(res3→15,res11→0),以及 AddOverrunCheckReservations 增强 — 反编译显示它是在八个具名 kMsr{A,B}OverrunCheck{0..3} 列上的四步 {5,13,21,29} ramp,而不是单个值。页面最后说明 MXU1 与第二个物理发射槽的 twin 关系。
对于重新实现,契约是:
- 四个
flat_hash_map<Modifier, array<int,19>>,以及GetResourceUsage为每个 family 构造的 modifier-key。 - 32 个 matpush 预留行(
{[0]:4,[push]:3,[latch]:2}形式,f32-NoXpose{[0]:2,...:1,...:1}半成本例外)、matres{[18]:8|4}tail、vlxmr{2,6,14,22,30}load-feed ramp,以及 matmul 行的目标列。 - 19 列
MxuResourceband:每个 index 预留哪个端口,由GetResourceUsage(res11→col0,res3→col15)、GetXluPathReservation(res14)和具名 overrun-check 枚举值(cols 2..9)锚定。 - 逐资源默认 fallback(res3→15,res11→0),以及
kMsr{A,B}OverrunCheck{0..3}上的 overrun-check{5,13,21,29}ramp。
| Class | xla::viperfish::MxuLatencyTable |
| VF ctor | xla::viperfish::MxuLatencyTable::MxuLatencyTable @0x1c8a52c0(约 27 KB) |
| VF lookup | xla::viperfish::MxuLatencyTable::GetResourceUsage @0x1c8ae5c0 |
| 预留数组 | std::array<int,19> — bound resource >= 0x13 → BUG() |
MxuResource 数量 | 19(MxuResource::kNumMxuResources,CHECK 锚定,mxu_latency_table_vf.cc:58) |
| Map 偏移 | matpush @ this+0x00(key+4 array)· matmul @ this+0x20(key+8 array) |
| Default-key 集 | res3 → 15,res11 → 0;其他任何 res → InvalidArgument |
| 行构造器 | SetReservations<{Matpush@0x1c8abde0, Vlxmr@0x1c8accc0, Matres@0x1c8acea0}> |
| Overrun 增强 | AddOverrunCheckReservations @0x1c8abfe0 — {5,13,21,29} on kMsr{A,B}OverrunCheck{0..3} |
| MXU1 twin | 第二个 VectorExtended 发射槽,−20-bit control region;共享一个预留后端 |
预留数组形状
目的
每个 VF 预留行都是一个按 MxuResource 索引的稠密 19-int 向量。op 的预留在构建时是稀疏的(一个 3–6 项 flat_hash_map<MxuResource,int>),但存储为稠密形式:SetReservations 将数组零初始化,只写入具名单元格,因此 op 从未触及的资源持有 0 个周期。
结构
四张 map 位于 MxuLatencyTable 对象中的固定偏移,区分依据是 key 类型(C++ template argument),而不是 discriminator 字段:
MxuLatencyTable (heap, reachable at owning LatencyTable + 0x1d8)
this + 0x00 flat_hash_map<MatpushModifier, array<int,19>> ── latch / matprep (key 4 B → array at key+4)
this + 0x20 flat_hash_map<MatmulModifier, array<int,19>> ── matmul (key 6 B → array at key+8)
this + ... flat_hash_map<MatresModifier, array<int,19>> ── matrix-result read (key 1 B)
this + ... flat_hash_map<VlxmrModifier, array<int,19>> ── vector-latch-into-MRB (key 4 B)
```text
`GetResourceUsage` `@0x1c8ae5c0` 读取 matmul map 时将 `a2 + 32`(`this+0x20`)传给 `find<MatmulModifier>`,读取 matpush map 时将裸 `a2`(`this+0x00`)传给 `find<MatpushModifier>` — 两者均有字节确认。value array 会用三次 `vmovups ymm` load(19 ints = 76 bytes ≈ 三个 32-byte load)从 hash bucket 复制到栈上后再索引。复制偏移依赖 key 宽度:8 字节对齐的 `MatmulModifier` bucket 用 `vmovups [rdx+8]`,4 字节 `MatpushModifier` bucket 用 `vmovups [rdx+4]`。
### 行如何构建 — `SetReservations`
`SetReservations<MatpushModifier>` `@0x1c8abde0`(已验证)将稀疏 `{MxuResource → cycles}` map 稠密化为 19-int array 并 `try_emplace`:
```c
function SetReservations<Modifier>(packed_key, resource_to_cycles_map, target_map): // VF @0x1c8abde0
array<int,19> res_vector = {0}; // vxorps + vmovups zero-init, 19 slots
for (resource_index, cycles) in resource_to_cycles_map:
if resource_index >= 19: // mxu_latency_table_vf.cc:58
LogFatal(MakeCheckOpString(resource_index, 19,
"resource_index < to_underlying(MxuResource::kNumMxuResources)"))
res_vector[resource_index] = cycles
if not target_map->try_emplace(packed_key, res_vector).second: // mxu_latency_table_vf.cc:71
LogFatal("resource_map->try_emplace(key, res_vector).second") // keys must be unique将 19-int array 存入新 bucket 本身也是三次 vmovups 写([rdx+4]、[rdx+0x24]、[rdx+0x30])— array 落在 bucket offset 4,位于 4-byte MatpushModifier key 之后。SetReservations<VlxmrModifier> @0x1c8accc0 和 SetReservations<MatresModifier> @0x1c8acea0 在边界(>= 0x13)和两个 CHECK 字符串(vf.cc:58、vf.cc:71)上逐字节相同,只是 key 类型不同。三个 CHECK 都在反编译中有字节确认。
NOTE — array 宽度是资源数量,不是行数。Viperfish 上
MxuResource::kNumMxuResources = 19,而 Ghostlite / TPU7x 上是 11。因此两个世代会用同一个整数索引不同的物理子单元,重新实现必须为每个 gen 保持独立的MxuResourceenum。19 由SetReservations(写侧)和GetResourceUsage(读侧)中的resource >= 0x13边界字节锚定。
Lookup 与 Default-Key 集
算法
GetResourceUsage @0x1c8ae5c0 有字节确认:
function MxuLatencyTable::GetResourceUsage(this, instr, resource, is_throughput): // VF @0x1c8ae5c0
// 1. Per-resource default fallback, taken before any table lookup.
if resource == 3: default_cycle = 15 // VF latch/issue-slot seed
elif resource == 11: default_cycle = 0 // VF matmul-issue slot seed
else: // status path, vf.cc
return InvalidArgument("Unsupported kind of resource")
if instr > 0x10A: goto matpush_dispatch // opcodes above the matmul band
// 2. Matmul family — key byte[0] = MatmulDataFormat, byte[1..] = 0; map @ this+0x20.
switch (instr):
case 212: key = MatmulModifier{format=1}; if find(this+0x20, key): read array[resource]
case 218: key = MatmulModifier{format=2}; ...
case 230: key = MatmulModifier{format=6}; ...
default: goto matpush_dispatch
matpush_dispatch:
// 3. Matpush family — key from the latch mode, map @ this+0x00.
switch (instr):
case 267: key = MatpushKey(latch_mode) // plain matpush
case 271: key = MatpushKey(latch_mode ^ 0xB) // xpose pass
case 277: key = MatpushKey(latch_mode | 0x14) // wide pass
default: LogFatal("Unsupported opcode") // vf.cc:578
if not find(this+0x00, key): throw out_of_range // raw_hash_map::at
// 4. Read.
array<int,19> res_vector = entry.value // vmovups copy ([rdx+8] matmul / [rdx+4] matpush)
if resource >= 19: BUG() // bound 0x13
return res_vector[resource]
```text
matpush key 由三个 helper 组装:`byte[0] = GainLatchModeToMatmulDataFormat(latch_mode)`(`@0x1d629260`)、`byte[1..2] = LatchModeIsTranspose(latch_mode)`(`@0x1d628ea0`)、`byte[3] = LatchOpcodeToMsr(0x8F)`(`@0x1c8a1300`)。opcode 特定的预变换 — opcode 271 的 `^0xB`、opcode 277 的 `|0x14` — 将同一物理 latch 路由到 transposed 或 wide bucket。
> **NOTE —** 逐资源 default key 是**逐世代**的。Viperfish 上是 `resource==3 → 15` 和 `resource==11 → 0`(`@0x1c8ae5c0` 处 `a4==3 → v9=15`、`a4==11 → v9=0`);Ghostlite/TPU7x 上会移动到 `resource==4 → 3` / `resource==9 → 9`。重新实现必须从逐 gen lookup 读取 default-resource key,不能把 VF pair 跨 gen 搬运。除 3 或 11 以外的任何 `resource` 返回 `InvalidArgument` Status("Unsupported kind of resource"),而不是越界读取。
>
> **GOTCHA —** matmul opcode 在各 gen 间**不**稳定。VF 在 opcode 212/218/230(formats 1/2/6)上匹配 matmul,在 267/271/277 上匹配 matpush;Ghostlite 匹配另一组 opcode。按逐 gen opcode list 绑定 family。matmul opcode 230 = `0xE6` 是 `s8` matmul ordinal(format 6 = int8/x8);212 = `0xD4` 是 f32(format 1);218 = `0xDA` 是 bf16(format 2)。
### Function Map
| Function | Address | Role |
|---|---|---|
| `viperfish::MxuLatencyTable::MxuLatencyTable` | `0x1c8a52c0` | VF ctor — 填充全部四张 map(约 27 KB) |
| `viperfish::MxuLatencyTable::GetResourceUsage` | `0x1c8ae5c0` | VF lookup — family dispatch + `find` + `array[resource]` |
| `viperfish::SetReservations<MatpushModifier>` | `0x1c8abde0` | 将 `{res→cyc}` 稠密化为 `array<int,19>`,`try_emplace` |
| `viperfish::SetReservations<VlxmrModifier>` | `0x1c8accc0` | vlxmr 行构造器(bound 0x13) |
| `viperfish::SetReservations<MatresModifier>` | `0x1c8acea0` | matres 行构造器(bound 0x13) |
| `viperfish::AddOverrunCheckReservations` | `0x1c8abfe0` | 插入 `kMsr{A,B}OverrunCheck{0..3}` → `{5,13,21,29}` |
| `GainLatchModeToMatmulDataFormat` | `0x1d629260` | matpush key byte[0] |
| `LatchModeIsTranspose` | `0x1d628ea0` | matpush key byte[1..2] |
| `LatchOpcodeToMsr` | `0x1c8a1300` | matpush key byte[3] |
| `viperfish::MxuLatencyTable::MxuOpResourceReservations` | `0x1c8ad080` | 累积窗口内逐资源预留 |
| `viperfish::MxuLatencyTable::MxuOpHoldIssues` | `0x1c8ad3a0` | 背靠背发射停顿递推 |
| `LatencyTableViperfish::GetXluPathReservation` | `0x1c8a3200` | 通过 `ViperfishPerformance` res 0x0e 读取 Xlu deposit(锚定 `MxuResource` col 14) |
---
## 19 个 MxuResource 列
### 目的
19 个 `MxuResource` 索引是 MXU 微流水预留端口。枚举(`xla::viperfish::MxuResource`)在二进制中没有 `ToString`,因此多数列按其 writer-modifier 角色进行功能性命名。若干列由直接 accessor 或 `try_emplace` CHECK-string 证据钉住,置信度为 CERTAIN。
### 列
| col | name | anchor | written by |
|---|---|---|---|
| 0 | MXU issue / dispatch port | `GetResourceUsage` `ViperfishPerformance::Resource 0xb → col0` | matpush, matmul |
| 1 | vlxmr load-feed stage 0 | ctor vlxmr map | vlxmr (No + Xpose) |
| 2 | `kMsrAOverrunCheck0` | `AddOverrunCheckReservations` CHECK `vf.cc:78` | overrun (MSR-A), matmul |
| 3 | `kMsrAOverrunCheck1` | overrun CHECK `vf.cc:80` | overrun (MSR-A) |
| 4 | `kMsrAOverrunCheck2` | overrun CHECK `vf.cc:82` | overrun (MSR-A), matmul |
| 5 | `kMsrAOverrunCheck3` | overrun CHECK `vf.cc:84` | overrun (MSR-A), matmul |
| 6 | `kMsrBOverrunCheck0` | overrun CHECK `vf.cc:87` | overrun (MSR-B) |
| 7 | `kMsrBOverrunCheck1` | overrun CHECK `vf.cc:89` | overrun (MSR-B) |
| 8 | `kMsrBOverrunCheck2` | overrun CHECK `vf.cc:91` | overrun (MSR-B), matmul |
| 9 | `kMsrBOverrunCheck3` | overrun CHECK `vf.cc:93` | overrun (MSR-B) |
| 10 | matpush PUSH port — MSR bank 0 | ctor `Msr0` push pair | matpush(Msr0), matmul |
| 11 | matpush PUSH port — MSR bank 1 | ctor `Msr1` push pair | matpush(Msr1) |
| 12 | matpush LATCH port — MSR bank 0 | ctor `Msr0` latch pair | matpush(Msr0), matmul |
| 13 | matpush LATCH port — MSR bank 1 | ctor `Msr1` latch pair | matpush(Msr1), matmul |
| 14 | Xlu / matrix-result deposit | `GetXluPathReservation` → `ViperfishPerformance` res 0xe | vlxmr(Xpose) |
| 15 | MXU-result port(matmul throughput `{8,16,32}`) | `GetResourceUsage` `Resource 3 → col15`;ctor key `15 → {8,16,32}`(fmt 1/2/6) | matmul |
| 16 | MXU-result sub-stage A | ctor matmul map | matmul |
| 17 | MXU-result sub-stage B | ctor matmul map | matmul |
| 18 | matmul-result-feed / accumulate tail | matres row `[18] = 8 \| 4` | matres |
> **NOTE —** Cols 2..9 是 `OverrunCheck` 预留,由 `AddOverrunCheckReservations` `@0x1c8abfe0` 安装:具名枚举值 `kMsrAOverrunCheck{0,1,2,3}`(cols 2,3,4,5)和 `kMsrBOverrunCheck{0,1,2,3}`(cols 6,7,8,9),每个都携带**渐进 ramp `{5,13,21,29}`**(不是平坦值),背后分别有 `vf.cc:78..93` 的不同 `try_emplace(...).second` CHECK 字符串。MSR-A 集合在 `a1 != 1`(MSR0)路径上采用,MSR-B 集合在 `a1 == 1`(MSR1)路径上采用;`a1 ∉ {0,1}` 在 `vf.cc:95` 处是 `LogFatal("Invalid MSR.")`。vlxmr feed 在自己的行中占用 col 1,但 overrun 增强给 cols 2..9 命名。
### MSR bank 选择
matpush PUSH/LATCH 端口遵循 key 的 `Msr` byte:`Msr0 → {push col 10, latch col 12}`,`Msr1 → {push col 11, latch col 13}`。Viperfish 有两个 matrix-staging-register bank(`ViperfishTarget::MatrixStagingRegisterCount` `@0x1d49ace0` = 2),因此 latch 上的 1-bit `Target` 字段(在 bundle abs 58 解码,见 [`slot-mxu`](../isa/slot-mxu.md))选择哪个 bank,该 bank index 随后成为 key byte[3] = `LatchOpcodeToMsr(0x8F)`。
---
## Matpush 预留行
### 目的
matpush(latch/matprep)family 是移动 operand 的阶段;它的预留决定背靠背 latch 发射的门控。VF ctor 构造 32 个 key:外层循环遍历 `kMsrs = {0, 1}`(`@0xb43b1df`,loop base `+0x21`),内层循环遍历格式表 `{2,3,4,5,6,7,8}`(`@0xb43b1d8`,bf16/f8e5m2.bf16/f8e4m3b11.bf16/u8/s8/u4/s4),另加直接构造的 f32 key — 每个都与 {NoXpose, Xpose} 交叉。
### Value matrix
32 行可归约为一条规则加一个例外。每个 key 都得到 `{[0]:4, [push]:3, [latch]:2}`,**但** `{f32, NoXpose, *}` 得到半成本 `{[0]:2, [push]:1, [latch]:1}`:
```text
array(fmt, xpose, Msr) = (fmt == 1 (f32) && !xpose)
? { [0]:2, [10+Msr]:1, [12+Msr]:1 } // f32 NoXpose — HALF cost
: { [0]:4, [10+Msr]:3, [12+Msr]:2 }; // everything else
where push-col = 10+Msr, latch-col = 12+Msr (Msr ∈ {0,1})key ({fmt,xpose,0,Msr}) | fmt | xpose | Msr | array<19> non-zero cells |
|---|---|---|---|---|
0x00000001 | 1 (f32) | no | 0 | [0]=2 [10]=1 [12]=1(half) |
0x01000001 | 1 (f32) | no | 1 | [0]=2 [11]=1 [13]=1(half) |
0x00000101 | 1 (f32) | yes | 0 | [0]=4 [10]=3 [12]=2 |
0x01000101 | 1 (f32) | yes | 1 | [0]=4 [11]=3 [13]=2 |
0x000000NN | 2..8 | no | 0 | [0]=4 [10]=3 [12]=2 |
0x010000NN | 2..8 | no | 1 | [0]=4 [11]=3 [13]=2 |
0x000001NN | 2..8 | yes | 0 | [0]=4 [10]=3 [12]=2 |
0x010001NN | 2..8 | yes | 1 | [0]=4 [11]=3 [13]=2 |
32 个 key 覆盖 fmt ∈ {1..8}(NN = 02..08)。格式码是 MatmulDataFormat:1 f32,2 bf16,3 f8e5m2.bf16,4 f8e4m3b11.bf16,5 u8,6 s8,7 u4,8 s4。fmt 9(f8e5m2)和 fmt 10(f8e4m3fn)没有 VF matpush key — ctor 格式表到 8 为止;这些 packed FP8 格式仅属于 Ghostlite/TPU7x。
QUIRK — 概览描述的逐 dtype throughput delta 不存在于 VF matpush 预留行中。在 Viperfish 上,除 f32-NoXpose 外每个 matpush key 都支付平坦
{[0]:4, [push]:3, [latch]:2}(f32-NoXpose 半成本为{[0]:2, [push]:1, [latch]:1});matpush dtype 只在 f32-vs-rest × transpose 上分叉,因此array[0](MxuResource 0,issue/dispatch port)是平坦 4(或 f32-NoXpose 的 2)— 不是逐格式{2,4,8}ramp(ctor@0x1c8a52c0lines 650/683 处*(_BYTE *)v998 = 0; LODWORD(v998[1]) = {2|4})。逐格式 throughput 幅度{8,16,32}(format 1/2/6 → 8/16/32)反而落在 matmul 预留数组的array[15](MxuResource 15,MXU-result port)中,在此 ctor 中字节锚定:*(_BYTE *)v145 = 15; *((_DWORD *)v145 + 1) = 8(line 1466/1467,fmt 1)、15 → 16(line 1946/1947,fmt 2)、15 → 32(line 2855/2856 和 3287/3288,fmt 6)。它也镜像在ViperfishPerformancegrid col r3 中(见performance-vf)。把逐格式 scaling 放进 matpush 预留的重新实现会在 VF 上过度计费 integer latch — 逐格式 scaling 应属于 matmularray[15]。
Sibling Families — Matres、Vlxmr、Matmul
Matres — result-read tail
MatresModifier 是 1-byte {fmt} key(array 在 key+4 读取)。整行只有 col 18 一个 tail 单元格,按 dtype 分叉:
| key / fmt | dtype | array<19> |
|---|---|---|
0x01..0x04 | f32 / bf16 / f8e5m2.bf16 / f8e4m3b11.bf16 | [18] = 8 |
0x05..0x08 | u8 / s8 / u4 / s4 | [18] = 4 |
matmul-result tail 对宽/decompose dtype 将 result-feed port(col 18)持有 8 个周期,对 integer dtype 持有 4 个周期 — integer accumulate retire 速度快一倍。
Vlxmr — vector-latch-into-MRB feed
VlxmrModifier 是 4-byte {fmt, xpose, 0, Msr} key(形状与 matpush 相同;array 在 key+4)。VF ctor 只构建两个 key — 比 32-key matpush family 更粗的维度 — 行是单调 load-feed ramp:
| key | fmt | xpose | Msr | array<19> |
|---|---|---|---|---|
0x00000000 | 0 | no | 0 | [1]=2 [2]=6 [3]=14 [4]=22 [5]=30 |
0x00000101 | 1 | yes | 0 | [1]=2 [6]=6 [7]=14 [8]=22 [9]=30 [14]=33 |
NoXpose feed 用 {2,6,14,22,30}(从 6 开始 step-8)ramp 填充 cols 1..5;Xpose 变体把 ramp 移到 cols 1,6..9,并添加 [14]=33,即 Xlu/matrix-result deposit(col 14 — GetXluPathReservation 读取的同一端口)。Xpose vlxmr 是唯一触及 Xlu cross-lane port 的 family member。
NOTE — overrun-check 插入(cols 2..9 上的
{5,13,21,29})与 NoXpose-vlxmr feed(cols 2..5)和 Xpose-vlxmr feed(cols 6..9)写入的相同MxuResource列重叠。这些是不同 map entry(vlxmr vs matmul-augmented),因此不会在同一行中冲突;列复用反映的是 MSR overrun-check 端口和 vlxmr feed stage 是同一物理子流水在不同占用深度上的表现。
Matmul — pure-matmul 预留
MatmulModifier 是 6-byte {fmt, ..., gains} packed key — 比 4-byte matpush/vlxmr 和 1-byte matres key 更宽 — 因此其 array 在 key+8 读取(lookup 中的 vmovups [rdx+8]),而其他 family 在 key+4。GetResourceUsage 构造 lookup key 时只设置 byte[0]=fmt(opcodes 212/218/230 对应 1/2/6);ctor 构建 12 个 key,b2 ∈ {0,1} 且 gains bit b5 ∈ {0,1}。pure-matmul 行目标列为 {0, 2, 4, 5, 8, 10, 12, 13, 15, 16, 17} — issue port(0)、overrun-augmented MSR ports(2,4,5,8)、push/latch banks(10,12,13)和三个 mxu-result ports(15,16,17)。Col 15 携带字节锚定的逐格式 throughput 幅度 array[15] = {8,16,32},对应 formats 1/2/6(ctor @0x1c8a52c0:1466/1947/2856/3288),也就是 GetResourceUsage 在 resource==3 → 15 default path 上返回的单元格。
GOTCHA (matmul values — partly pinned) — matmul
array[15]throughput 单元格是字节锚定 CERTAIN:ctor 为 formats 1/2/6 写入 keyMxuResource 15 → {8,16,32}(@0x1c8a52c0:1466处*(_BYTE *)v145 = 15; *((_DWORD *)v145 + 1) = 8,:1947处= 16,:2856/:3288处= 32),而GetResourceUsage在resource==3 → 15default path 上精确读取此单元格。其余 MatmulModifier 单元格(push/latch banks 10/12/13、MSR overrun ports 2/4/5/8、mxu-result sub-stages 16/17)由find_or_prepare_insert_large<MatmulModifier>(@0x1c8af3e0)内联构建,复制经AddOverrunCheck增强的输入 map;这条路径难以完整静态提取值 — KEY 结构(6-byte,array at +8)、目标列集合和 overrun 增强(按 MSR bank 在 cols 2..9 上的{5,13,21,29})已 CONFIRMED,但那些非[15]单元格整数为 MEDIUM,重新实现者应从 ctor 双重循环重新推导。matmul latency(脉动深度,而非 occupancy)存在于ViperfishPerformancegrid(bf16 base 121/131)中。
Overrun-Check 增强
AddOverrunCheckReservations @0x1c8abfe0 在每次 matmul-map insert 前对每个 MSR bank 调用一次,用 matpush overrunning staging register 的结构冒险成本增强输入 {MxuResource → int} map。反编译显示它不是单个值 — 而是在具名枚举值下插入四步 ramp:
function AddOverrunCheckReservations(msr, reservation_map): // VF @0x1c8abfe0
if msr == 1: // MSR-B bank
reservation_map.try_emplace(kMsrBOverrunCheck0, 5) // vf.cc:87, col 6
reservation_map.try_emplace(kMsrBOverrunCheck1, 13) // vf.cc:89, col 7
reservation_map.try_emplace(kMsrBOverrunCheck2, 21) // vf.cc:91, col 8
reservation_map.try_emplace(kMsrBOverrunCheck3, 29) // vf.cc:93, col 9
elif msr == 0: // MSR-A bank
reservation_map.try_emplace(kMsrAOverrunCheck0, 5) // vf.cc:78, col 2
reservation_map.try_emplace(kMsrAOverrunCheck1, 13) // vf.cc:80, col 3
reservation_map.try_emplace(kMsrAOverrunCheck2, 21) // vf.cc:82, col 4
reservation_map.try_emplace(kMsrAOverrunCheck3, 29) // vf.cc:84, col 5
else:
LogFatal("Invalid MSR.") // vf.cc:95
```text
`{5,13,21,29}` ramp(step 8)是逐 K-tile overrun 成本:overrun staging register 的 matpush 会在更深 overrun depth 0..3 上逐渐更久地持有 overrun-check port(5 → 13 → 21 → 29 cycles)。每个 `try_emplace` 都由自己的 `.second` CHECK 保护,因此同一个 overrun-check resource 不会被插入到一个 matmul 行两次。这是 matmul 预留的结构冒险半边,被折叠进 matmul map,使 consumer 将其视为普通 array 的一部分。
---
## MXU1 Twin 关系
Viperfish 通过**两个** `VectorExtended` bundle slots(MXU0 / MXU1)发射 MXU ops,它们是在一个共享 8×6-bit operand pool 上的固定 −20-bit control-region twin([`slot-mxu`](../isa/slot-mxu.md),[`decode-side-vf-gxc`](../isa/decode-side-vf-gxc.md))。成本模型不会按 slot 复制预留表:`MxuLatencyTable` 以 `MatmulDataFormat`/`GainLatchMode` modifier 为 key,而不是以哪个物理 slot 或四个物理 MXU array 中的哪个发射了 op 为 key。MXU1 和 MXU0 op 都解析为针对同一四张 map 的同一个 `find → array[resource]` 读取。
slot-vs-array 正交性(两个 control slots,`mxu_count = 4` 个物理 array)是 bundle encoder 的职责;预留模型将其折叠为 modifier key。`MxuOpResourceReservations` `@0x1c8ad080` 累积一个 instruction window 跨两个 slot 施加的逐 `MxuResource` 预留,`MxuOpHoldIssues` `@0x1c8ad3a0` 计算下一条 MXU op 在仍被持有的资源下会停顿多少周期 — 也就是 [概览](mxu-latency-overview.md) 和 [`mxu-opholdissues-stall`](mxu-opholdissues-stall.md) 详述的停顿递推。
> **NOTE —** 因为 matpush 只持有 push/latch port 1–3 个周期(`{2,1,1}`/`{4,3,2}` 行),而 systolic matmul latency 是数百周期,VF 上背靠背 bf16 latch stream 以接近 issue-rate 流水;int8 路径在 VF 上的预留同样是 `{4,3,2}`(dtype scaling 存在于 `ViperfishPerformance` grid),因此仅 matpush *occupancy* 不会让这一代上的 int8 相对 bf16 降速 — 这是 VF 特有的 quirk,不同于 [概览](mxu-latency-overview.md) 为一般 modifier 模型描述的 `{8,7,6}` scaling。
---
## Related Components
| Name | Relationship |
|---|---|
| `mxu-latency-overview` | 本页整数所填充的跨 gen 不变索引方案 |
| `matmul-mode-modifiers` | `MatmulDataFormat`/`MatpushModifier` key bytes 和 format → group binding |
| `performance-vf` | 独立的 `ViperfishPerformance` grid(28 cols)— 与此 `array<int,19>` 不同的逐格式 `{8,16,32}` matmul throughput cells |
| `resource-enum` | 更高层 23-slot `ResourceVector` — 不同于 `MxuResource` |
| `mxu-latency-gl` / `-gf` / `-pf` | `array<int,11>`(GL/GF)和 PF sibling reservation matrices |
| `mxu-opholdissues-stall` | 消费这些 array 的 issue-stall 递推 |
---
## 交叉引用
- [MXU 延迟概览](mxu-latency-overview.md) — `MxuLatencyTable` 模型、四个 family、以及本页为 VF 具体化的 `find → array[resource]` 读取
- [MatmulMode & Modifiers](matmul-mode-modifiers.md) — `MatmulModifier`/`MatpushModifier` key bytes、`MatmulDataFormat` codes,以及 format → reservation-group binding
- [MXU 延迟:PF](mxu-latency-pf.md) — Pufferfish 预留矩阵(单 MSR,无 Target 字段)
- [MXU 延迟:GL (Ghostlite)](mxu-latency-gl.md) — 带 shifted defaults(res4→3,res9→9)的 `array<int,11>` 预留矩阵
- [MXU 延迟:GF (TPU7x)](mxu-latency-gf.md) — TPU7x `array<int,11>` 预留矩阵
- [Performance: VF](performance-vf.md) — 28 列 `ViperfishPerformance` grid、matmul throughput cells,以及与此预留表共存的 res 0x0e Xlu deposit
- [Resource Enum (23-slot)](resource-enum.md) — 逐 bundle `ResourceVector`,不同于 MXU 内部的 `MxuResource`
- [MXU Slot](../isa/slot-mxu.md) — 此表定价的 latch/matmul ops 所属 LLO MXU instruction slot;输入 matpush key 的 Transpose/Target latch bits
- [Decode-Side: VF / GXC](../isa/decode-side-vf-gxc.md) — MXU1 −20 twin,以及 matpush key 读取的 abs 57/58 Transpose/Target fields
- [MxuOpHoldIssues 停顿递推](mxu-opholdissues-stall.md) — `MxuOpResourceReservations`/`MxuOpHoldIssues` 如何把这些 array 转换为发射停顿