MXU 延迟:JF / DF
地址适用于来自 libtpu-0.0.40-cp314 wheel 的 libtpu.so(BuildID md5
89edbbe81c5b328a958fe628a9f2207d,未剥离 — 完整 C++ 符号)。其他版本会不同。下面每个整数都从.rodata手工读出,并与 IDA 反编译交叉核对;验证状态位于 Confidence 列。
摘要
本页记录最老一代 TensorCore MXU 成本模型:Jellyfish(TPU v2)和 Dragonfish(TPU v3)的延迟/吞吐表。与后续每一代不同,JF 和 DF 不使用堆分配的 latency[] 数组加二维 Instruction × Resource 预留网格(该模型从 Pufferfish 开始 — 见 Performance: PF)。它们使用一个固定大小的 0xe00 字节内联 POD struct — platforms_deepsea::jellyfish::isa::Performance — 其按指令吞吐是每个 Instruction 一个扁平单元的查找,另有一个 LatencyTableJellyfish,其冲突边模型是从同一个 struct 复制出来的十五个整数。
重新实现者应把它看作由一个 struct 驱动的两张表:
- 吞吐 —
JfCycleTable::GetCyclesForThroughput(Instruction)读取Performance[offsetLUT[Instruction]],受 33 位有效指令 mask 门控。33 个CycleTable::Instruction序号中有 16 个从 struct 定价;其余返回默认值1。MXU matprep/matmul/matrix-result 端口成本为 8 周期;vector/EUP result 阶段成本为 1。 - 延迟 —
LatencyTableJellyfish从单例Performance复制十五个单元到自己的[+0x18..+0x50]字段块。其中两个 — matmul 基础延迟和 matprep 基础延迟 — 是 DF 相对 JF 唯一更改的两个整数:PerformanceDf覆写Performance[+0x28] 88→66和[+0x2c] 8→13。JF 和 DF 成本模型的其他所有内容都按字节相同。
两张表都由同一个 JfCycleTable(xla::jellyfish::JfCycleTable,vtable 0x21c1ffb8)服务;CycleTable::Create 为 v2 和 v3 注册同一个 JfCycleTable。JfCycleTable vtable 没有 GetLatency 槽 — 延迟轴完全存在于 LatencyTableJellyfish 中。两半在 matmul-rewrite / MRB-accumulation 消费者中汇合,这些消费者会同时读取吞吐单元和复制出的延迟字段。
如果你读过 LLVM 调度 itinerary(InstrItineraryData),最接近的类比是每个 itinerary-class 的 operand-cycle 表加一个独立的 forwarding/bypass latency 表 — 只是这里两者都被烘焙为 .rodata 常量,并且“itinerary class”是 CycleTable::Instruction enum,即一种 MXU-modifier 展开,而不是 opcode。
| 吞吐读取器 | JfCycleTable::GetCyclesForThroughput(Instruction) 0x1c89dce0 |
| 吞吐公式 | valid = (I < 0x21) && ((0x19FFC0821 >> I) & 1);valid ? Performance[offsetLUT[I]] : 1 |
| offsetLUT | 0xb438b70(33 × int64,Instruction → Performance 字节偏移) |
| resLUT | 0xb438aec(33 × int32,Instruction → Resource 列 0..6) |
| Performance struct | 0xe00 字节,内联 POD;基础哨兵 0x7fffffff;PerformanceJf ctor 0x1d4930c0,PerformanceDf ctor 0x1d493060 |
| 延迟表 | LatencyTableJellyfish ctor 0x1c8a0c20 — 将 15 个 Performance 单元复制到 [+0x18..+0x50] |
| MXU 发射(吞吐) | 已定价 matprep/matmul/matres 格式为 8 周期/op |
| MXU matmul 基础延迟 | JF 88 / DF 66(Performance[+0x28] → LatencyTable[+0x50]) |
| MXU matprep 基础延迟 | JF 8 / DF 13(Performance[+0x2c] → LatencyTable[+0x4c]) |
| EUP push→pop 边 | 4 周期(Performance[+0x30] → LatencyTable[+0x1c]) |
| JF→DF 差异 | 正好 2 个 int32 单元(+0x28、+0x2c);吞吐表按字节相同 |
吞吐读取器
GetCyclesForThroughput
JfCycleTable::GetCyclesForThroughput 是一个四行函数。它对 Instruction 序号做边界检查,用 33 位有效 mask 测试;命中时,用一个 33 项 int64 LUT 中取得的字节偏移,索引 Performance struct(保存在 JfCycleTable+0x10)。未命中时返回默认值 1。以下逐字来自反编译:
// xla::jellyfish::JfCycleTable::GetCyclesForThroughput @ 0x1c89dce0 (verified)
__int64 JfCycleTable::GetCyclesForThroughput(this, unsigned int instr) {
if ( ((instr < 0x21) & (uint8_t)(0x19FFC0821uLL >> instr)) == 1 )
return *(uint32_t*)( *(uint64_t*)(this + 0x10) // Performance*
+ qword_B438B70[instr] ); // offsetLUT[instr]
return 1; // default
}
```text
有效 mask `0x19FFC0821` 选择十六个已定价序号;另外十七个短路到 `1`。每个*未定价*序号的 `offsetLUT` 槽字面值都是 `0x0`,这没有危害,因为在到达读取前 mask 测试总会失败。
> **注意 —** 边界是 `< 0x21`(33 个序号,`0x00..0x20`),mask 正好 33 位宽。序号 `≥ 0x21` 在这里永不定价;它们属于其他 `CycleTable` 路径。重新实现必须同时应用边界和 mask — 仅依赖 offsetLUT 会为未定价序号读取 `Performance[0]`(vtable)。
### 资源列
`CycleTable::GetResource` 是一个单一扁平查找 — 每个 `Instruction` 映射到七个 `Resource` 列之一:
```c
// xla::jellyfish::CycleTable::GetResource @ 0x1c89ce20 (verified)
__int64 CycleTable::GetResource(this, int instr) {
return dword_B438AEC[instr]; // resLUT[instr], 33 x int32, values 0..6
}七个不同值 0..6 不是私有 enum — 它们是 23 槽 ResourceVector 的槽索引。唯一消费者 AccumulateInstructionUsage 调用 ResourceVector::Acc(GetResource(I), (double)GetCyclesForThroughput(I)),而 Acc 用 23 的硬边界索引 [ResourceVector + Resource*8]:
// xla::jellyfish::ResourceVector::Acc @ 0x1c89adc0 (verified)
__int64 ResourceVector::Acc(this, unsigned int resource, double cycles) {
if ( resource >= 0x17 ) __ud1(); // bound 0x17 = 23 ResourceVector slots
this[resource] += cycles; // vaddsd [rdi + resource*8]
return resource;
}
```text
因此七个 JF/DF `Resource` 列就是前七个 `ResourceVector` 槽:`R[0]` Matpush、`R[1]` Matmul、`R[2]` Xlu、`R[3]` VectorAlu0、`R[4]` VectorAlu1、`R[5]` VectorAluAny、`R[6]` VectorEup。JF 成本模型只填充 23 槽累加器的 MXU/vector 头部。
---
## 整数吞吐表
这是对全部 33 个 `CycleTable::Instruction` 序号的 `JfCycleTable::GetCyclesForThroughput` 完整重建。`offsetLUT`(`0xb438b70`)和 `resLUT`(`0xb438aec`)列逐字节从 `.rodata` 读出;周期值是在重建的 `PerformanceJf` 映像中解析出的已定价单元。JF 和 DF 在每个单元上都相同(已定价偏移中没有 `+0x28` 或 `+0x2c`,即唯二的 DF 覆写项)。
| `Instr` | `offsetLUT[I]` | `Res` | `ResourceVector` 槽 | 已定价 | JF cyc | DF cyc |
|---|---|---|---|---|---|---|
| `0x00` | `0x910` | `r1` | Matmul | yes | 8 | 8 |
| `0x01` | `0x000` | `r1` | Matmul | no | 1 | 1 |
| `0x02` | `0x000` | `r1` | Matmul | no | 1 | 1 |
| `0x03` | `0x000` | `r1` | Matmul | no | 1 | 1 |
| `0x04` | `0x000` | `r1` | Matmul | no | 1 | 1 |
| `0x05` | `0x92c` | `r0` | Matpush | yes | 8 | 8 |
| `0x06` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x07` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x08` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x09` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x0a` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x0b` | `0x92c` | `r0` | Matpush | yes | 8 | 8 |
| `0x0c` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x0d` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x0e` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x0f` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x10` | `0x000` | `r0` | Matpush | no | 1 | 1 |
| `0x11` | `0x000` | `r6` | VectorEup | no | 1 | 1 |
| `0x12` | `0x33c` | `r4` | VectorAlu1 | yes | 1 | 1 |
| `0x13` | `0x340` | `r4` | VectorAlu1 | yes | 1 | 1 |
| `0x14` | `0x344` | `r3` | VectorAlu0 | yes | 1 | 1 |
| `0x15` | `0x39c` | `r5` | VectorAluAny | yes | 1 | 1 |
| `0x16` | `0x398` | `r5` | VectorAluAny | yes | 1 | 1 |
| `0x17` | `0x954` | `r2` | Xlu | yes | 8 | 8 |
| `0x18` | `0x3f8` | `r6` | VectorEup | yes | 1 | 1 |
| `0x19` | `0x368` | `r5` | VectorAluAny | yes | 1 | 1 |
| `0x1a` | `0x3f4` | `r6` | VectorEup | yes | 1 | 1 |
| `0x1b` | `0x960` | `r2` | Xlu | yes | 8 | 8 |
| `0x1c` | `0x94c` | `r2` | Xlu | yes | 8 | 8 |
| `0x1d` | `0x000` | `r2` | Xlu | no | 1 | 1 |
| `0x1e` | `0x000` | `r2` | Xlu | no | 1 | 1 |
| `0x1f` | `0x958` | `r2` | Xlu | yes | 8 | 8 |
| `0x20` | `0x39c` | `r5` | VectorAluAny | yes | 1 | 1 |
十六个已定价序号正是 `{0x00, 0x05, 0x0b, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1f, 0x20}`。七个 **8-cycle** 单元是 MXU matprep/matmul/matrix-result 吞吐端口(`0x00`、`0x05`、`0x0b`、`0x17`、`0x1b`、`0x1c`、`0x1f`);九个 **1-cycle** 已定价单元是 vector-ALU / EUP result 阶段。
> **怪异点 — 两个序号共享一个单元。** `Instr 0x15` 和 `Instr 0x20` 都读取 `offsetLUT = 0x39c`(VectorAluAny,值 1)。扁平单元模型不要求每个序号有不同偏移;预留列和吞吐单元是解耦的。
`Instr 0x00..0x10` 区段(MXU 序号)由 MXU 分类器 `CycleTableInstruction`(`0x1c89ca80`)产生,它通过 `GainLatchMode → Instruction` LUT 映射 matmul opcode `0x8d..0x96`,并通过 `MatmulDataFormat → Instruction` LUT 映射 matprep/matpush opcode `0x9b..0xa5`(两者都在 [JfCycleTable](jf-cycletable.md) 中解码)。非 MXU 区段 `0x11..0x20` 由 HLO 级成本模型作为直接序号立即数发出 — 完整生产者分解见 [IARs Per TensorCore](iars-per-tensorcore.md)。
---
## Performance Struct 与 JF→DF 差异
### 布局
`Performance` 是一个 `0xe00` 字节(3584 字节)内联 POD,vtable 位于 `+0x00`,`DeviceIdentifiers` 位于 `+0x08..+0x14`,按 `Instruction` 的成本缓冲区覆盖 `[+0x18 .. +0xdf8]`(890 个 `int32` 槽)。基础构造函数 `Performance::Performance`(`0x1d492900`)用哨兵 **`0x7fffffff`**(= `INT_MAX`,*不是* `0xffffffff`)`memset` 填充整个缓冲区,该值从 `.rodata @0x84a2f60` 广播(按字节精确读取为 `0x7fffffff`)。`PerformanceJf`(`0x1d4930c0`)随后通过复制预烘焙 16 字节 `.rodata` 块、广播填充(标量 `@0x84a2b08 = 1`、`@0x84a2854 = 2`、`@0x84a2d0c = 8`,全都按字节精确读取)以及立即数 store 覆写单元 — 116 个 store op 触及 419 个不同 `int32` 槽;其他 471 个保持哨兵。
吞吐相关常量块,直接从 `.rodata` 读取:
| `.rodata` 块 | 字节(4 × int32) | 落到 | 作用 |
|---|---|---|---|
| `@0xa2c8a30` | `{4, 105, 7, 92}` | `Performance[+0x18]` | RPU producer / matres-self conflict floors |
| `@0xa2dcd30` | `{88, 8, 4, 1}` | `Performance[+0x28]` | matmul base(`+0x28`)、matprep base(`+0x2c`)、EUP edge(`+0x30`) |
| `@0xa2da220` | `{8, 8, 8, 1}` | `Performance[+0x910]` | MXU throughput band head(`Instr 0x00`) |
| `@0xa2cf810` | `{8, 1, 1, 8}` | `Performance[+0x940]` | MXU throughput cell `[+0x94c]`(`Instr 0x1c`) |
### DF 覆写
`PerformanceDf` 是 `PerformanceJf` 加一次 vtable 交换和正好一次 quadword store。来自反编译:
```c
// platforms_deepsea::jellyfish::isa::PerformanceDf::PerformanceDf @ 0x1d493060 (verified)
PerformanceJf::PerformanceJf(this, dev); // build the full JF image first
*(uint64_t*)this = off_21CC7478; // swap the vtable
*((uint64_t*)this + 5) = 0xD00000042uLL; // store at this+0x28 (= int32[5..6])this + 5(qword)是 Performance[+0x28]。qword 0xD00000042 写入 [+0x28] = 0x42 = 66 和 [+0x2c] = 0x0D = 13。因此:
| 单元 | JF | DF | 作用 |
|---|---|---|---|
Performance[+0x28] | 88 | 66 | MXU matmul 基础延迟 |
Performance[+0x2c] | 8 | 13 | MXU matprep 基础延迟 |
其他 419 个已填充槽中的每一个都在 JF 与 DF 间按字节相同。因为 +0x28 和 +0x2c 都不是 offsetLUT 目标,GetCyclesForThroughput 对所有 16 个已定价序号在 JF 和 DF 上都返回相同值 — 整个 v2→v3 成本差异就是这两个延迟表整数。
注意 — v2→v3 MXU 变化只体现在 matmul 基础延迟中。 Dragonfish 将 MXU 数量翻倍(1→2)并提高 TensorCore 时钟。吞吐单元仍保持 8 周期/op;加速编码为更低的 matmul 基础延迟(88→66),以及略更高的 matprep 基础延迟(8→13)。若重新实现为 DF 缩放吞吐单元,就会重复计算 v3 优势。
LatencyTableJellyfish 复制映射
LatencyTableJellyfish 是冲突边 / forwarding latency 模型。它的构造函数从单例 Performance(由 Performance::CreateTensorCore 构建一次,并作为文件作用域 TpuPerformanceTable(version) 表缓存)复制十五个单元到自己的 [+0x18..+0x50] 字段块。这些字段就是 LatencyTableJellyfish::LatencyBetweenInternal(0x1c8a0d60)读取的按边延迟下限 — r15 是 LatencyTable*,不是 Performance*。复制逐字来自构造函数反编译;Performance 源被按 int32[] 索引(v2[k] = Performance[k*4]):
// xla::jellyfish::LatencyTableJellyfish::LatencyTableJellyfish @ 0x1c8a0c20 (verified)
v2 = TpuPerformanceTable(version)::table; // = Performance::CreateTensorCore(...)
LT[+0x18] = *(int32*)(v2+0x44); // Performance[+0x44] (raw byte add, not v2[17])
LT[+0x1c] = v2[12]; // Performance[+0x30] ← EUP push→pop edge
LT[+0x20] = v2[9]; // Performance[+0x24]
LT[+0x24] = v2[7]; // Performance[+0x1c]
LT[+0x28] = v2[8]; // Performance[+0x20]
LT[+0x2c] = v2[597]; // Performance[+0x954] ← MXU-result throughput cell (Instr 0x17)
LT[+0x30] = v2[595]; // Performance[+0x94c] ← MXU-result throughput cell (Instr 0x1c)
LT[+0x34] = v2[580]; // Performance[+0x910] ← matprep throughput cell (Instr 0x00)
LT[+0x38] = v2[587]; // Performance[+0x92c] ← matmul throughput cell (Instr 0x05)
LT[+0x3c] = v2[456]; // Performance[+0x720] ← xpose-result cell
LT[+0x40] = v2[455]; // Performance[+0x71c] ← xpose-result B cell
LT[+0x44] = v2[262]; // Performance[+0x418]
LT[+0x48] = v2[264]; // Performance[+0x420]
LT[+0x4c] = v2[11]; // Performance[+0x2c] ← MXU matprep base (JF 8 / DF 13)
LT[+0x50] = v2[10]; // Performance[+0x28] ← MXU matmul base (JF 88 / DF 66)
```text
解析出的值(JF / DF),作用由读取各字段的 `LatencyBetweenInternal` 谓词推断:
| `LatencyTable` 偏移 | ← `Performance` 偏移 | JF | DF | 作用 |
|---|---|---|---|---|
| `+0x18` | `+0x44` | 1 | 1 | matres-result FIFO floor(UNVERIFIED role) |
| `+0x1c` | `+0x30` | 4 | 4 | **EUP push→pop 边** |
| `+0x20` | `+0x24` | 92 | 92 | RPU-result floor |
| `+0x24` | `+0x1c` | 105 | 105 | RPU op→op / matres-self conflict |
| `+0x28` | `+0x20` | 7 | 7 | UsesRpu producer floor |
| `+0x2c` | `+0x954` | 8 | 8 | MXU-result cell(`Instr 0x17`) |
| `+0x30` | `+0x94c` | 8 | 8 | MXU-result cell(`Instr 0x1c`) |
| `+0x34` | `+0x910` | 8 | 8 | matprep throughput cell(`Instr 0x00`) |
| `+0x38` | `+0x92c` | 8 | 8 | matmul throughput cell(`Instr 0x05`) |
| `+0x3c` | `+0x720` | 8 | 8 | xpose-result cell |
| `+0x40` | `+0x71c` | 8 | 8 | xpose-result B cell |
| `+0x44` | `+0x418` | 8 | 8 | branch-op cell |
| `+0x48` | `+0x420` | 8 | 8 | branch-op cell |
| `+0x4c` | `+0x2c` | **8** | **13** | **MXU matprep base** |
| `+0x50` | `+0x28` | **88** | **66** | **MXU matmul base** |
前三列中的三个(`+0x20`/`+0x24`/`+0x28` ← `Performance[+0x24]`/`[+0x1c]`/`[+0x20]`)来自头部块 `@0xa2c8a30 = {4,105,7,92}`(注意复制索引会重排 `92`/`105`/`7`,而 `Performance[+0x18]` 处的首个 `4` 未复制);`+0x18`(← `Performance[+0x44]` = `1`)是 `@0xa2c8a40 = {2,1,1,1}` 块的第二个单元,不是头部块。最后两个(`+0x4c`/`+0x50`)来自 `@0xa2dcd30 = {88,8,4,1}`,也是 DF 覆写触及的唯二单元。构造函数顶部的版本 guard 会 `CHECK` `tpu_version_ ∈ {kJellyfish, kDragonfish}`,确认此表只服务 v2/v3。
> **陷阱 — matmul base 和 matprep base 在字段顺序上相对 struct 顺序是*交换*的。** `Performance[+0x28]`(matmul base)复制到更高的 `LatencyTable[+0x50]`,`Performance[+0x2c]`(matprep base)复制到更低的 `LatencyTable[+0x4c]`。若重新实现把 `{88,8,...}` 块线性复制到延迟表尾部,就会转置 matmul/matprep 基础延迟。
### EUP Push→Pop 边
`Performance[+0x30] = 4`(`@0xa2dcd30 = {88,8,4,1}` 的第三个元素)复制到 `LatencyTable[+0x1c]`。当生产者是 EUP push(opcode `0x128..0x13a`)且消费者是 `0x14e` EUP-result pop(或 pseudo-EUP)时,`LatencyBetweenInternal` 会把边延迟提升到这个 `4`。该函数还应用一个*独立*的硬编码最小下限 `4`(`mov r12d, 0x4`,在 `SetIar` 后接 indexed-load 时提升到 `5`)。两个 `4` 在 JF/DF 上重合。每代递进 `4 (JF/DF) → 7 (PF) → 6 (VF) → 13/14 (GL)` 见 [EUP Latency Overview](eup-latency-overview.md) 和 [EUP Per-Gen Integers](eup-per-gen-integers.md)。
---
## 超越函数估算成本
`JfCycleTable` vtable 在吞吐读取器之外带有两个超越函数估算槽。两者都是常量返回函数,逐字读取如下:
| 函数 | vtable 槽 | 值 |
|---|---|---|
| `JfCycleTable::EstimateSinCosCost` `0x1c89dd20` | `+0x18` | **198**(`0xc6`) |
| `JfCycleTable::EstimateTanCost` `0x1c89dd40` | `+0x20` | **219**(`0xdb`) |
这些为 JF/DF 上 Payne–Hanek 风格的 range-reduced 超越函数定价;DF 继承它们(一个 `JfCycleTable` 注册服务两代)。见 [EUP Payne–Hanek](eup-paynehanek.md)。
---
## JF 与后续每代的差异
扁平的每个 `Instruction` 一个单元模型是 v2/v3 独有的。从 Pufferfish 起,`Performance` 变成堆上的 `latency[]` 数组加二维 `GetResourceUsage(Instruction, Resource)` 网格,`PfCycleTable::GetCyclesForThroughput`(`0x1c89de60`)包装 `GetResourceUsage` 调用,而不是读取扁平 offset-LUT。Viperfish 更进一步:`VfCycleTable::GetCyclesForThroughput`(`0x1c89e2c0`)分派到 `viperfish::MxuLatencyTable::GetResourceUsage`(已在反编译中确认),这是一个 `FlatHashMap<Modifier, array<int,19>>` 预留表,由 `{MatmulDataFormat, is_transpose, Msr}` 键控 — 那里的 matprep 阶段根本不是扁平单元,而是十九个 `MxuResource` 端口中的四个。
| Gen | Codename | TpuVer | 吞吐模型 | 资源列 | matmul base | matprep base | EUP edge |
|---|---|---|---|---|---|---|---|
| JF | Jellyfish | v2 | flat inline POD + offset-LUT | 7 | 88 | 8 | 4 |
| DF | Dragonfish | v3 | = JF + 2 cells | 7 | 66 | 13 | 4 |
| PF | Pufferfish | v4 | heap `latency[]` + 2-D grid | 20 | (grid) | (grid) | 7 |
| VF | Viperfish | v5p | `Modifier → array<19>` reservation | 28 | 131 | reservation | 6 |
| GL | Ghostlite | v6e | `Modifier → array<11>` reservation | 31 | (grid) | fixed rows | 13/14 |
| GF | 6acc60406 | v7 | `array<11>` reservation | 31 | (grid) | fixed rows | 12 |
systolic 契约 — weight-stationary 128×128 array、matpush 8×128/4×256 tile、latch / push / matmul / matres 序列 — 从不改变;只有成本模型编码在拓宽。跨代预留矩阵概念见 [MXU Latency Overview](mxu-latency-overview.md),各代页面见 [MXU Latency: PF](mxu-latency-pf.md)、[VF](mxu-latency-vf.md)、[GL](mxu-latency-gl.md)、[GF](mxu-latency-gf.md)。
---
## 相关组件
| 组件 | 关系 |
|---|---|
| `JfCycleTable::GetCyclesForThroughput` `0x1c89dce0` | 吞吐读取器(offsetLUT → `Performance` 单元) |
| `CycleTable::GetResource` `0x1c89ce20` | `Instruction → Resource` 列 LUT(`0xb438aec`) |
| `ResourceVector::Acc` `0x1c89adc0` | 将吞吐累加到 23 槽向量的消费者 |
| `PerformanceJf` ctor `0x1d4930c0` / `PerformanceDf` ctor `0x1d493060` | 内联 POD 填充和 2 单元 DF 覆写 |
| `LatencyTableJellyfish` ctor `0x1c8a0c20` | 15 字段 `Performance → LatencyTable` 复制映射 |
| `LatencyTableJellyfish::LatencyBetweenInternal` `0x1c8a0d60` | 按边延迟下限读取器(EUP edge、matmul/matprep base) |