Skip to content

Performance:VF

本页所有地址都适用于来自 libtpu-0.0.40-cp314 wheel 的 libtpu.so(构建 libtpu_lts_20260413_b_RC00,BuildID md5 89edbbe81c5b328a958fe628a9f2207d)。该二进制被 stripped;下面每个符号都是 demangled C++ 名称。段映射:.text/.rodata VMA == 文件偏移;.data.rel.ro VMA − 0x200000 == 文件偏移。

摘要

本页转储 Viperfish(v5/v5e)的 ViperfishPerformance 对象:逐指令延迟数组,以及为每条 LLO 指令占用每个 intra-op 微流水线端口多少周期定价的 Instruction × Resource 占用网格。它是 performance-overview 中记录的网格家族在 VF 上的具体化,也就是 Pufferfish、Viperfish、Ghostlite 和 6acc60406 共享的堆延迟数组 + 堆二维 vector<int> 网格,并由逐字节相同的 GetResourceUsage 读取。Viperfish 位于资源数量演进的中间(7 → 7 → 20 → 28 → 31 → 31):它把 Pufferfish 的 20 列网格加宽了 8 列,主要是 4 阶段 matprep 吞吐组和 transpose-binary 结果子阶段。

参考框架是 LLVM SchedMachineModelGetLatency(instr) 返回指令的流水线深度(调度器把真依赖边抬高到的值),GetResourceUsage(instr, res) 返回 instr 占用功能单元端口 res 的周期数。VF 网格是通过读取填充它的构造函数恢复出来的,即 ViperfishPerformance::ViperfishPerformance @0x1c8c4840,其中包含一个 new 0x600 延迟数组(384 个 int32)和一个 new 0x2400 网格(384 行 × 24 字节 vector<int>,每行 new 0x70 = 28 个 int32),而不是来自 .td 文件。延迟数组(先 memset 为 0xff,随后每个槽位都被覆盖)和 378 个已填充网格单元格都来自构造函数的 762 次 DWORD 立即数写入(384 个延迟 + 378 个网格),逐字节精确。

本页记录对象布局和 GetResourceUsage 读取路径(24 字节行步幅、两个边界检查)、按占用者 LLO 类命名的 28 个资源列、写入列(res 0x0e,即 conv R[2] Xlu 项,由 GetXluPathReservation 读取)、延迟数组直方图(121/131 matmul/matprep 基础延迟),以及 LLO opcode 如何通过 GetViperfishInstruction 和 MXU latch-mode 扇出到达网格行。最后说明该网格如何与单独的 MxuLatencyTable 保留矩阵共存。

对重新实现而言,契约是:

  • ViperfishPerformance 对象布局:堆延迟数组(384 个 int32)+ 堆二维网格(384 × 28),包含精确大小和 [this+0x18]/[this+0x20] 网格指针/计数。
  • GetResourceUsage(instr, res) 读取路径:两个边界检查,24 字节(3 × a2 << 3)行步幅。
  • 按占用者命名的 28 个资源列,以及 Xlu/matrix-result 写入列(res 0x0e)及其 GetXluPathReservation 访问器。
  • LLO opcode 如何到达网格行(GetViperfishInstruction、matmul/matprep latch-mode WORD 表扇出),以及吞吐单元格如何与 MxuLatencyTable 共存。
xla::viperfish::ViperfishPerformance
构造函数ViperfishPerformance::ViperfishPerformance @0x1c8c4840new 0x600 latency,new 0x2400 grid,row new 0x70
读取路径ViperfishPerformance::GetResourceUsage @0x1c8cbc40 · GetLatency @0x1c8cbc20 · GetResources @0x1c8cbc00
资源数量28(行宽 new 0x70 = 28 int32;GetResources count 0x1c
延迟行384(new 0x600;memset 0xff,全部被覆盖)
网格384 行 × 28 宽;128 行中的 378 个已填充单元格
写入列res 0x0e(14),即 conv R[2] Xlu 项,matrix-result ops 的 cell = 8
kResources 顺序@0xb43cda8{0..27} 的 28 B 排列)
Opcode → 行GetViperfishInstruction @0x1c8a3300(jt @0xb43a104,idx = op−1)
Xlu 访问器LatencyTableViperfish::GetXluPathReservation @0x1c8a3200res = 0xe
EUP push / pop 延迟push(instr 0xcc..0xd2)= 6,pop(instr 0x168)= 1

