资源枚举
地址适用于来自 libtpu-0.0.40-cp314 wheel 的 libtpu.so。其他版本会有所不同。
摘要
TPU 成本模型把一个 VLIW bundle 建模为一组功能单元占用量向量。ResourceVector 是一个包含 23 个 double 槽位的扁平数组,每个槽位对应一个抽象硬件资源:两条 MXU 管线(matmul 发射与 gain push)、跨 lane 单元、两条专用加一条任意向量 ALU lane、向量 EUP / load / store 端口、四个内存传输项、六条 ICI ring 链路,以及三个 SparseCore 引擎。每条 LLO 指令都会把它的吞吐周期沉积到 CycleTable::GetResource 命名的槽位中,而 bundle 的发射成本是对整个向量做一次结构化归约:MaxResourceCycles。本页还原 enum 值→名称表、各代的槽位数量,以及该归约的重叠模型。
读者不应把它和调度器的 ResourceType enum 混淆。它们是同一个二进制里的两种不同“资源”抽象,混在一起是最核心的陷阱。成本模型的 Resource(本页,23 个槽位,嵌套在 ResourceVector 中的 enum)衡量的是周期权重,即某个单元忙碌多少周期。调度器的 ResourceType(ResourceType 分类,47 个 ID,一个 AsyncTracker enum)衡量的是并发限制,即某类 async collective 可以同时发射多少个。它们在语义上有重叠(都命名了六条 ICI 链路),但它们是不同的 enum,具有不同的取值空间、打印路径和消费者。本页记录成本模型的 Resource;与 ResourceType 的对比放在末尾。
名称来自一个地方:ResourceVectorToString @ 0x1c89bde0 按物理偏移顺序读取前 22 个 double,并用单个 printf 模板格式化,因此字符串顺序就是 enum 顺序。第 23 个槽位(R[22])是真实存在的,可由 Acc 写入,也会被 MaxResourceCycles 读取,但 ToString 从不打印它,它在二进制中没有名称字符串。
重新实现时的契约是:
- 23 槽位
Resourceenum 的值→名称→偏移表,以及把数量固定为 23 的Acc边界检查。 MaxResourceCycles归约:哪些槽位重叠(普通 MAX),哪些按 50% 混合(vector-ALU 端口均衡),哪些串行化(内存组)。- 各代的槽位数量(PF=20、VF=28、GL=31,来自具名
kResources符号;存在第四个 31 槽位表,但其代际归属未确认),以及更宽的代如何把 enum 扩展到R[22]之后。 - 代际不变的
CycleTable::GetResourceop→slot 表。 - 此 enum 与调度器
ResourceTypeenum 的边界。
| 枚举 | xla::jellyfish::ResourceVector::Resource(嵌套) |
| 槽位数量 | 23(Acc 边界 < 0x17);ToString 命名 22 个 |
| 向量布局 | 23 × double(+0x00..+0xb0)+ operand-bytes map +0xb8 + output-bytes map +0xd8 |
| 名称来源 | ResourceVectorToString @ 0x1c89bde0(format string @ 0x87f40ea) |
| 沉积 | ResourceVector::Acc(Resource, double) @ 0x1c89adc0 |
| 归约 | ResourceVector::MaxResourceCycles() @ 0x1c89b9e0 |
| Op→slot 映射 | CycleTable::GetResource(Instruction) @ 0x1c89ce20(table @ 0xb438aec) |
| 各代数量 | PF=20, VF=28, GL=31(具名符号);GF=31(推断,UNVERIFIED)(kResources permutations) |
23 槽位资源枚举
名称
ResourceVectorToString @ 0x1c89bde0 按物理偏移顺序把前 22 个 double 传给 .rodata 0x87f40ea 处的格式字符串。反编译逐字节确认了完整模板:
RV[Matpush: %.0f, Matmul: %.0f, Xlu: %.0f, VectorAlu0: %.0f, VectorAlu1: %.0f,
VectorAluAny: %.0f, VectorEup: %.0f, VectorLoad: %.0f, VectorStore: %.0f,
MemXferInputLatency: %.0f, MemXferInputBandwidth: %.0f, MemXferOutputLatency: %.0f,
MemXferOutputBandwidth: %.0f, IciYPlus: %.0f, IciYMinus: %.0f, IciXPlus: %.0f,
IciXMinus: %.0f, IciZPlus: %.0f, IciZMinus: %.0f, ScScs: %.0f, ScTile: %.0f,
ScCollective: %.0f]
```text
由于每个 `double` 都是按顺序读取的 8 字节槽位,enum 值等于数组索引,也等于 `offset / 8`:
| idx | offset | name | 功能单元 |
|---|---|---|---|
| R[0] | +0x00 | `Matpush` | MXU gain/latch-push(matpush)管线 |
| R[1] | +0x08 | `Matmul` | MXU matmul-issue(matprep)管线 |
| R[2] | +0x10 | `Xlu` | 跨 lane 单元(matres read / EUP / transcendental staging) |
| R[3] | +0x18 | `VectorAlu0` | vector ALU lane 0(专用) |
| R[4] | +0x20 | `VectorAlu1` | vector ALU lane 1(专用) |
| R[5] | +0x28 | `VectorAluAny` | vector ALU “任意”(负载均衡)lane |
| R[6] | +0x30 | `VectorEup` | vector extended-precision unit |
| R[7] | +0x38 | `VectorLoad` | vector load 端口 |
| R[8] | +0x40 | `VectorStore` | vector store 端口 |
| R[9] | +0x48 | `MemXferInputLatency` | input-DMA 启动延迟项 |
| R[10] | +0x50 | `MemXferInputBandwidth` | input-DMA 每字节带宽项 |
| R[11] | +0x58 | `MemXferOutputLatency` | output-DMA 启动延迟项 |
| R[12] | +0x60 | `MemXferOutputBandwidth` | output-DMA 每字节带宽项 |
| R[13] | +0x68 | `IciYPlus` | ICI ring 链路 +Y |
| R[14] | +0x70 | `IciYMinus` | ICI ring 链路 −Y |
| R[15] | +0x78 | `IciXPlus` | ICI ring 链路 +X |
| R[16] | +0x80 | `IciXMinus` | ICI ring 链路 −X |
| R[17] | +0x88 | `IciZPlus` | ICI ring 链路 +Z |
| R[18] | +0x90 | `IciZMinus` | ICI ring 链路 −Z |
| R[19] | +0x98 | `ScScs` | SparseCore SCS sequencer |
| R[20] | +0xa0 | `ScTile` | SparseCore tile-execute core |
| R[21] | +0xa8 | `ScCollective` | SparseCore collective engine |
| R[22] | +0xb0 | (未命名;有效) | 保留/scalar,由 `MaxResourceCycles` 读取、由 `Acc` 写入;从不打印 |
`ResourceVector::Acc(Resource, double)` @ `0x1c89adc0` 会用触发陷阱的 `ud1` 对 `resource >= 0x17`(23)做边界检查,然后按 8 字节步长执行 `vector[resource] += cycles`。因此 23 个槽位都是有效的(R[0..22]);打印路径只覆盖前 22 个。整个向量主体是 `0xf8` 字节:23 个 double(`+0x00..+0xb0`)、一个 operand-bytes `flat_hash_map`(`+0xb8`)和一个 output-bytes `flat_hash_map`(`+0xd8`)。
> **陷阱 —** `R[22]` 是真实但匿名的。`Acc` 接受它(边界是 `< 23`,不是 `< 22`),`MaxResourceCycles` 也会在 plain-MAX 组中读取 `[+0xb0]`,但 `ResourceVectorToString` 在 22 个参数处停止。只根据 printf 模板把向量定为 22 个槽位的重新实现者,会在第一次沉积到槽位 22 时发生缓冲区越界,并在归约中静默丢掉一个周期项。应按 23 个槽位定大小。它的语义角色(可能是 scalar-sequencer / reserved count)没有字符串证据(MEDIUM)。
### MaxResourceCycles 归约
`MaxResourceCycles` @ `0x1c89b9e0` 不是扁平最大值,而是在槽位上叠加三条规则;这种叠加正是成本模型重叠假设的核心:
```c
function MaxResourceCycles(rv): // sub_1C89B9E0
a = rv[3]; b = rv[4]; c = rv[5] // VectorAlu0, VectorAlu1, VectorAluAny
if c > 0: // (A) 50% port-balance blend
if a > b: d = min(a-b, c); c -= d; b += d // fill the less-busy dedicated lane
elif b > a: d = min(b-a, c); c -= d; a += d
c *= 0.5 // const @0xa2df5c8
a += c; b += c
vec_alu = max(a, b)
mem = rv[9] + rv[10] + rv[11] + rv[12] // (B) MemXfer serial sum
acc = max(vec_alu, mem)
for r in {0,1,2,6,7,8, 13..22}: // (C) plain MAX (independent units overlap)
acc = max(acc, rv[r])
return acc三组规则:
- (A)
{R[3], R[4], R[5]}上的 50% 混合:双 lane vector ALU。“Any” lane 工作(R[5])会在两条专用 lane(R[3]、R[4])之间负载均衡;无法完美均衡的剩余部分按 50% 重叠(0.5常量)。这建模的是 2-lane ALU,而不是 XLU 混合。 - (B)
{R[9..12]}上的串行求和:内存子系统。Input latency + input bandwidth + output latency + output bandwidth 会相加:一次传输的启动、载荷、store 的启动以及它的载荷按顺序发生。 - (C) 其他所有项做普通 MAX,包括
R[0]=Matpush、R[1]=Matmul和R[22]。
怪癖 —
Matpush和Matmul位于 plain-MAX 组中,因此一个 bundle 中背靠背的 MXU 操作会重叠(MXU 是流水化的)。唯一串行化的是内存传输(R[9..12])。把 matmul 串行化的朴素成本模型会在每一代上大幅高估 MXU-bound bundle。应相信 MAX。
Add / Scale:摊销 DMA 模型
另外两个归约处理循环缩放。Add(@ 0x1c89b820)会对两个 DMA latency 槽位 {R[9], R[11]} 做 MAX 合并,而其他每个槽位都 ADD:跨合并后的子向量只支付一次 DMA 启动成本,但移动的字节数会累积。ScaleResource(@ 0x1c89b6a0)在把单次迭代 bundle 乘以 trip count 时跳过同样两个 latency 槽位:带宽和计算线性缩放,DMA 启动不缩放。结果是一个干净的摊销启动模型:latency 只付一次,bandwidth 按迭代支付。循环 emitter 通过 GetSubset 利用这一点(prologue subset 只保留 input-DMA 槽位,tail subset 保留 compute + output-DMA)。循环组合见 Bundle-Aware Cost。
Op→Slot 映射:CycleTable::GetResource
每条 LLO 指令的吞吐周期都会沉积到一个具名槽位中。op→slot 映射是一个代际不变的扁平表查找:CycleTable::GetResource(Instruction) @ 0x1c89ce20 字面上就是对 .rodata 0xb438aec(每项 4 字节)执行 return table[Instruction],不是逐代 virtual。Instruction 值是 LLO opcode 空间折叠后的约 33 个 bucket(CycleTable Family)。
CycleTable::Instruction family | 示例 bucket | → Resource 槽位 |
|---|---|---|
| matprep(matmul-issue) | 0..4(bf16 / fp8 / int8 matprep) | R[1] Matmul |
| gain-latch / push | 5..16(bf16/int4/fp8 latches, incl. transposed) | R[0] Matpush |
| matrix-result read + EUP + sin/cos | 23, 27, 28, 29, 30, 31(matres TC/0/1, eup0/1, sincos) | R[2] Xlu |
| input rotate(RotIn/RotOut) | 18, 19 | R[4] VectorAlu1 |
| shuffle / permute | 20 | R[3] VectorAlu0 |
| broadcast / reduce / cross-lane / tan | 21, 22, 25, 32 | R[5] VectorAluAny |
| extended-precision / lane-compare | 17, 24, 26 | R[6] VectorEup |
RecordHloCycles @ 0x130bbfe0 把这些连接起来:r = GetResource(Instruction)(具名槽位,代际不变),c = vtable[+0x10](Instruction)(逐代 GetCyclesForThroughput),然后 Acc(r, c)。内存 op 绕过这个表,cost_model_util::RecordMemXfer* 家族会直接沉积到 R[9..12]。
注 — rotate/shuffle/broadcast/reduce 家族恰好分布到
MaxResourceCycles的 50% 混合组所归约的三个VectorAlu槽位(R[3]/R[4]/R[5])。这是有意设计的:shuffle 固定到VectorAlu0,rotate 固定到VectorAlu1,对称的 “any” op(broadcast/reduce/cross-lane/tan)进入VectorAluAny,让混合逻辑可以对它们做负载均衡。
各代槽位数量
23 槽位 enum 是 TensorCore ResourceVector 的打印表面,但逐代的 Performance::GetResources()::kResources 数组声明了每一代实际填充多少槽位,以及按什么迭代顺序填充。每个 kResources 都是连续范围 [0..N-1] 的一个 1 字节/项排列,其中 N 是该代的资源数量。前三行按字节锚定到具名符号(nm -C 会把每个地址解析到一个 …Performance::GetResources() const::kResources 符号);解码后的字节恰好都是 [0..N-1] 的排列:
| Gen | kResources 地址 | 槽位数量 N | 是否扩展到 R[22] 之后? |
|---|---|---|---|
| Pufferfish | 0xb43cd94(具名 PufferfishPerformance 符号) | 20 | 否(无 SparseCore / extra-ICI 槽位) |
| Viperfish | 0xb43cda8(具名 ViperfishPerformance 符号) | 28 | 是(增加 ICI / Sc 子槽位) |
| Ghostlite | 0xb43cdc4(具名 GhostlitePerformance 符号) | 31 | 是 |
| 6acc60406 (GF) | 0xb43cde3(未命名;推断) | 31 | 是 |
Pufferfish 的 20 槽位排列从不引用 R[20]/R[21]/R[22],它没有 SparseCore 或 extra-ICI 资源。更宽的代(VF/GL)会用额外 ICI 子链路和 SparseCore 子单元把 enum 扩展到 R[22] 之后;这些高索引槽位在 TensorCore ResourceVectorToString 中没有名称(它只打印 22 个)。若要命名它们,需要 SparseCore 侧的打印路径,而此构建并未通过 TensorCore estimator 暴露该路径(LOW / not recovered)。
UNVERIFIED — 第四行(
0xb43cde3)不是符号锚定的代。nm恰好只解析出三个Performance::GetResources()::kResources符号(PF/VF/GL);Ghostlite 之后的下一个具名符号是kProgramSharedRegistryInitialized@0xb43ce10。0xb43cde3处的 31 个字节是真实存在的第二个[0..30]排列(紧跟在 Ghostlite 的 31 字节表之后,两者都在单个 Ghostlite 符号之下,即kResources是 Ghostlite 根据子版本判别量索引的[2][31]数组),但此构建中没有符号或TpuVersion引用把它归属到 6acc60406 / TPU7x。6acc60406 标签是基于邻接关系和槽位数量的合理推断,而不是已还原事实。
边界:成本模型 Resource 与调度器 ResourceType
二进制中还有第二个更大的“resource” enum,重新实现者不可避免会遇到它:ResourceType,即 LatencyHidingScheduler 的 AsyncTracker 分类。它们不是同一个 enum,不能合并。
成本模型 Resource(本页) | 调度器 ResourceType | |
|---|---|---|
| 枚举 | ResourceVector::Resource | AsyncTracker ResourceType |
| 取值 | 23(R[0..22]) | 47(base {0..12} + target {13..46}) |
| 衡量 | 周期权重(某个单元有多忙) | 并发上限(可同时发射多少个) |
| 名称来源 | ResourceVectorToString @ 0x1c89bde0 | GetResourceName @ 0x13616500(base)/ 0x10fff420(TPU) |
| 消费者 | MaxResourceCycles → bundle 发射成本 | GetNumAvailableResources → co-issue 限流 |
| Per-op 映射 | CycleTable::GetResource(LLO opcode → slot) | GetResourceTypeForOp(HLO opcode → rt) |
这两个 enum 在一个物理概念上重叠:六条 ICI ring 链路。成本模型把周期权重沉积到 R[13..18](Ici{Y,X,Z}{Plus,Minus});调度器通过 ResourceType id 14..19(也叫 kIci{Y,X,Z}{Plus,Minus})限制同一批链路的并发发射,其上限由 xla_tpu_sparse_core_ici_overlap_limit knob(字段 1130)约束。使用 ICI 链路的 bundle 既会产生成本周期(本模型),也会消耗并发槽位(另一个模型)。调度器 enum、其生产者和可用数量来源在专门的 ResourceType 分类 页面中完整记录;这里只总结二者差异以固定边界。
陷阱 — 在
MaxResourceCycles归约中,{3,4,5}上的 0.5 混合是VectorAlu0/1/Any端口均衡组(不是 XLU 混合),{9..12}上的串行求和是MemXfer{Input,Output}{Latency,Bandwidth}内存组(不是 MXU 组)。MXU 管线R[0]/R[1]位于 plain-MAX 组中,会重叠而不是串行化。
相关组件
| 组件 | 关系 |
|---|---|
| CycleTable 家族 | 提供 GetResource(op→slot)和 GetCyclesForThroughput(槽位权重) |
| Bundle-Aware Cost | 消费 MaxResourceCycles 的 GetSubset / 循环组合 |
| ResourceType 分类 | 调度器独立的 47-ID 并发上限 enum |
| 成本模型概览 | 构建逐代 Performance 槽位数据的三组类 |
交叉引用
- 成本模型概览 — 产生
ResourceVector数据的工厂分发和逐代类表 - CycleTable 家族 —
Instructionbucket enum 以及沉积到槽位中的逐代吞吐周期 - Bundle-Aware Cost —
GetSubsetprologue/tail 分区与软件流水化循环成本 - Per-Opcode 周期常量 — 填充每个槽位的逐代周期值
- ResourceType 分类 — 调度器的 47-ID
AsyncTracker资源模型(并发,不是周期权重) - 调度器概览 — 同时消费两种资源模型的
LatencyHidingScheduler - MXU 槽位 — 映射到
Matmul/Matpush/Xlu槽位的 LLO MXU opcode