Performance 系列概览
地址适用于来自 libtpu-0.0.40-cp314 wheel 的 libtpu.so。其他版本会有所不同。
摘要
每一代 TensorCore 都通过按代划分的 Performance 对象为其指令定价:一个扁平的按指令延迟数组,加上一个二维 Instruction × Resource 占用网格。这个网格是 libtpu 中类似 LLVM SchedMachineModel 的结构,也就是 ProcResource/WriteRes 表;不同之处在于它不是声明在 .td 文件中,而是通过读取填充它的构造函数重建出来的。给定一条指令的行和一个资源列,GetResourceUsage(instr, res) 返回该指令占用该微流水线端口的周期数;GetLatency(instr) 返回该指令的流水线深度。这两种读取为调度器的吞吐量模型和依赖模型提供输入。
六个代号中有两个不同的 C++ 类族实现这个思路。扁平族,即 Jellyfish/Dragonfish(platforms_deepsea::jellyfish::isa::Performance),把成本存储在一个 3584 字节的内联 POD 中,并通过 offset LUT 将一条指令解析到一个单元;另有一个 CycleTable::GetResource LUT 将每条指令映射到 7 个资源列之一;这里没有 2D 网格,也没有 GetResourceUsage。网格族,即 Pufferfish、Viperfish,以及两个 GhostlitePerformance 变体(Ghostlite/v6e,由 GlcCycleTable 构建;6acc60406/v7x,由 GfcCycleTable 构建;二者内部均为 “GhPerf”),会在堆上分配一个延迟数组和一个由 std::vector<int> 行组成的 2D 网格,四者都由字节完全相同的 GetResourceUsage 读取。架构在 Pufferfish 发生了变化:内联 POD 加 offset LUT 的模型让位于后续产品线使用的堆网格模型。
本页是该系列的框架性参考。它记录共享对象布局、GetResourceUsage/GetLatency 读取路径、资源数量演进(7→7→20→28→31→31)、opcode 如何通过按代的 Get<Gen>Instruction 分类器到达网格行,以及卷积成本模型读取的 matrix-result/Xlu 存放列(conv 的 R[2] 单元)。各代网格,也就是其填充单元、延迟数组和逐列命名,都在各自页面中说明;本页解释那些页面导出的坐标轴。并行的按 opcode 元数据表 opcode_produced_register_type 会按目标产生的寄存器类别对每个 LLO opcode 分类;它记录在这里,是因为它与网格所依赖的是同类按 opcode 查找,并且卷积窗口成本路径会基于它对 DMA 层级合并进行门控。
对于重新实现,契约是:
- 两个族的
Performance对象布局:扁平内联 POD(JF/DF)与堆延迟数组 + 堆 2D 网格(PF/VF/GL/GF),包含精确大小和字段偏移。 GetResourceUsage(instr, res)与GetLatency(instr)读取路径,包括两个边界检查和 24 字节行步幅。- 资源数量演进,以及按代的 Xlu/matrix-result 存放列(PF res 6 → VF res 0x0e → GL res 0x0f → GF res 0x10),包括读取它的
GetXluPathReservation访问器。 - LLO opcode 如何到达网格行(
Get<Gen>Instruction分类器,包括 MXU matmul/matprep latch-mode 扇出),以及吞吐量单元如何与独立的MxuLatencyTable共存。 opcode_produced_register_type分类体系,以及它驱动的卷积窗口 DMA 层级合并门控。
| 扁平族 | platforms_deepsea::jellyfish::isa::Performance — PerformanceJf @0x1d4930c0, PerformanceDf @0x1d493060 |
| 网格族 | Pufferfish/Viperfish/GhostlitePerformance(Ghostlite v6e 通过 GlcCycleTable,6acc60406 v7x 通过 GfcCycleTable;二者均为 “GhPerf”) |
| 扁平读取路径 | JfCycleTable::GetCyclesForThroughput @0x1c89dce0; CycleTable::GetResource LUT @0xb438aec |
| 网格读取路径 | GhostlitePerformance::GetResourceUsage @0x1c8d3700; PF @0x1c8c3880; VF @0x1c8cbc40 |
GF (6acc60406) ctor | GhPerf ctor @0x1c8d3740(未命名/符号错误)— latency new 0x744 (465 int32), grid new 0x2b98 (465 × 24), 行宽 31 |
| GL (Ghostlite) ctor | GhostlitePerformanceC1 @0x1c8cbc80 — latency new 0x770 (476), grid new 0x2ca0 (476 × 24), 宽 31 |
| 资源演进 | 7 (JF) → 7 (DF) → 20 (PF) → 28 (VF) → 31 (GL) → 31 (GF) |
| Xlu 存放列 | PF res 6(冲突惩罚)· VF res 0x0e · GL res 0x0f · GF res 0x10 — 对 matrix-result ops,cell = 4 |
| 按 opcode 的 reg-type | xla::jellyfish::internal::opcode_produced_register_type @0x223a16c0 — byte[461], 值 0–4 |
两种 Performance 架构
目的
Performance 回答调度器针对每条指令需要的两个问题:它的流水线有多深(GetLatency,用于真实依赖边)以及它占用每个功能单元端口多少周期(GetResourceUsage,用于累计每个 bundle 的吞吐压力)。两种架构回答同样的问题,但答案的存储方式不同;变化点 Pufferfish 标记的是实际的硅片时代边界,而不仅是重构:最老的几代(1-MXU Jellyfish、2-MXU Dragonfish)需要定价的指令很少,扁平的每指令单元 LUT 就足够;从 Pufferfish 开始,更宽的 MXU/EUP 流水线需要显式的逐端口网格。
扁平族(Jellyfish, Dragonfish)
JF/DF 使用一个固定的 0xe00 字节(3584 B)内联 POD,即 platforms_deepsea::jellyfish::isa::Performance。没有单独的延迟数组,也没有 2D 网格;相反,延迟模型和吞吐量单元都作为零散的 int32 槽位存在同一个缓冲区中,并由按指令的 offset LUT 选择正确槽位。
struct Performance { // 0xe00 bytes, built by CreateTensorCore @0x1d4927e0
void* vtable; // +0x00
u64 device_id_lo; // +0x08
u32 device_id_hi; // +0x10
bool is_tensorcore; // +0x14 (=1 from CreateTensorCore)
int32 buf[890]; // +0x18 .. +0xdf8 ; default = 0x7fffffff (INT_MAX) sentinel
};
```text
基类 ctor `@0x1d492900` 将 `buf` memset 为哨兵值 `0x7fffffff`(注意:不是 `0xffffffff`,这与堆族不同)。随后 `PerformanceJf @0x1d4930c0` 通过复制预烘焙的 16 字节 `.rodata` 常量块来覆盖已填充单元(例如将 `{4,105,7,92}` 写入 `+0x18`,将 `{88,8,4,1}` 写入 `+0x28`);116 次存储操作覆盖 890 个槽位中的 419 个,其余 471 个保留哨兵值。`PerformanceDf @0x1d493060` 是 `PerformanceJf` 加一次 vtable 替换和正好 **一次** quadword 存储,即 `[+0x28] = 0xd00000042`,使 `[+0x28]=66`(matmul base)且 `[+0x2c]=13`(matprep base)。DF 与 JF 的差异只有这两个 `int32` 单元,没有其他差异。
读取路径是 `JfCycleTable::GetCyclesForThroughput(Instruction) @0x1c89dce0`:
```c
function JfCycleTable_GetCyclesForThroughput(table, instr): // @0x1c89dce0
// valid set = {0,5,11,18,19,20,21,22,23,24,25,26,27,28,31,32} (16 of 33)
if (instr < 0x21) && ((0x19FFC0821 >> instr) & 1):
return Performance[ offsetLUT[instr] ] // offsetLUT @0xb438b70 (33 × i64)
return 1 // unpriced → default 1 cycle配套的 CycleTable::GetResource(instr) @0x1c89ce20 是一次普通 LUT 读取,即 [LUT@0xb438aec + instr*4],把 33 个 CycleTable::Instruction ordinal 中的每一个映射到 7 个资源列之一(0–6)。消费者 AccumulateInstructionUsage @0x144fd720 将 (GetResource(I), GetCyclesForThroughput(I)) 累加到 23 槽 per-bundle ResourceVector 的前 7 个槽中。
注意 —
Performance::Instruction轴不是原始 LLO opcode。CycleTableInstruction @0x1c89ca80只对 MXU 频段分类,即 matmul opcode0x8d..0x96通过latch_mode(),matprep/matpush0x9b..0xa5通过matmul_data_format(),进入 33 值 enum;所有其他 opcode 都通过其他CycleTable路径定价。JF/DF 中 MXU 吞吐量单元统一为 8 cycles;JF→DF 加速存在于 matmul base latency(88→66)中,而不是吞吐量中。
网格族(Pufferfish, Viperfish, Ghostlite, 6acc60406)
PF/VF/GL/GF 在堆上分配两个对象:一个按 Instruction 索引的扁平延迟数组,以及一个每条指令一行的 2D 网格,每行是 N 个资源宽的 std::vector<int>。四者的对象布局完全相同,只有宽度不同:
struct GridPerformance { // built by the per-gen ctor (table below)
int32* latency; // +0x00 ; new (rows*4), memset 0xff
u64 latency_size; // +0x08 ; = rows
u64 latency_cap; // +0x10 ; = rows
vector<int>* grid; // +0x18 ; new (rows*24), one vector<int> per row
u64 grid_outer_count; // +0x20 ; = rows (read as the GetResourceUsage outer bound)
u64 grid_outer_cap; // +0x28 ; = rows
// each row (24 B std::vector<int>): { int* data (new N*4, zero-init), size=N, cap=N }
};
```text
| Gen | Ctor | Latency alloc | Rows | Grid alloc | Row width (resources) | Populated cells |
|---|---|---|---|---|---|---|
| PF (Pufferfish) | `@0x1c8be080` | `new 0x540` | 336 | `new 0x1f80` (336 × 24) | `new 0x50` = 20 | 265 |
| VF (Viperfish) | `@0x1c8c4840` | `new 0x600` | 384 | `new 0x2400` (384 × 24) | `new 0x70` = 28 | 378 |
| GL (Ghostlite, v6e) | `@0x1c8cbc80` | `new 0x770` | 476 | `new 0x2ca0` (476 × 24) | `new 0x7c` = 31 | 358 |
| GF (`6acc60406`, v7x) | `@0x1c8d3740` | `new 0x744` | 465 | `new 0x2b98` (465 × 24) | `new 0x7c` = 31 | 285 |
延迟数组被 memset 为 `0xff`(255)。在 PF/VF 上,之后每个槽位都会被覆盖,因此默认值不会保留。在 GL/GF 上,只有已定价指令(约 92 个 GF 行 / 约 132 个 GL 行)会被写入;其余保留 `0xff = 255`,这些未定价指令会从后备的 `CycleTable::GetResource` 路径取得成本,而不是从网格中取得。
> **特例 —** GL ctor `@0x1c8cbc80` 带有干净的符号 `_ZN3xla9ghostlite20GhostlitePerformanceC1Ev`,但 GF ctor `@0x1c8d3740` 在二进制中符号错误;它是由 `GfcCycleTable` 分配的 GhPerf 变体,结构上是一个 `GhostlitePerformance`(相同布局、31 宽行),但指令集是 465 行而不是 476 行。两者是不同的构造函数,具有不同的单元值和不同的 base latencies(GF EUP/transcendental 212/204,GL 为 192/182),不是共享实例。
---
## GetResourceUsage 读取路径
### 目的
`GetResourceUsage(instr, res)` 是吞吐量模型和 Xlu-reservation 访问器读取网格单元时经过的唯一访问器。它在 PF/VF/GL/GF 中字节完全相同,即同样的两个边界检查和同样用 `lea` 计算的 24 字节行步幅,因此一个说明覆盖所有四个网格世代。
### 算法
```c
function GhostlitePerformance_GetResourceUsage(perf, instr, res): // @0x1c8d3700 (GF/GL twin)
if perf.grid_outer_count <= instr: // [perf+0x20] ; outer bound (e.g. 465 GF / 476 GL)
trap() // ud2 / BUG
row_base = perf.grid // [perf+0x18]
row = row_base + instr*24 // lea rax+rax*2 (×3), then ×8 → 24-byte stride
if row.size <= res: // [row+8] ; inner bound = row width (31 / 28 / 20)
trap()
return row.data[res] // *(int*)([row+0] + res*4) = grid[instr][res]GetLatency(instr) 是更简单的兄弟路径,即由 [perf+0x8] 作为边界的 latency[instr],返回该指令的流水线深度(调度器把真实依赖边提高到的值)。GetResources() 返回 kResources 遍历顺序(一个 .rodata 字节数组,列出按填充顺序排列的资源索引:GF @0xb43cde3,GL @0xb43cdc4,VF @0xb43cda8,PF @0xb43cd94),GetResourceLatency @0x1c8b1e60 会迭代它来汇总一行。
易错点 — OUTER 索引是按代的
Performance::Instruction,不是原始 LLO opcode。每个已填充行的索引都会解析到一致的 LLO opcode(因此对于已定价行,该轴就是 opcode),但映射是按代分类器(GetGhostliteInstruction @0x1c8b1740、GetViperfishInstruction @0x1c8a3300、GetPufferfishInstruction @0x1c8a1fe0),且 MXU 频段会通过次级 latch-mode WORD 表把单个 matmul/matprep opcode 扇出成多个 ordinal。直接用 LLO opcode 索引网格的重新实现会误读每个 MXU 行。
matrix-result / Xlu 存放列
一个网格列是特殊的:它保存卷积成本模型作为 R[2] 项读取的 matrix-result(Xlu)吞吐量。该列索引会随 MXU 几何结构跨代变化:PF 上是 res 6,VF 上是 res 0x0e,GL 上是 res 0x0f,GF 上是 res 0x10;每个 matrix-result/cmem/transpose-result 指令的已填充单元都是 4。专用访问器正是读取这一列:
function LatencyTableGhostlite_GetXluPathReservation(this, value): // @0x1c8b21c0 (GL)
if value.opcode == 0x8b: // kVectorSetPermutePattern, handled directly
return 3 * (is_transpose(value) ? 1 : 0) + 1
instr = GetGhostliteInstruction(value)
return GhostlitePerformance::GetResourceUsage(this.perf /*[this+0x1d0]*/, instr, 15) // res 0x0f
```text
VF 访问器 `@0x1c8a3200` 形状相同,只是 `res = 0x0e`,并对 permute-pattern opcode 硬编码 `8`/`1`。PF 完全没有直接的 Xlu 网格列:它的 conv/transpose Xlu 成本由单独的 `XluConflictPenaltyTable`(`XposeXLUReservationLatency @0x1c8a13e0`)定价,这是按代的结构差异。Xlu 单元(=4)是 `6acc60406` conv 成本三元组中缺失的数值,另外两项来自 [MXU reservation matrix](mxu-latency-gf.md):`R[0]` matpush {2 bf16 / 4 fp8},`R[1]` matmul {4 bf16 / 8 fp8},`R[2]` Xlu {4}。
---
## 资源列
### 目的
网格的 INNER 轴是按代的 `Performance::Resource` enum:也就是 op 内部 EUP/MXU/Xlu 微流水线预留端口。二进制中没有任何一代带有 `Resource::ToString`,因此这些列按功能命名:通过读取哪类 LLO 指令会把周期存入每一列(OUTER 索引为 opcode),并用两个具名访问器 `GetXluPathReservation` 和 `GetResourceLatency` 锚定。这些名称在*含义*上(每列预留哪个物理端口)足以重新实现,但不是字面符号名。
> **注意 —** 按代的 `Performance::Resource` enum(20/28/31 列)不同于 23 槽 per-bundle `ResourceVector`([Resource Enum](resource-enum.md))。网格为 op 内部微流水线阶段占用定价;`ResourceVector` 是高层成本模型存入的 per-bundle 功能单元累加器。`kResources` 字节数组给出的是网格列遍历顺序,而不是 `ResourceVector` 槽顺序。
### 列族和演进
这些列会分组成可识别的频段,并随着 MXU/EUP 流水线跨代增长而变宽。matmul-throughput 表示是最清晰的信号:PF 上是单列(res 9,96 个单元);VF 上是一个 51 单元列加一个 4 阶段 matprep 组(res 3 + res 4..7);GL/GF 上则有更宽的 EUP-AndPop FIFO 深度和 BarnaCore 尾部。
| Band | PF cols | VF cols | GL/GF cols | Role |
|---|---|---|---|---|
| 地址 / load-store / sync | r0..r2 | r0..r1, r21 | r0..r2 | DMA/address generation, scalar store, sync-flag |
| EUP transcendental-prep | r2..r3 | (via latency) | r3..r6 (GF) | 按 dtype 的 prep 阶段(携带 {4 bf16 / 8 fp8} 量级) |
| MXU matmul / matprep throughput | r9..r12 | r2..r8 | (EUP-prep cols) | 按格式的 matmul/matprep 周期单元 |
| EUP-result-pop FIFO | r5 | r9..r11 | r8..r11 (GF) | EUP transcendental 之后的 drain 阶段 |
| Cross-lane / transpose result | r7, r11..r13 | r12..r17 | r12..r15 (GF) | permute/rotate/broadcast/transpose result 阶段 |
| **Xlu / matrix-result deposit** | **r6** | **r0x0e** | **r0x0f / r0x10** | conv 的 `R[2]` 单元(对 matrix-result ops 为 =4) |
| Reduce result | r14..r15 | r18 | — | `kVector*ReduceF32` result 阶段 |
| Pack / extract / U64 | — | — | r21..r24 (GF) | `kVector{Xor,PseudoPack,MultiplyU64,Extract}` 阶段 |
| Tail (CCF / RNG / sync-wait / BarnaCore) | r16..r19 | r19..r27 | r25..r30 (GF) | CCF push/pop, PRNG, sync-flag wait, BarnaCore scalar-sync |
完整的逐单元导出位于各代页面:[JF/DF](performance-jf-df.md)(扁平 7 列模型)、[PF](performance-pf.md)(20)、[VF](performance-vf.md)(28)、[GL](performance-gl-ghperf.md) 和 [GF](performance-gf-ghperf.md)(各 31)。
---
## opcode_produced_register_type
### 目的
与按代的 `Performance::Instruction` 键并行,libtpu 带有一个单一的、跨代不变的按 opcode 字节表,用于按目标产生的寄存器类别对每个 LLO opcode 分类。它记录在本页,是因为它与网格所依赖的是同类按 opcode 查找,并且卷积窗口成本路径用它来决定两个连续窗口轴是否可以合并为一个 DMA 描述符层级;这个决定会直接改变 DMA fragment 数量,从而改变成本模型应用的效率乘数。
### 表及其分类体系
`xla::jellyfish::internal::opcode_produced_register_type @0x223a16c0` 是 `.data` 中的 `byte[461]`(文件偏移 `0x21fa16c0`),按 LLO opcode `0..0x1cc` 索引。边界 `0x1cd = 461` 同时匹配 `ComputeDmaLevels` 范围检查和 `LloOpcodeName`。每个字节是目标寄存器类别:
| Value | Class | Count | Examples |
|---|---|---|---|
| 0 | 无目标寄存器 | 216 | `kEvent`, stores, pushes, fences, barriers, sync-flag, DMA-issue |
| 1 | Predicate 寄存器 | 11 | `kPredicate*`, `kScalarCompare`, `kScalarAddCarryU32` |
| 2 | Scalar 寄存器 (SREG) | 58 | `kScalarAddressCalculation`, `kAllocationAddress`, `kScalarLoad`, `kScalarMultiplyU32`, `kRelocatableConstant` |
| 3 | Vector-mask 寄存器 | 19 | `kVectorMask*`, `kVectorCompare`, `kVectorCreate*Mask` |
| 4 | Vector 寄存器 | 157 | 大部分 vector ALU/load/result ops |
该分类体系是确定性的:它来自把字节表与 `LloOpcodeName::opcode_name @0x21ccfef0`(461 个 `char*`,通过 `R_X86_64_RELATIVE` 重定位解析)交叉连接,并检查具名 opcode 集。直方图 `{0:216, 1:11, 2:58, 3:19, 4:157}` 总和为 461。
### DMA 层级合并门控
`ComputeDmaLevels`(`@0x1c86b1a2..1d8`)读取 `window_desc+0xe0[axis]` 处的逐轴 stride-level 操作数(一个 `LloValue*`),取得其 opcode,并根据寄存器类型门控连续轴合并:
```c
opcode = WORD[stride_level_operand]
if opcode >= 0x1cd: fatal() // bound check, same 461
regtype = opcode_produced_register_type[opcode]
if regtype != 2 && regtype != 4: // cmp 2 je / cmp 4 jne break
break // contiguity break → extra DMA descriptor level
// else (Scalar or Vector) the axes may merge if KnownEq(stride) holds产生 Scalar(address/index,类型 2)或 Vector(类型 4)值的 stride 操作数可以将一个窗口轴合并到单个 DMA 层级中;predicate(1)、mask(3)或 no-result op(0)会强制连续性中断,也就是额外的 DmaLevel、更大的 fragment 乘积和更差的效率乘数。典型的 conv-window stride 操作数(kScalarAddressCalculation @0x86、kAllocationAddress、kParameterAddress、kScalarLoad、kScalarMultiplyU32、kRelocatableConstant)都是类型 2,因此可合并;不寻常的 mask 类型 stride 操作数(例如 kVectorMaskMove @0x199,类型 3)会切碎 DMA。
注意 —
kVectorReadIar @0x1是类型 4,kBarnaCoreVectorStore @0x1cc(最后一个有效 opcode)是类型 0;类型 4 覆盖大部分高 opcode 范围(0x102..0x127、0x13b..0x166、0x180..0x1a2、0x1c9..0x1cb)。配套的opcode_info @0x223a1320(461 × 2 字节)保存第二个按 opcode 的 info word,与寄存器类型配对;其字段含义尚未解码。
系列总结
| Gen | Codename | TpuVersion | Performance model | Resource cols | Xlu deposit column |
|---|---|---|---|---|---|
| JF | Jellyfish | 0 (v2) | flat inline POD 0xe00 + offset LUT | 7 | (flat; res 2 MXU-result band) |
| DF | Dragonfish | 1 (v3) | = JF + 2 cells | 7 | (flat; = JF) |
| PF | Pufferfish | 2 (v4) | heap latency[336] + grid 336×20 | 20 | res 6 (conflict-penalty) |
| VF | Viperfish | 3 (v5p) | heap latency[384] + grid 384×28 | 28 | res 0x0e (GetXluPathReservation) |
| GL | Ghostlite | 4 (v6e) | heap latency[476] + grid 476×31 | 31 | res 0x0f |
| GF | 6acc60406 | 5 (v7x) | heap latency[465] + grid 465×31 | 31 | res 0x10 |
这些网格中的 matmul/matprep 吞吐量单元与一个独立的按代 MxuLatencyTable(modifier × MxuResource reservation matrix)共存;两个成本表在按 dtype 的吞吐量整数上保持一致(GF EUP-prep 列携带与 MxuLatencyTable matmul 单元相同的 {4 bf16 / 8 fp8} 量级)。reservation matrix 及其计算的 back-to-back stall 记录在 MXU Latency Overview 和 MxuOpHoldIssues Stall 下。
交叉引用
- Resource Enum — 23 槽 per-bundle
ResourceVector,不同于按代的Performance::Resource微流水线列 - Performance: JF/DF — 扁平 7 列内联 POD 模型,以及 {88,8,4,1}→{66,13,4,1} 的 JF→DF delta
- Performance: PF — 20 列 Pufferfish 网格及其 conflict-penalty Xlu 定价
- Performance: VF — 28 列 Viperfish 网格和
GetXluPathReservationres 0x0e - Performance: GL (GhPerf 476×31) — Ghostlite v6e 网格,Xlu deposit res 0x0f
- Performance: GF (GhPerf 465×31) —
6acc60406v7x 网格,Xlu deposit res 0x10 - MXU Latency Overview —
MxuResourceenum,以及与网格共存的 reservation-matrix 成本模型 - MxuOpHoldIssues Stall — back-to-back matmul stall recurrence 和 balancing gate
- MXU Latency: GF —
6acc60406MXU reservation matrix 和 convR[0]/R[1]/R[2]三元组 - MXU Slot — 资源列预留的物理 MXU 子单元