对象布局

目的

ViperfishPerformance 从两个堆对象回答调度器的两个逐指令问题:流水线深度(GetLatency)和逐端口占用(GetResourceUsage)。这两个对象分别是一个按 Instruction 索引的扁平延迟数组,以及一个每条指令一行 vector<int> 的二维网格。

结构

该布局是共享的网格家族布局(performance-overview),带有 VF 的具体大小:

c
struct ViperfishPerformance {       // built by ctor @0x1c8c4840
    int32*  latency;                // +0x00 ; new 0x600 (384 int32), memset 0xff
    u64     latency_size;           // +0x08 ; = 384
    u64     latency_cap;            // +0x10 ; = 384
    vector<int>* grid;              // +0x18 ; new 0x2400 (384 × 24-byte vector<int>)
    u64     grid_outer_count;       // +0x20 ; = 384  (the GetResourceUsage outer bound)
    u64     grid_outer_cap;         // +0x28 ; = 384
    // each row (24-byte std::vector<int>): { int* data (new 0x70 = 28 int32, zero-init), size=28, cap=28 }
};
```text

构造函数正好发出 **762 次 DWORD 立即数写入**,即 384 个延迟 + 378 个网格单元格。写入数量就是完整性证明:每个 `mov DWORD PTR [rax+off], imm` 都按它加载的基址分类为 `[rbx]`(延迟基址)或 `[r12]`(网格基址,`r12 = [rbx+0x18]`),并且没有任何非 `rax` 基址的 DWORD-imm 写入。不同于 Ghostlite/`6acc60406` 的未定价行会保留 `0xff` memset 默认值,**每个** VF 延迟槽位(0..383)都被显式覆盖;`0xff = 255` 默认值不会保留。

> **特性 —** VF Performance 构造函数 `@0x1c8c4840` 不能生成 Hex-Rays 伪代码(它太大,并且混合了 VEX 写入),因此此处单元格值来自对其 762 次 DWORD 立即数写入的逐字节 objdump 解码,并通过写入数量恒等式(`762 = 384 + 378`)交叉校验。*读取路径* `GetResourceUsage` 和 `GetResources` 可以干净地反编译,并确认 28/ 384 行形状;单元格整数是 objdump 级别,而不是 Hex-Rays 级别,但通过写入计数方法具有 HIGH 置信度。

---

## GetResourceUsage 读取路径

### 目的

`GetResourceUsage(instr, res)` 是吞吐模型和 Xlu 保留访问器读取网格单元格的唯一访问器。它在 PF/VF/GL/GF 间逐字节相同:两个边界检查和一个由 `lea` 计算的 24 字节行步幅。

### 算法

反编译中已按字节确认:

```c
function ViperfishPerformance::GetResourceUsage(perf, instr, res):   // @0x1c8cbc40
    if perf.grid_outer_count <= instr:        // [perf+0x20] = 384 ; outer bound
        BUG()                                  // ud2
    grid = perf.grid                           // [perf+0x18]
    v4 = 3 * instr                             // row index ×3 ...
    if perf.grid[v4].size <= res:              // [grid + 8*v4 + 8] ; inner bound = 28 (row width)
        BUG()
    return *(int*)(perf.grid[v4].data + 4*res) // [grid + 8*v4] + 4*res = grid[instr][res]

