JfCycleTable
本页中的每个偏移、值、掩码和地址,均按字节精确读取自
libtpu-0.0.40-cp314wheel 中的libtpu.so(BuildID md589edbbe81c5b328a958fe628a9f2207d)。其他版本会有所不同。所有.rodata地址都是虚拟地址;对这个二进制而言,.rodataVMA == 文件偏移(section[11]位于0x84a0000),并且.textVMA == 文件偏移。下面四个 LUT 是用struct.unpack从原始.rodata字节转储出来的,lea位移则根据反汇编重新以算术方式解析。
摘要
xla::jellyfish::JfCycleTable 是两个最早 TensorCore 世代的成本模型中的吞吐量半边:Jellyfish(TpuVersion 0,v2)和 Dragonfish(TpuVersion 1,v3)。它是唯一一个通过扁平字节偏移 LUT 读取周期数的 CycleTable 子类,而不是对 Performance::GetResourceUsage 做 switch;从 Pufferfish 开始,这一族的形态发生变化(见 CycleTable 族)。本页是 JF/DF 读取路径的字节级转录:四条指令的 GetCyclesForThroughput 函数体、33 位计价掩码 0x19FFC0821、完整 33 项 int64 offsetLUT(@0xb438b70)、完整 33 项 int32 resLUT(@0xb438aec),以及生成外层索引的两个 MXU 修饰符分类器 LUT(latchLUT @0xb4389f4,fmtLUT @0xb438ac4)。
需要带走的单一设计事实是:JfCycleTable 不保存周期数。它在 +0x10 保存一个 Performance*,并保存一对 .rodata LUT,用来把一个周期类别(CycleTable::Instruction,一个密集的 0x00..0x20 枚举,不是 LLO opcode)转换成 (a) 该 Performance 网格中的字节偏移,以及 (b) 一个 ResourceVector 槽位索引。网格本身,也就是 0xe00 字节的 PerformanceJf/PerformanceDf POD 及其构造函数源块,已完整记录在 Performance: JF/DF;本页只覆盖其上的索引逻辑,并逐字转录四张查找表。
重新实现者必须遵守的契约:
- 吞吐量读取是
valid ? Performance[offsetLUT[cls]] : 1。valid = (cls < 0x21) && ((0x19FFC0821 >> cls) & 1)。边界和掩码二者都必需,见下面的 GOTCHA。 - 33 个类别中只有 16 个被计价。 另外 17 个短路到默认的
1周期。每个未计价类别的offsetLUT槽位字面值都是0x0。 GetResource(cls)是一个独立的扁平 LUT(resLUT),返回0..6。 这些是进入 23 槽ResourceVector的直接槽位索引;调度器执行ResourceVector[resLUT[cls]] += (double)cycles。- 7 个被计价的 MXU 端口成本为 8 周期;9 个被计价的 vector/EUP 阶段成本为 1。 被计价的偏移中没有
+0x28/+0x2c,因此 JF→DF 的双单元差异永远不会到达吞吐量 LUT,也就是说吞吐量表在 JF 和 DF 中逐字节相同。 - 超越函数由标量虚函数覆盖来计价(
EstimateSinCosCost = 198,EstimateTanCost = 219),而不是由周期类别 LUT 计价。
| 类 | xla::jellyfish::JfCycleTable,服务于 TpuVersion 0(Jellyfish v2)和 1(Dragonfish v3) |
Performance* | 保存在 JfCycleTable+0x10(由 GetCyclesForThroughput 读取) |
| 吞吐量读取器 | JfCycleTable::GetCyclesForThroughput(Instruction) @0x1c89dce0(vtable 槽位 +0x10) |
| 吞吐量公式 | valid = (cls < 0x21) && ((0x19FFC0821 >> cls) & 1);valid ? Performance[offsetLUT[cls]] : 1 |
| 计价掩码 | 0x19FFC0821,33 个类别中 16 个被计价;其余默认 1 |
| offsetLUT | qword_B438B70 @0xb438b70,33 × int64,class → Performance 字节偏移 |
| 资源读取器 | CycleTable::GetResource(Instruction) @0x1c89ce20(世代不变) |
| resLUT | dword_B438AEC @0xb438aec,33 × int32,class → ResourceVector 槽位 0..6 |
| MXU 分类器 | CycleTableInstruction(LloInstruction*) @0x1c89ca80(仅 MXU 带) |
| latchLUT / fmtLUT | unk_B4389F4 @0xb4389f4(52 × int32)/ unk_B438AC4 @0xb438ac4(11 × int32) |
| 超越函数 | EstimateSinCosCost @0x1c89dd20 = 198;EstimateTanCost @0x1c89dd40 = 219 |
| 置信度 | 已确认(字节锚定),除非某行另有说明 |
吞吐量读取路径:GetCyclesForThroughput
JfCycleTable::GetCyclesForThroughput 是一个四条指令的函数。它把周期类别序号限制在 < 0x21,用 33 位有效掩码测试该序号,命中时用从 33 项 int64 offsetLUT 拉取的字节偏移索引 Performance 网格(保存在 JfCycleTable+0x10)。未命中时返回默认值 1。反编译逐字如下:
// xla::jellyfish::JfCycleTable::GetCyclesForThroughput @0x1c89dce0 (decompiled, exact)
__int64 GetCyclesForThroughput(JfCycleTable *this, unsigned int cls) {
__int64 result = 1;
if ( ((cls < 0x21) & (unsigned __int8)(0x19FFC0821uLL >> cls)) == 1 )
return *(unsigned int *)( *(_QWORD *)(this + 0x10) // Performance*
+ qword_B438B70[cls] ); // offsetLUT[cls]
return result; // default 1
}
```text
原始机器码固定了每个常量(`objdump -d -M intel`):
```text
1c89dce0: 89 f1 mov ecx,esi ; ecx = cls
1c89dce2: 83 fe 21 cmp esi,0x21 ; cls < 0x21 ?
1c89dce5: 0f 92 c0 (setb al) ; al = (cls < 0x21)
1c89dce8: 48 ba 21 08 fc 9f 01 movabs rdx,0x19ffc0821 ; the priced mask
1c89dcf2: 48 d3 ea shr rdx,cl ; rdx >>= cls
1c89dcf5: 20 c2 and dl,al ; dl = mask_bit & in_bound
1c89dcf7: b8 01 00 00 00 mov eax,0x1 ; default = 1
1c89dcfc: 80 fa 01 cmp dl,0x1 ; valid ?
( 75 .. jne 1c89dd15) ; -> ret 1
1c89dd01: 89 c8 mov eax,ecx ; eax = cls
1c89dd03: 48 8d 0d 66 ae b9 ee lea rcx,[rip+0xeeb9ae66] ; = 0xb438b70 (offsetLUT)
1c89dd0a: 48 8b 04 c1 mov rax,QWORD PTR [rcx+rax*8] ; rax = offsetLUT[cls]
1c89dd0e: 48 8b 4f 10 mov rcx,QWORD PTR [rdi+0x10] ; rcx = Performance*
1c89dd12: 8b 04 01 mov eax,DWORD PTR [rcx+rax*1] ; eax = Performance[offset]
1c89dd15: c3 retlea 位移相对于下一条指令地址(0x1c89dd0a)按 RIP 计算:0x1c89dd0a + 0xeeb9ae66 = 0x1_0b438b70,64 位加法从 bit 32 进位,留下有效地址 0xb438b70,即 offsetLUT。(objdump 会直接标注目标 VA。)Performance* 位于 [rdi+0x10] = JfCycleTable+0x10,周期读取是 4 字节 int32 load。
GOTCHA:边界和掩码不是冗余的。 重新实现必须同时应用
cls < 0x21和 33 位掩码。边界保护 33 项 LUT 不发生越界读取;掩码在边界内选择 16 个被计价的类别。只依赖offsetLUT会让每个未计价类别都读取Performance[0](vtable 指针,不是周期值),因为每个未计价类别的 LUT 槽位字面值都是0x0。掩码会在到达该读取之前短路。
计价掩码 0x19FFC0821
逐位读取掩码((0x19FFC0821 >> cls) & 1)会把 33 值枚举划分为 16 个计价类别和 17 个未计价类别:
mask = 0x1_9FFC_0821 = 0b1 1001 1111 1111 1100 0000 1000 0010 0001
bit: 32 ... 0
```text
| | 类别 | 数量 |
|---|---|---|
| **计价**(读取 `Performance[offsetLUT[cls]]`) | `0x00, 0x05, 0x0b, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1f, 0x20` | 16 |
| **未计价**(返回默认 `1`) | `0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x1d, 0x1e` | 17 |
---
## offsetLUT:`qword_B438B70`(33 × `int64`,字节精确)
class→字节偏移表,直接从 `.rodata @0xb438b70` 作为 33 个小端 `int64` 转录而来。每个计价条目的解析值都是在该字节偏移处,从重建的 `PerformanceJf` 内存映像读回的单元值(每个 MXU 带单元都在 [PerformanceJf 构造函数](performance-jf-df.md#the-rodata-constant-blocks)中显式给出;8 周期单元来自 `[+0x910]` 处的 `xmmword_A2DA220 = {8,8,8,1}`、覆盖 `[+0x920..+0x980]` 的 `dword_84A2D0C = 8` 广播,以及 `[+0x940]` 处的 `xmmword_A2CF810 = {8,1,1,8}`)。
| 类别 | `offsetLUT[cls]` | 计价 | JF/DF 周期 | 源写法(写入该单元的位置) |
|------:|-----------------:|:------:|----------:|------------------------------------------|
| `0x00` | `0x910` | 是 | **8** | block `xmmword_A2DA220 = {8,8,8,1}` `[0]` |
| `0x01` | `0x000` | 否 | 1 | —(掩码短路) |
| `0x02` | `0x000` | 否 | 1 | — |
| `0x03` | `0x000` | 否 | 1 | — |
| `0x04` | `0x000` | 否 | 1 | — |
| `0x05` | `0x92c` | 是 | **8** | bcast `dword_84A2D0C = 8`(`[+0x920..+0x930]` run) |
| `0x06` | `0x000` | 否 | 1 | — |
| `0x07` | `0x000` | 否 | 1 | — |
| `0x08` | `0x000` | 否 | 1 | — |
| `0x09` | `0x000` | 否 | 1 | — |
| `0x0a` | `0x000` | 否 | 1 | — |
| `0x0b` | `0x92c` | 是 | **8** | bcast `8`(**与 `0x05` 共享偏移 `0x92c`**) |
| `0x0c` | `0x000` | 否 | 1 | — |
| `0x0d` | `0x000` | 否 | 1 | — |
| `0x0e` | `0x000` | 否 | 1 | — |
| `0x0f` | `0x000` | 否 | 1 | — |
| `0x10` | `0x000` | 否 | 1 | — |
| `0x11` | `0x000` | 否 | 1 | — |
| `0x12` | `0x33c` | 是 | 1 | bcast `dword_84A2B08 = 1`(`[+0x334]` run) |
| `0x13` | `0x340` | 是 | 1 | bcast `1`(`[+0x344]` run) |
| `0x14` | `0x344` | 是 | 1 | bcast `1` |
| `0x15` | `0x39c` | 是 | 1 | bcast `1`(`[+0x394]` run) |
| `0x16` | `0x398` | 是 | 1 | bcast `1` |
| `0x17` | `0x954` | 是 | **8** | bcast `8`(`[+0x950..+0x980]` run) |
| `0x18` | `0x3f8` | 是 | 1 | imm `1`(`*((_DWORD*)this+254) = 1`) |
| `0x19` | `0x368` | 是 | 1 | bcast `1`(`[+0x364]` run) |
| `0x1a` | `0x3f4` | 是 | 1 | bcast `1`(`[+0x3e8]` run) |
| `0x1b` | `0x960` | 是 | **8** | bcast `8` |
| `0x1c` | `0x94c` | 是 | **8** | block `xmmword_A2CF810 = {8,1,1,8}` `[3]` |
| `0x1d` | `0x000` | 否 | 1 | — |
| `0x1e` | `0x000` | 否 | 1 | — |
| `0x1f` | `0x958` | 是 | **8** | bcast `8` |
| `0x20` | `0x39c` | 是 | 1 | bcast `1`(**与 `0x15` 共享偏移 `0x39c`**) |
7 个 **8 周期**单元是 MXU matprep/matmul/matrix-result 吞吐量端口,即类别 `0x00, 0x05, 0x0b, 0x17, 0x1b, 0x1c, 0x1f`。9 个 **1 周期**计价单元是 vector-ALU / cross-lane / EUP result 阶段,即类别 `0x12, 0x13, 0x14, 0x15, 0x16, 0x18, 0x19, 0x1a, 0x20`。
> **QUIRK:不同类别会别名到共享单元。** 扁平单元模型不要求每个类别有不同的 `Performance` 偏移。`0x05`/`0x0b` 都读取 `0x92c`(transposed-bf16 latch 和 plain latch 落到同一个吞吐量单元),而 `0x15`/`0x20` 都读取 `0x39c`。8 周期偏移的不同实例折叠为六个值(`0x910, 0x92c, 0x94c, 0x954, 0x958, 0x960`),并且吞吐量单元与 `resLUT` 资源列解耦:两个类别可以共享单元但阻塞不同 lane(这里 `0x05`→`R[0]` 与 `0x0b`→`R[0]` 恰好相同,但 `0x15`/`0x20` 都→`R[5]`)。如果重新实现用偏移本身作为缓存键,会静默合并这些类别。
---
## resLUT:`CycleTable::GetResource` / `dword_B438AEC`(33 × `int32`,字节精确)
`CycleTable::GetResource` 在世代间不变(由全部六个 `CycleTable` 子类共享,不是 `JfCycleTable` 覆盖),并且是一个单一扁平查找:
```c
// xla::jellyfish::CycleTable::GetResource @0x1c89ce20 (decompiled, exact)
__int64 GetResource(CycleTable *this, int cls) {
return dword_B438AEC[cls]; // resLUT @0xb438aec, 33 × int32, values 0..6
}位移校验一致:0x1c89ce22 处的 lea rcx,[rip+0xeeb9bcc3] 解析为 0x1c89ce29 + 0xeeb9bcc3 = 0x1_0b438aec → 有效地址 0xb438aec。从 .rodata 转录的完整 33 项如下:
resLUT @0xb438aec (cls 0x00 .. 0x20):
1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 6 4 4 3 5 5 2 6 5 6 2 2 2 2 2 5
```text
| 类别带 | `resLUT[cls]` | `ResourceVector` 槽位 |
|------------|--------------:|-----------------------|
| `0x00`–`0x04` | `1` | `R[1]` Matmul |
| `0x05`–`0x10` | `0` | `R[0]` Matpush |
| `0x11` | `6` | `R[6]` VectorEup |
| `0x12`, `0x13` | `4` | `R[4]` VectorAlu1 |
| `0x14` | `3` | `R[3]` VectorAlu0 |
| `0x15`, `0x16` | `5` | `R[5]` VectorAluAny |
| `0x17` | `2` | `R[2]` Xlu |
| `0x18` | `6` | `R[6]` VectorEup |
| `0x19` | `5` | `R[5]` VectorAluAny |
| `0x1a` | `6` | `R[6]` VectorEup |
| `0x1b`–`0x1f` | `2` | `R[2]` Xlu |
| `0x20` | `5` | `R[5]` VectorAluAny |
### 为什么这些值是 `ResourceVector` 槽位索引
`GetResource` 返回一个原始整数,但唯一的消费者直接把它当作进入 23 槽 `ResourceVector` 的槽位索引。成本 lambda `AccumulateInstructionUsage` `@0x144fd720` 是绑定证据:
```c
// xla::jellyfish::sdc_checker::...::AccumulateInstructionUsage::operator() @0x144fd720 (decompiled)
__int64 operator()(CycleTable &ct, ResourceVector &rv, unsigned int cls) {
unsigned int Resource = CycleTable::GetResource(&ct, cls); // resLUT[cls]
__int64 cyc = (*(vtable + 0x10))(&ct, cls); // GetCyclesForThroughput(cls)
ResourceVector::Acc(&rv, Resource, (double)cyc); // rv[Resource] += cyc
return 1;
}而 ResourceVector::Acc @0x1c89adc0 是一个以该整数为键、带边界检查的 +=:
// xla::jellyfish::ResourceVector::Acc @0x1c89adc0 (decompiled, exact)
__int64 Acc(ResourceVector *this, unsigned int resource, double cycles) {
if ( resource >= 0x17 ) __ud1(); // hard bound 0x17 = 23 slots
this[resource] += cycles; // vaddsd [rdi + resource*8]
return resource;
}
```text
`cmp esi, 0x17 / jae ud1` 边界把向量固定为 **23** 个 `double` 槽;JF/DF `resLUT` 只发出值 `0..6`,也就是 23 槽累加器的 MXU/vector **头部**。剩余槽位 `R[7..22]`(vector load/store、四个内存传输项、六条 ICI 链路、三个 SparseCore 引擎)由其他成本路径写入,绝不会由这个扁平 LUT 写入;完整向量和 `MaxResourceCycles` 重叠归约见 [资源枚举](resource-enum.md)。
---
## 7 个 JF 资源列:命名
7 个不同的 `resLUT` 值不是私有成本枚举;它们是前 7 个 `ResourceVector::Resource` 序号。名称直接来自 `ResourceVectorToString` `@0x1c89bde0` 内的 `printf` 模板,该模板以物理偏移顺序格式化前 22 个槽位,因此字符串顺序*就是*枚举顺序。模板头部(逐字节读取):
```text
RV[Matpush: %.0f, Matmul: %.0f, Xlu: %.0f, VectorAlu0: %.0f, VectorAlu1: %.0f,
VectorAluAny: %.0f, VectorEup: %.0f, VectorLoad: %.0f, VectorStore: %.0f, ...]前 7 个 %.0f 槽位与 resLUT 值 0..6 一一对应:
| Res | ResourceVector 槽位 | 名称 | JF/DF 占用类别 | 吞吐量 |
|---|---|---|---|---|
0 | R[0] +0x00 | Matpush | 0x05–0x10(latch / push-gains 带) | 0x05,0x0b = 8;其余 dflt 1 |
1 | R[1] +0x08 | Matmul | 0x00–0x04(matprep 带) | 0x00 = 8;其余 dflt 1 |
2 | R[2] +0x10 | Xlu | 0x17, 0x1b–0x1f(matrix-result / cross-lane) | 0x17,0x1b,0x1c,0x1f = 8 |
3 | R[3] +0x18 | VectorAlu0 | 0x14 | 1 |
4 | R[4] +0x20 | VectorAlu1 | 0x12, 0x13 | 1 |
5 | R[5] +0x28 | VectorAluAny | 0x15, 0x16, 0x19, 0x20 | 1 |
6 | R[6] +0x30 | VectorEup | 0x11, 0x18, 0x1a | 0x18,0x1a = 1;0x11 dflt 1 |
这就是 JF/DF 成本模型表达资源冲突的方式:映射到同一列的两个类别相加(它们在同一个功能单元 lane 上串行化);不同列中的两个类别重叠(bundle 的贡献是逐 lane 最大值,而不是总和,即 MaxResourceCycles)。注意,列索引与 matmul/matprep opcode 直觉是解耦的:resLUT 把 matprep 带(0x00–0x04)映射到 R[1] Matmul,把 latch 带(0x05–0x10)映射到 R[0] Matpush,这是 matmul 流水线顺序,而不是朴素的 opcode 到名称配对。
NOTE:
R[k]名称已绑定确认;微端口语义未确认。 这七个名称已由ResourceVectorToString模板和Acc槽位索引确认。更深层的物理映射(每列保留哪个 MXU 子阶段)没有针对CycleTable::Resource或CycleTable::Instruction成本枚举的ToString,因此仍为功能性/推断。
生成外层索引:CycleTableInstruction 和两个 MXU LUT
GetCyclesForThroughput/GetResource 的参数是一个周期类别,不是 opcode。对 MXU 带而言,该类别由 CycleTableInstruction(LloInstruction*) @0x1c89ca80 生成,它是二进制中唯一的 LLO→类别分类器。它只分类两个 opcode 带,对任何其他内容都会 fatal:
// xla::jellyfish::CycleTableInstruction @0x1c89ca80 (decompiled, exact shape)
uint32_t CycleTableInstruction(const LloInstruction *insn) {
uint32_t op = insn->opcode;
if ((uint16_t)(op - 141) <= 9) { // opcodes 0x8d..0x96 = matmul / latch band
uint8_t lm = insn->latch_mode();
if (lm >= 0x34 || !bittest64(0xF000003FFFC3F, lm))
LogFatal("Unsupported gain latch mode ", /*cycle_table.cc:431*/);
return unk_B4389F4[lm]; // latchLUT @0xb4389f4, 52 × int32
}
if ((uint16_t)(op - 155) <= 0xA) { // opcodes 0x9b..0xa5 = matprep / matpush band
uint8_t f = insn->matmul_data_format() - 1;
if (f >= 0xA)
LogFatal("Unsupported matmul data format ", /*cycle_table.cc:464*/);
return unk_B438AC4[f]; // fmtLUT @0xb438ac4, first 11 × int32
}
LogFatal("Unsupported instruction ", /*cycle_table.cc:470*/);
}
```text
分类器只覆盖 MXU 带(类别 `0x00`–`0x10`)。vector/EUP/matrix-result 类别 `0x11`–`0x20` 不是由这里生成的;它们由 HLO 级成本模型根据 HLO opcode / `PrimitiveType` / 内存传输角色作为直接序号立即数发出(见 [IARS Per TensorCore](iars-per-tensorcore.md));其生产 LLO opcode 集尚未解码(中等置信度)。
### latchLUT:`unk_B4389F4`(52 × `int32`,有效掩码 `0xF000003FFFC3F`)
`GainLatchMode → CycleTable::Instruction`,由原始 `latch_mode()` 字节索引。只有有效掩码中设置的 mode 可达;其他所有 mode 都会在读取前 `LogFatal`。字节精确转录如下(只显示非零/可达行;其他每个索引都读为 `0`):
| `latch_mode()` | `latchLUT[lm]` → 类别 | `latch_mode()` | `latchLUT[lm]` → 类别 |
|---------------:|:----------------------:|---------------:|:----------------------:|
| `0x00`, `0x02`, `0x04` | `5` | `0x12`, `0x14`, `0x16`, `0x18` | `9` |
| `0x01`, `0x03`, `0x05` | `11` | `0x13`, `0x15`, `0x17`, `0x19` | `15` |
| `0x0a` | `12` | `0x30` | `7` |
| `0x0b`, `0x0e`, `0x10` | `6` | `0x31` | `13` |
| `0x0c` | `9` | `0x32` | `8` |
| `0x0d` | `15` | `0x33` | `14` |
| `0x0f`, `0x11` | `12` |(所有其他项,掩码拒绝)| `0` |
有效掩码 `0xF000003FFFC3F` 会在查找*之前*由 `_bittest64` 检查;四个高位(`0x30`–`0x33`,bit 48..51 处的 `0xF`)映射到类别 `{7, 13, 8, 14}`(已由 `.rodata` 转储确认),并且只在会发出这些 latch mode 的世代上可达。它们归属于某个具体后续世代这一点没有字节锚定(推断)。
### fmtLUT:`unk_B438AC4`(前 11 × `int32`)
`MatmulDataFormat → CycleTable::Instruction`,由 `matmul_data_format() - 1` 索引(JF 读取器校验 `< 0xA`,因此会读取前 11 项)。字节精确转录如下:
```text
fmtLUT @0xb438ac4 (index = matmul_data_format()-1):
index: 0 1 2 3 4 5 6 7 8 9 10
class: 0 1 1 1 4 4 4 4 2 3 1即 fmt 1 → class 0、fmt 2/3/4 → class 1、fmt 5/6/7/8 → class 4、fmt 9 → class 2、fmt 10 → class 3、fmt 11 → class 1。MXU 修饰符语义(哪个数值格式是 bf16 / fp8 / int8)随 matmul mode 修饰符一起记录。
NOTE:格式 LUT 比 JF 读取器实际使用的更宽。 在 index 10 之后,
.rodata中还有额外的 packed-int8 / int4 格式值,但 JF 分类器的< 0xA有效性检查会拒绝它们(它们由后续世代路径使用)。这里把它们标为对 JF/DF 推断,因为 JF 分类器会对它们LogFatal。
JF vs DF:吞吐量表相同
PerformanceDf::PerformanceDf @0x1d493060 构建完整 PerformanceJf 映像,替换 vtable,并发出恰好一次 quadword store:
// platforms_deepsea::jellyfish::isa::PerformanceDf::PerformanceDf @0x1d493060 (decompiled, exact)
PerformanceJf::PerformanceJf(this, dev);
*(_QWORD *)this = off_21CC7478; // swap vtable → PerformanceDf
*((_QWORD *)this + 5) = 0xD00000042uLL; // store at this+0x28
```text
`this + 5`(qword)是 `Performance[+0x28]`;`int64 0x0000000D_00000042` 写入 `[+0x28] = 0x42 = 66`(低于 JF 的 `88`)和 `[+0x2c] = 0x0D = 13`(高于 JF 的 `8`)。**`0x28` 和 `0x2c` 都不出现在 `offsetLUT` 中**(计价偏移为 `{0x910, 0x92c, 0x94c, 0x954, 0x958, 0x960, 0x33c, 0x340, 0x344, 0x368, 0x398, 0x39c, 0x3f4, 0x3f8}`),因此 `GetCyclesForThroughput` 在 JF 和 DF 上返回相同的 16 个值。这两个改变的单元是由 `LatencyTableJellyfish` 消费的 matmul / matprep *基础延迟*,不是吞吐量单元;整个 v2→v3 成本差异都位于延迟轴上([MXU 延迟:JF/DF](mxu-latency-jf-df.md)),本页的吞吐量表对两个世代逐字节相同。
---
## 超越函数:标量虚函数覆盖
JF/DF 上的超越函数成本**不**经过周期类别 LUT。`JfCycleTable` vtable 携带两个标量 `const` 虚函数覆盖(槽位 `+0x18` / `+0x20`),不论操作数大小如何都返回固定估计:
```c
__int64 JfCycleTable::EstimateSinCosCost(...) @0x1c89dd20 { return 198; } // sin / cos
__int64 JfCycleTable::EstimateTanCost(...) @0x1c89dd40 { return 219; } // tan这些值会由成本模型的超越函数路径叠加在逐步流水线计数之上。它们与 Pufferfish 相同(同样是 198 / 219),随后随着 XLU 流水线加速在后续世代中缩小(VF 154 / 170,GL/GF 142 / 151);完整的跨世代表见 逐 opcode 周期常量。
重新实现配方
忠实的 JfCycleTable 只需要上面的四个 LUT 加上 Performance 网格:
int GetCyclesForThroughput(const Performance *perf, uint32_t cls) {
if (cls < 0x21 && ((0x19FFC0821ULL >> cls) & 1))
return *(const int32_t *)((const char *)perf + offsetLUT_B438B70[cls]);
return 1; // default
}
int GetResource(uint32_t cls) {
return resLUT_B438AEC[cls]; // 0..6 = ResourceVector slot
}
// per-op accumulation (the only consumer):
// resource_vector[GetResource(cls)] += (double)GetCyclesForThroughput(perf, cls);
// transcendentals bypass the LUT: sin/cos -> 198, tan -> 219.
```text
需要逐字嵌入的四张 `.rodata` 表:
| 表 | 地址 | 形状 | 映射 |
|-------|---------|-------|------|
| `offsetLUT` | `0xb438b70` | 33 × `int64` | class → `Performance` 字节偏移(本页) |
| `resLUT` | `0xb438aec` | 33 × `int32` | class → `ResourceVector` 槽位 `0..6`(本页) |
| `latchLUT` | `0xb4389f4` | 52 × `int32`(掩码 `0xF000003FFFC3F`) | `GainLatchMode` → class |
| `fmtLUT` | `0xb438ac4` | 11 × `int32`(index `fmt-1`) | `MatmulDataFormat` → class |
按 [Performance: JF/DF](performance-jf-df.md) 构建 `PerformanceJf`(`0xe00` 字节 POD、哨兵 `0x7FFFFFFF`、419 次构造函数 store),为 Dragonfish 应用单个 DF 单元补丁,吞吐量半边就完整了。
---
## 交叉引用
- [CycleTable 族](cycletable-family.md):抽象基类、六工厂注册表、按版本分派,以及共享周期类别枚举框架。
- [Performance: JF/DF](performance-jf-df.md):本页 `offsetLUT` 索引进入的 `0xe00` 字节 `PerformanceJf`/`PerformanceDf` POD:布局、419 次构造函数 store、15 个 `.rodata` 源块和 2 单元 DF 差异。
- [逐 opcode 周期常量](per-opcode-cycle-constants.md):跨世代吞吐量整数和超越函数标量表;JF/DF 扁平单元模型的上下文。
- [VfCycleTable](vf-cycletable.md):模型演化到的 Viperfish `switch`-over-`GetResourceUsage` 路径。
- [资源枚举](resource-enum.md):`resLUT` 发出的头部 `R[0]..R[6]` 所属的 23 槽 `ResourceVector`,以及 `MaxResourceCycles` 重叠归约。
- [MXU 延迟:JF/DF](mxu-latency-jf-df.md):正交延迟轴;`LatencyTableJellyfish` 复制映射和 JF→DF 的 `88→66 / 8→13` 基础延迟差异。
- [Matmul Mode 修饰符](matmul-mode-modifiers.md):`latchLUT` / `fmtLUT` 折叠进周期类别的 `GainLatchMode` / `MatmulDataFormat` 修饰符。
- [IARS Per TensorCore](iars-per-tensorcore.md):生成周期类别 `0x11..0x20` 作为直接序号立即数的非 MXU emitter 路径。
- **二进制:** `extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so`(build-id `89edbbe81c5b328a958fe628a9f2207d`)
- **索引条目:** Part VII — Cost & Latency Model / CycleTable — [返回索引](../index.md)