3 * instr 后接 8 * 索引,就是 24 字节 std::vector<int> 步幅({int* data, u64 size, u64 cap})。GetLatency(instr) @0x1c8cbc20 是更简单的同级函数,即由 [perf+0x8] 界定的 latency[instr]GetResources() @0x1c8cbc00 返回 &kResources.rodata 字节数组 @0xb43cda8,按填充顺序列出 28 个列索引)。

陷阱 — 外层索引是逐代的 ViperfishPerformance::Instruction不是原始 LLO opcode。映射由 GetViperfishInstruction @0x1c8a3300 完成(jt @0xb43a104,idx = op−1,边界 0x1a9),而 MXU band 会通过二级 latch-mode WORD 表(@0xb43b140,valid-latch mask 0x3ffcc03)把单个 matmul/matprep opcode 扇出到多个 ordinal。直接用 LLO opcode 索引网格的重新实现会误读每个 MXU 行。

matrix-result / Xlu 写入列

有一列保存 convolution 成本模型作为其 R[2] 项读取的 matrix-result(Xlu)吞吐。在 Viperfish 上它是 res 0x0e(14),由专用访问器确认:

c
function LatencyTableViperfish::GetXluPathReservation(this, value):   // @0x1c8a3200 (verified)
    if value.opcode == 139:                        // kVectorSetPermutePattern, handled directly
        return (value[+0x40] != 0) ? 8 : 1
    instr = GetViperfishInstruction(value)
    return ViperfishPerformance::GetResourceUsage(this.perf /* [this+0x1d0] */, instr, 14)   // res 0x0e
```text

`res = 0x0e` 立即数已按字节确认(`GetResourceUsage(*(this+58*8), instr, 14)`),permute-pattern opcode 139(`kVectorSetPermutePattern`)返回硬编码的 `8`/`1`。Xlu 写入列跨代追踪 MXU 几何:res 6 PF → **res 0x0e VF** → res 0x0f GL → res 0x10 GF;已填充单元格对于 VF matrix-result/transpose-result 类是 `8`(与下面的 `kVectorMatres` r22 单元格一致)。

---

## 28 个资源列

### 目的

网格的内层轴是 `ViperfishPerformance::Resource` 枚举:28 个 intra-op EUP/MXU/Xlu 微流水线保留端口。二进制中没有 `Resource::ToString`,因此列按功能命名,即依据哪些 LLO 指令类向各列写入周期,外层索引是通过 `GetViperfishInstruction` 解析并与 `LloOpcodeName::opcode_name` `@0x21ccfef0` 交叉连接的 opcode。这些名称在*含义*上足以用于重新实现(每列保留哪个物理端口),但不是字面符号名。

### 列

`kResources` `@0xb43cda8` 是 28 字节遍历顺序:`13 18 1a 19 08 02 03 16 09 0a 04 05 06 07 0b 12 0f 14 1b 0c 10 01 15 0d 11 17 00 0e`(`{0..27}` 各出现一次)。按主导占用者为每列命名:

| col | cells | val(s) | occupant LLO band(分类器命名) | physical reservation port |
|---|---|---|---|---|
| r0 | 2 | 2 | low-ordinal MXU/setup band(ins 0x0, 0x2| MXU/setup 地址端口 A |
| r1 | 2 | 2,3 | `kDmaGeneral..`(ins 0x38| DMA 地址/发射端口 |
| r2 | 27 | 7 | MXU matmul band 0xd4..0x106(MatmulPackedMsk/Lmr) | MXU matmul prep/issue 端口 |
| r3 | 51 | 8,16,32 | MXU matmul/matprep band | **MXU matmul 吞吐端口**(×51|
| r4 | 38 | 4,5 | matprep band + DoneWithGains/LoadLmr(0xd5..0x10a| MXU matprep 吞吐 stage A |
| r5 | 38 | 12,13 | 同一 band | MXU matprep 吞吐 stage B |
| r6 | 38 | 20,21 | 同一 band | MXU matprep 吞吐 stage C |
| r7 | 38 | 28,29 | 同一 band | MXU matprep 吞吐 stage D |
| r8 | 2 | 32 | `kVectorLoadLmr`(0x109..0x10a| LMR / gain-load 端口 |
| r9 | 16 | 2,6 | result-pop FIFO band(0x10b..0x11a| MXU/EUP result-pop FIFO stage A |
| r10 | 16 | 1,5 | 同上 | result-pop FIFO stage B |
| r11 | 16 | 3,7 | 同上 | result-pop FIFO stage C |
| r12 | 9 | 41,48 | `kVectorPermute/Rotate/BroadcastLane`(0x11b..| cross-lane result stage A |
| r13 | 14 | 52,57,59 | 同上 + reduce band | cross-lane / reduce result stage B |
| **r14** | 23 | 8,16 | TransposeBinary(0x123)/Permute/Rotate/Broadcast | **Xlu / matrix-result 写入**(conv `R[2]`) |
| r15 | 5 | 109,117 | `kVectorTransposeBinary`(0x11f..0x127| transpose-binary result sub-stage A |
| r16 | 5 | 112,120 | 同上 | transpose-binary result sub-stage B |
| r17 | 3 | 7,15 | 同上 | transpose-binary result sub-stage C |
| r18 | 5 | 24 | `kVector{Add,Max,Min,..}ReduceF32`(0x12e..0x132| reduce-result 端口 |
| r19 | 1 | 3 | `kVectorCcfPush`(0x134| CCF push 端口 |
| r20 | 1 | 1 | `kVectorPrng`(0x13e| PRNG 端口 |
| r21 | 14 | 1 | `kVectorSyncFlag*/kVectorWait*/SfrfPush`(0x13f..| sync-flag / wait / SFRF 端口 |
| r22 | 1 | 8 | `kVectorMatres`(0x169| matrix-result(Matres)端口 |
| r23 | 1 | 8 | `kVectorXlaneResult/Transpose-result`(0x16a| cross-lane / transpose result 端口 |
| r24 | 1 | 3 | `kVectorCcfPop`(0x16b| CCF pop 端口 |
| r25 | 4 | 5 | `kVectorStoreEvenOddSublanes`(0x174..0x177| sublane-store 端口 |
| r26 | 6 | 6 | barnacore/store band(0x178..0x17d| sublane-store / scatter 端口 |
| r27 | 1 | 3 | `kVectorSetRngSeed`(0x17f| RNG seed 端口 |

列填充量(cells/col)总和为 378:`r0:2 r1:2 r2:27 r3:51 r4:38 r5:38 r6:38 r7:38 r8:2 r9:16 r10:16 r11:16 r12:9 r13:14 r14:23 r15:5 r16:5 r17:3 r18:5 r19:1 r20:1 r21:14 r22:1 r23:1 r24:1 r25:4 r26:6 r27:1`。

> **注意 —** `ViperfishPerformance::Resource` 枚举(28 列)不同于 23 槽逐 bundle `ResourceVector`([`resource-enum`](resource-enum.md)),是一个*不同且更低层*的枚举;它也不同于 [`mxu-latency-vf`](mxu-latency-vf.md) 中 19 值 `MxuResource`,是*第三个*枚举。这里的网格为 intra-op 微流水线阶段占用定价;`ResourceVector` 是逐 bundle 累加器;`MxuResource` 是 MXU 内部保留端口集合。同一个成本模型里有三条资源轴,混淆它们是核心陷阱。`kResources` 字节数组给出的是该网格的列遍历顺序,不是 `ResourceVector` 槽位顺序。

### VF 相对 Pufferfish 的加宽方式

Viperfish 在 Pufferfish 的 20 列基础上增加了 8 列:4 阶段 matprep 吞吐组(r4..r7,承载 `{4,12,20,28}` 和 `{5,13,21,29}` 的 step-8 占用)以及 transpose-binary 结果子阶段(r15..r17),再加上 CCF push/pop(r19/r24)。matmul 吞吐表示本身也加宽了:PF 上单列(res 996 个单元格)变成 VF 上的 res r3(51 个单元格)加 4 阶段 matprep 组。

> **特性 —** r4..r7 matprep 阶段承载 `{4,5}/{12,13}/{20,21}/{28,29}` 取值对,即 step-8 斜坡 `{4,12,20,28}`(及其 `+1` 伴随值 `{5,13,21,29}`)。同一个 `{5,13,21,29}` 斜坡也出现在 [`mxu-latency-vf`](mxu-latency-vf.md) 的 `MxuResource` overrun-check 插入中(`AddOverrunCheckReservations`),反映 matprep 吞吐阶段和 MSR overrun-check 端口是同一个逐 K-tile latch 流水线的两个成本模型视图。它们是*不同表中的不同枚举*,不要用其中一个索引另一个。

---

## 延迟数组

### 目的

`latency[instr]` 是指令的流水线深度,即真依赖边权重。VF 构造函数先把数组 memset 为 `0xff`,随后覆盖全部 384 个槽位(默认值不会保留)。

### 直方图

384 个条目有 19 个不同取值:

```text
  1: 148    2: 113    131: 27    121: 25    7: 18    8: 14    6: 8    3: 5
  164: 5    115: 5    114: 4    9: 3    5: 2    4: 2    30: 1    122: 1    0: 1
  36: 1    49: 1

有意义的簇:

  • 1/2(148+113):便宜的 vector/scalar/sync ops。
  • 131 / 121:MXU matmul / matprep 基础延迟(systolic 流水线深度;MxuLatencyTable 保留数组说明这些周期中各 MXU 子端口被占用多少)。
  • 7:matprep;8:result-pop;6EUP push 延迟(instr 0xcc..0xd2;pop instr 0x168 = 1)。
  • 164:transpose-binary;115:reduce;114/122:permute/rotate/broadcast。
  • 36:CCF push;0:一个 MXU-setup ordinal;49:单个 op。

注意 — VF 上的 EUP push→pop 边是延迟 6(push)/ 1(pop),并且 VF 只通过延迟数组为 EUP push 定价;push 行保留网格单元格(ViperfishTarget::VectorEupReservationCycles @0x1d49b060 = 1,全速率)。这与 Pufferfish 不同,后者的 EUP push 还会保留网格端口 r2/r3,并以半速率运行(VectorEupReservationCycles = 2)。重新实现不能假设 VF 上的 EUP push 由网格定价。


Opcode → 网格行

分类器

GetViperfishInstruction @0x1c8a3300 通过跳转表 @0xb43a104(idx = op−1,边界 0x1a9)把 LLO opcode 映射到网格行 Instruction ordinal。大多数 opcode 直接映射(mov ax, IMM16);MXU band 是例外。matmul 分支 @0x1c8a3373 读取二级 latch-mode WORD 表 @0xb43b140(valid-latch mask 0x3ffcc03),把单个 matmul/matprep opcode 扇出到约 0x60 个 band ordinal(VF Instruction 0xd4..0x106 + 0x10b..0x11a)。因此,一个 vmatmul/vmatprep LLO opcode 会变成多个网格行,每个 (opcode, latch_mode/MatmulDataFormat) 对对应一行。

网格外层索引就是已定价行的 opcode(每个已填充行都会解析到一个一致的、由分类器命名的 LLO opcode),但该映射是逐代分类器,而不是原始 opcode 空间。MXU band 的逐 ordinal (opcode, latch_mode) 解码是作为 latch-mode 分类器机制读取的,而不是逐 ordinal 枚举;band 身份(matmul/matprep)、其基础延迟(121/131)和吞吐单元格(r3 {8,16,32}、r4..r7 4 阶段)都是逐字节精确的。

与 MxuLatencyTable 共存

该网格中的 matmul 吞吐单元格(res r3 = 每个 MatmulDataFormat{8,16,32},即 fmt1=8、fmt2=16、fmt6/int8-x8=32;r4..r7 为 4 阶段 matprep 占用)重复了也存在于单独 MxuLatencyTable 保留矩阵中的 matmul-rate 数值。这两个成本表共存并为不同事物定价:该网格为按 Instruction ordinal 键控的 intra-op 微流水线端口占用定价;MxuLatencyTable 为按 MatmulDataFormat/GainLatchMode 修饰符键控的 MXU 子资源占用定价。基础 op 延迟(121/131)在这个延迟数组中;逐 MxuResource 的 hold-cycle 向量在 array<int,19> 中。

注意 — {8,16,32} matmul rate 在两个表中都按字节确认,而吞吐路径读取的是 MxuLatencyTable,不是这个网格。 网格 col-r3 单元格 {8,16,32} 已在 ViperfishPerformance 构造函数 @0x1c8c4840 中按字节锚定(mov DWORD PTR [data+0xc], 8/0x10/0x20,matmul band 中共 51 个单元格)。相同三元组也在 MxuLatencyTable 构造函数 @0x1c8a52c0 中作为每种格式的 {MxuResource 15 (MatmulAccA) → 8/16/32} 独立按字节锚定(发出 key=15, value=8,然后 =16,再 =32 的行)。成本模型的 matmul 吞吐路径 VfCycleTable::GetCyclesForThroughput(CT 0)MxuLatencyTable::GetResourceUsage(0xd4, res 3, 0) 会把 res 3 → array[15] 重映射,并返回该保留单元格;因此被消费{8,16,32} 来自 MxuLatencyTable,而此网格的 r3 保存了一个镜像,matmul/matprep 吞吐路径对 CT-class 0/1/4 读取它。重新实现必须填充两者,但要把 matmul-rate 消费者接到 MxuLatencyTablearray[15]

注意 — 并行的 opcode_produced_register_type 表(@0x223a16c0,byte[461])及其驱动的 convolution-window DMA-level merge gate 是跨代不变的;它们记录在 performance-overview 中,并原样适用于 VF,因为它们按原始 LLO opcode 键控,而不是按逐代 Instruction ordinal 键控。


资源数量上下文

VF 的 28 列位于跨代演进中:

GenCodenameResource colslatency rowsgrid cellspopulated rowsEUP push latXlu deposit col
PFPufferfish203362651807res 6(conflict-penalty)
VFViperfish283843781286res 0x0eGetXluPathReservation
GLGhostlite3147635813213/14res 0x0f
GF6acc60406314652859212res 0x10

该枚举从 PF → VF → GL 加宽为 20 → 28 → 31:VF 增加了 4 阶段 matprep 组(r4..r7)+ transpose-binary 结果子阶段(r15..r17)+ CCF push/pop;GL/GF 增加了 BF16-EUP AndPop FIFO 深度 + BarnaCore 尾部。matmul 吞吐端口发生移动(PF 单列 res 9 → VF 中 4 阶段组上的 res 3),Xlu 写入列跟随 MXU 几何变化(res 6 → 0x0e → 0x0f → 0x10)。


相关组件

名称关系
performance-overview网格家族对象布局、共享的 GetResourceUsage 读取路径,以及 opcode_produced_register_type gate
mxu-latency-vf与此网格共存的单独 MxuLatencyTable array<int,19> 保留矩阵
resource-enum23 槽逐 bundle ResourceVector,不同于此处的 28 列 Resource 枚举
performance-pf / -gl-ghperf / -gf-ghperf20 / 31 / 31 列同族网格
slot-mxumatmul/matprep band 行定价的 LLO MXU opcode

交叉引用