XLU 冲突惩罚表
地址适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(BuildID md589edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,未 strip —nm -C可解析下文所有符号)。.text/.rodataVMA == 文件偏移;.data.rel.roVMA − 0x200000 == 文件偏移。其他版本会不同。
摘要
XluConflictPenaltyTable 是每个 (op,op) 延迟模型中非 MXU 结构冒险的一半。当两个跨 lane 操作在没有真实数据依赖的情况下,背靠背发射到同一个 XLU 时,第二个操作必须等第一个操作充分排空跨 lane 数据通路后才能进入该通路 — 这是基础操作延迟网格看不到的结构停顿。本表为该停顿定价。它直接对应 MxuLatencyTable 保留矩阵,但粒度粗得多:MXU 模型按资源保存周期数组索引,而 XLU 模型把每个跨 lane 操作折叠为六种 XluInstrType 之一,并索引一个平坦的 int32[from][to][vxpose] 惩罚网格。
熟悉的参照物是 LLVM SubtargetInfo::getWriteProcResBegin 的转发延迟 / read-advance 表:从生产者写到消费者读的一条边携带固定的旁路/冒险周期数,列表调度器把它加到依赖上。XluConflictPenaltyTable 是把这个想法用于一个功能单元,并按操作类别而不是寄存器来索引。两个操作必须在同一个 MXU 实例上(表按每个 MXU 实例建模,因此这里不建模跨实例冲突),第三个索引 vxpose — MXU 实例 id & 3 — 捕获奇/偶 XLU-FIFO 端口不对称性,使惩罚随实例倾斜。
本页记录三件事,并逐字节锚定到二进制:XluInstrType 枚举和 GetXluInstrType opcode 映射;写入器 SetXluConflictPenaltyBetween 与读取器 XluConflictPenaltyBetween 完全共享的表几何(单元地址算术、stored = value + 1 的“已设置”偏置、边界检查);以及从三个构造函数的 Set 调用序列直接转录的 Viperfish、Pufferfish、Ghostlite 每代完整惩罚矩阵。最后给出调度器端消费者 — LatencyTableViperfish::LatencyBetweenInternal 的 XLU 分支,它对 XLU 冒险项做普通 MAX 归约。
重新实现时,契约是:
XluInstrType的 6 值枚举,以及GetXluInstrType(LloValue*)的 opcode → 类型映射(包括vxpose_mode转置子表)。int32 cell[from][to][vxpose]几何:cell = base + 72·from + 12·to + 4·vxpose + 8;+1存储偏置;to < 6/vxpose < 3边界,from不检查。- 每代安装策略:VF/PF 的传播式 override(自动填充
B16packed 兄弟项)与 Ghostlite 的非传播式基础Set(每个单元显式设置)。 - 三个惩罚矩阵,以及
IsPacked/Packed16Version/IsTranspose分类器。 - 高层
XluConflictPenaltyBetween(LloValue*, LloValue*)的同 MXU 检查 + final-transpose 虚拟旁路,以及收取该单元值的LatencyBetweenInternalMAX 归约。
| 类 | xla::jellyfish::XluConflictPenaltyTable(嵌入在所属 LatencyTable + 0x18) |
| 写入器 | SetXluConflictPenaltyBetween(from, to, vxpose, value) @ 0x1c8a0140(基础,非传播式) |
| 单元读取器 | XluConflictPenaltyBetween(from, to, vxpose) @ 0x1c8a0180 |
| 值读取器 | XluConflictPenaltyBetween(LloValue*, LloValue*) @ 0x1c8a01c0 |
| Opcode → 类型 | GetXluInstrType(LloValue*) @ 0x1c89ff20(转置子表 @ 0xa2dcce0) |
| 类型 → 名称 | XluInstrTypeToString @ 0x1c8a16a0 |
| 单元算术 | int32, cell = base + 72·from + 12·to + 4·vxpose + 8;stored = value + 1 |
| 边界 | to ∈ [0,5] (ud1),vxpose ∈ [0,2] (ud1);from 由调用方保证 |
| VF / PF 安装 | LatencyTableViperfish::Set… @ 0x1c8a42e0 · LatencyTablePufferfish::Set… @ 0x1c8a17e0(字节相同,传播式) |
| 消费者 | LatencyTableViperfish::LatencyBetweenInternal @ 0x1c8a4ac0(XLU 分支 — 普通 MAX) |
| 置信度 | CONFIRMED(按字节锚定),除非某行另有说明 |
XluInstrType 枚举
目的
冲突模型定价的每个跨 lane 操作都会先归约为六种 XluInstrType 值之一 — “非 MXU 冒险字母表”。这是表的行/列空间;除了跨 lane 的种类和元素宽度外,操作的其他信息都会被丢弃。该枚举把 XLU 的工作拆分为跨 lane reduce、transpose(三种 packed 宽度),以及合并的 permute/rotate/broadcast 家族。
名称
XluInstrTypeToString @ 0x1c8a16a0 通过内联 strcpy(前五个)或 .rodata 引用(第六个,25 个字符 @ 0x84ddb81)发出每个值的字面名称。字符串长度也会一并写入,因此名称逐字节精确:
| value | name | 宽度 / 家族 | IsPacked? |
|---|---|---|---|
| 0 | kReduceB32 | 32-bit cross-lane reduce | no |
| 1 | kReduceB16 | 16-bit cross-lane reduce | yes |
| 2 | kTransposeB32 | 32-bit transpose | no |
| 3 | kTransposeB16 | bf16-packed transpose | yes |
| 4 | kTransposeB8 | 8-bit-packed transpose | yes |
| 5 | kPermuteRotateOrBroadcast | permute / rotate / broadcast-lane | no |
B32/B16/B8 后缀表示元素宽度 packing。三个 packed 变体 {1, 3, 4} 是下文 IsPacked 标记的项;B32(以及 permute)类型未 packed。
GetXluInstrType — opcode → 类型
GetXluInstrType(LloValue*) @ 0x1c89ff20 通过 switch 把 LloOpcode(WORD[value])映射到其 XluInstrType。逐字节如下:
int GetXluInstrType(LloValue* v): // sub_1C89FF20
switch (WORD[v]): // LloOpcode
case 0x8B: return 5; // kVectorSetPermutePattern → permute
case 0xA6: // kVectorTranspose
m = LloInstruction::vxpose_mode(v); // VxposeMode
if (m >= 4) return 3; // out-of-range → kTransposeB16
return xpose_subtable[m]; // .rodata @0xa2dcce0 = {2,3,4,2}
case 0xA7: return 3; // kVectorTransposeBinary → kTransposeB16
case 0xF5..0xFC: return 0; // cross-lane reduce/index → kReduceB32
case 0xFD..0x101: return 1; // (the B16 reduce band) → kReduceB16
default:
if (WORD[v] <= 0x3B && bt(0x0C40000000000000, WORD[v]))
return 5; // bits {0x36,0x3a,0x3b} → permute/rotate/bcast
LOG(FATAL) << "Unexpected XLU instruction: " << ToMnemonic(v); // latency_table.cc:206
```text
`.rodata 0xa2dcce0` 处的转置子表是四个 `int32` `{2, 3, 4, 2}`(`xxd` 已确认):`VxposeMode` 0 → `kTransposeB32`,1 → `kTransposeB16`,2 → `kTransposeB8`,3 → `kTransposeB32`。因此 `kVectorTranspose`(`0xa6`)从其 `vxpose_mode` 解析 packing 宽度;`kVectorTransposeBinary`(`0xa7`)固定为 `kTransposeB16`。reduce 按 opcode 区间拆分:`0xf5..0xfc` → `kReduceB32`,`0xfd..0x101` → `kReduceB16`。permute/rotate/broadcast 组是 opcode `{0x36, 0x3a, 0x3b}`(由 `bt 0x0C40000000000000` 位掩码匹配,bits 0x36/0x3a/0x3b 置位)加上 `0x8b`。
> **注意 —** 这里的 `vxpose_mode` 轴(`GetXluInstrType` 内的*元素宽度*选择器)不同于 `vxpose` 表索引(下文的 *MXU 实例 id* 选择器)。这个名称冲突并不理想;前者是从 transpose 操作读出的 `VxposeMode` 枚举,后者是 `mxu_id & 3`。关于真正的 `VxposeMode` 枚举,见 [Transpose-Reservation Latency](xpose-reservation-latency.md)。
---
## 表几何
### 布局与单元地址
惩罚网格是一个 `int32 cell[from=6][to=6][vxpose=3]` 块,嵌在所属的每代 `LatencyTable` 内 `+0x18` 处(因此 VF/PF override 写入会落在 `LatencyTable+0x20`)。写入器和读取器中的单元地址算术完全相同 — 已确认两次:
```c
// writer XluConflictPenaltyTable::SetXluConflictPenaltyBetween sub_1C8A0140
// reader XluConflictPenaltyTable::XluConflictPenaltyBetween sub_1C8A0180
int32* cell(base, from, to, vxpose):
if (to >= 6) ud1; // bounds: to ∈ [0,5]
if (vxpose >= 3) ud1; // bounds: vxpose ∈ [0,2]
return (int32*)(base + 72*from + 12*to + 4*vxpose + 8);
// (from is NOT range-checked — the caller guarantees from ∈ [0,5])from 步幅是 72 = 6·12 字节(一个 [to][vxpose] 平面);to 步幅是 12 = 3·4 字节(一个 vxpose 行);vxpose 步幅是 4 字节(一个 int32);+8 是每块头部跳过。活跃读取区域覆盖 base+8 .. base+8+72·6 = base+0x1b8。
高层读取器用 int32 单位表达同一个地址:this[18·from + 3·to + vxpose + 2](18·4 = 72,3·4 = 12,2·4 = 8)— 完全相同。
+1 存储偏置
每个 Set 变体都存储 value + 1,读取器返回原始存储字而不相减:
void SetXluConflictPenaltyBetween(from, to, vxpose, value):
*cell(base, from, to, vxpose) = value + 1; // inc — the +1 "is-set" bias
int XluConflictPenaltyBetween(from, to, vxpose):
return *cell(base, from, to, vxpose); // raw stored word
```text
> **易错点 —** 读取时**不会**减去 `+1`。从未写入的单元保持 `0`(“无条目”的哨兵);显式设置为 `k` 周期惩罚的单元保存 `k+1`,调度器收取的是**存储值** — 因此显式惩罚实际是 `k+1` 周期,而 `0` 周期惩罚(`Set(...,0)`)会存储 `1` 并收取 `1`。若重新实现时存储并返回原始惩罚,每个已设置单元都会少一,并且无法区分“未设置”和“设置为 0”。请存储 `value + 1`;收取存储字。下文所有惩罚表列出的都是**原始 `Set` 参数**;存储/收取值是它 `+ 1`。
### 初始化与活跃步幅
`InitializeConflictLatency` @ `0x1c8a0040` 在构造函数安装单元前将该块清零,但它按 **84 字节(`0x54`)步幅**遍历 **6 个子块**,在每个子块起始处写入 `{1, 1}`(`0x0000000100000001`)头部对,并用清零的 YMM 通过 `vmovups` 清除其余部分。读/写单元算术使用 **72 字节(`0x48`)from 步幅**,不是 84。
> **怪异点 —** init 步幅(84)与索引访问步幅(72)每块相差 12 字节。活跃表是 72 字节步幅区域(已在写入器和读取器中确认);每个 init 子块多出的 12 字节是保留/头部 padding,并且写在每个 init 子块起始处的 `{1,1}` 字不会被索引访问器读取。重新实现可完全忽略 84 字节 init 布局,直接清零一个平坦的 `int32[6][6][3]` 加上 `+8` 头部跳过;该差异是 init 例程的产物,不是第二张表。(FUNCTIONAL — 头部字不会被任何已跟踪读取消费。)
---
## 每代安装策略
三个已解码代际用两种不同方式安装单元,这个差异使矩阵大小看起来很不平衡(VF 18 次调用,PF 10 次,GL 56 次)。
### 传播式 override — Viperfish 和 Pufferfish
`LatencyTableViperfish::SetXluConflictPenaltyBetween` @ `0x1c8a42e0` 与 `LatencyTablePufferfish::SetXluConflictPenaltyBetween` @ `0x1c8a17e0` **逐字节相同**(唯一差异是 `LOG(FATAL)` 源字符串 — `latency_table_vf.cc:1119` vs `latency_table_pf.cc:145`)。二者都是*传播式*形式:一次 `Set(from, to, vxpose, value)` 会自动填充 `from` 与 `to` 的 `B16` packed 兄弟项:
```c
void Set_propagating(from, to, vxpose, value): // VF sub_1C8A42E0 ≡ PF sub_1C8A17E0
CHECK(!IsPacked(from) && !IsPacked(to)); // override only sets the *unpacked* cell
if (to >= 6) ud1; if (vxpose >= 3) ud1;
v = value + 1;
*cell(base, from, to, vxpose) = v; // [from][to][vxpose]
to16 = Packed16Version(to); // B16 sibling of `to`, if any
if (to16.present) *cell(base, from, to16, vxpose) = v; // [from][B16(to)]
from16 = Packed16Version(from); // B16 sibling of `from`, if any
if (from16.present):
vv = v + 8*(from == 3); // +8 iff from == kTransposeB16 (dead — see below)
*cell(base, from16, to, vxpose) = vv; // [B16(from)][to]
if (to16.present) *cell(base, from16, to16, vxpose) = vv; // [B16(from)][B16(to)]因此一次 VF/PF 调用最多写入四个单元:基础 unpacked 单元、它的 B16-to 列兄弟、B16-from 行兄弟,以及 B16×B16 角落。CHECK(!IsPacked(...)) 强制 override 只能用 unpacked 参数调用 — packed 单元完全通过传播到达。Packed16Version 把 kReduceB32 → kReduceB16、kTransposeB32 → kTransposeB16,并报告 kPermuteRotateOrBroadcast 没有 B16 版本(因此 permute from/to 不传播)。
怪异点 — 传播值中的
+8*(from == 3)增量在实践中是死代码。from == 3是kTransposeB16,会被IsPacked标记 — 而开头的CHECK(!IsPacked(from))禁止用from == 3调用 override。该增量是防御性代码,在实际安装序列下永远不会触发(VF/PF 只用from ∈ {0, 2, 5}调用)。重新实现可以省略它。
非传播式基础 — Ghostlite
LatencyTableGhostlite(ctor @ 0x1c8b0c00)不安装 override:它直接调用基础 XluConflictPenaltyTable::SetXluConflictPenaltyBetween @ 0x1c8a0140,后者只写一个单元且不传播。因此 Ghostlite 会显式设置它想要的每个单元,包括 B16/B8 packed 行列 — 56 次调用覆盖 27 个不同的 (from, to) 对(其中 kReduceB32 → kTransposeB8 这一对设置两次;第二次写入生效)。
惩罚矩阵
下方所有值都是原始 Set 参数(表存储 value + 1;调度器收取存储值)。XluInstrType:0 kReduceB32,1 kReduceB16,2 kTransposeB32,3 kTransposeB16,4 kTransposeB8,5 kPermuteRotateOrBroadcast(缩写为 Perm)。矩阵是有方向的(from → to),并按 vxpose = MXU 实例 id & 3 参数化。
Viperfish — LatencyTableViperfish ctor @ 0x1c8a3f20
18 次显式 Set 调用(传播式 override 自动填充 B16 兄弟项)。Viperfish 使用全部三个 vxpose 模式:
| from | to | vx=0 | vx=1 | vx=2 |
|---|---|---|---|---|
kReduceB32 | kTransposeB32 | 40 | 57 | 40 |
kReduceB32 | Perm | 19 | 24 | 23 |
Perm | kReduceB32 | 41 | 41 | 40 |
Perm | kTransposeB32 | 33 | 52 | 37 |
kTransposeB32 | kReduceB32 | 105 | 88 | 105 |
kTransposeB32 | Perm | 102 | 82 | 97 |
这些行/列的 B16 兄弟项(kReduceB16、kTransposeB16)会在 ctor 时通过传播填充为相同值 — 例如 kReduceB16 → kTransposeB16 携带与 kReduceB32 → kTransposeB32 相同的 40/57/40。
Pufferfish — LatencyTablePufferfish ctor @ 0x1c8a1960
10 次显式 Set 调用 — 5 个不同的 (from, to) 对,每个都在 vxpose 0 和 vxpose 1 上以相同值安装(Pufferfish 在两个模式间复制,而不是变化):
| from | to | vx=0 | vx=1 |
|---|---|---|---|
kReduceB32 | kTransposeB32 | 56 | 56 |
Perm | kTransposeB32 | 46 | 46 |
kReduceB32 | Perm | 17 | 17 |
kTransposeB32 | Perm | 96 | 96 |
kTransposeB32 | kReduceB32 | 86 | 86 |
注意 — PF ctor(
@0x1c8a1960)进行了 10 次SetXluConflictPenaltyBetween调用,包括显式的(2, 0, 1, 86)— 因此kTransposeB32 → kReduceB32单元在两个vxpose平面上都设置为 86。PF 矩阵在vxpose 0/1上完全对称。
Ghostlite — LatencyTableGhostlite ctor @ 0x1c8b0c00
56 次显式基础 Set 调用覆盖 27 个不同的 (from, to) 对(非传播路径直接设置 packed 行/列)。完整矩阵:
| from | to | vx=0 | vx=1 |
|---|---|---|---|
kReduceB32 | kTransposeB32 | 44 | 50 |
kReduceB32 | kTransposeB16 | 44 | 44 |
kReduceB32 | kTransposeB8 | 36 | 32 |
kReduceB32 | Perm | 21 | 16 |
kReduceB16 | kTransposeB32 | 48 | 54 |
kReduceB16 | kTransposeB16 | 48 | 48 |
kReduceB16 | Perm | 25 | 20 |
kTransposeB32 | kReduceB32 | 44 | 38 |
kTransposeB32 | kReduceB16 | 44 | 38 |
kTransposeB32 | kTransposeB16 | 35 | 29 |
kTransposeB32 | kTransposeB8 | 39 | 29 |
kTransposeB32 | Perm | 40 | 29 |
kTransposeB16 | kReduceB32 | 12 | 12 |
kTransposeB16 | kReduceB16 | 12 | 12 |
kTransposeB16 | kTransposeB32 | 3 | 9 |
kTransposeB16 | kTransposeB8 | 7 | 3 |
kTransposeB16 | Perm | 8 | 3 |
kTransposeB8 | kReduceB32 | 16 | 20 |
kTransposeB8 | kReduceB16 | 16 | 20 |
kTransposeB8 | kTransposeB32 | 15 | 25 |
kTransposeB8 | kTransposeB16 | 15 | 19 |
kTransposeB8 | Perm | 4 | 3 |
Perm | kReduceB32 | 30 | 35 |
Perm | kReduceB16 | 30 | 35 |
Perm | kTransposeB32 | 29 | 40 |
Perm | kTransposeB16 | 29 | 34 |
Perm | kTransposeB8 | 17 | 18 |
易错点 —
kReduceB32 → kTransposeB8对在 GL ctor 中安装了两次:先是(0, 4, 0, 32)/(0, 4, 1, 28),然后是(0, 4, 0, 36)/(0, 4, 1, 32)。第二次写入生效,因此实时存储值是 36 / 32(原始),不是第一次的 32 / 28。若读者在该单元首次安装处停止,会记录错误惩罚。
数值含义
该矩阵编码 XLU FIFO-push 顺序冒险。重新实现者可以依赖几个模式:
- 方向很重要。
kTransposeB32 → kReduceB32是代价最高的边,明显高于其他边(VF105周期 — 完整 XLU 排空 — 而反向代价小得多),表示 transpose 后接 reduce 会使跨 lane 排空流水线停顿。PF 对同一方向定价为86。 - Packing 会降低成本。 最宽的 unpacked
B32 → B32对成本最高;B16/B8packed 变体逐步降低(GLkTransposeB16 → Perm = 8/3,kTransposeB8 → Perm = 4/3)。 vxpose索引会倾斜成本。 在 Viperfish 上,kReduceB32 → kTransposeB32是40 / 57 / 40— 实例 1 比实例 0 和 2 多+17,体现奇/偶 XLU-FIFO 端口不对称。Ghostlite 同样在vxpose 0/1间不同(例如Perm → kTransposeB32 = 29/40)。Pufferfish 不随vxpose变化(每个对都在 0/1 上复制)。
值读取器 — XluConflictPenaltyBetween(LloValue*, LloValue*)
XluConflictPenaltyBetween(LloValue* A, LloValue* B) @ 0x1c8a01c0 是调度器用两个指令值调用的形式。它分类二者,强制同 MXU 不变量,然后要么走动态 transpose-reservation 虚拟路径,要么读取静态单元。逐字节如下:
int XluConflictPenaltyBetween(LloValue* A, LloValue* B): // sub_1C8A01C0
fromType = GetXluInstrType(A);
toType = GetXluInstrType(B);
// both must have a valid MXU/unit id (bit 0x400 of WORD[v+0xb])
CHECK(A.unit_id().has_value()); // latency_table.cc:245
CHECK(B.unit_id().has_value()); // latency_table.cc:246
CHECK(A.unit_id() == B.unit_id()); // latency_table.cc:248 — SAME MXU
if (LloInstruction::IsFinalTransposeInSequence(A)):
CHECK(IsTranspose(GetXluInstrType(A))); // latency_table.cc:322/328
m = A.vxpose_mode();
h = A.GetTransposeHeight(); w = A.GetTransposeWidth();
return this->vtable[+0x10](m, fromType, toType, A.unit_id()&3, h, w); // XposeXLUReservationLatency
vxpose = A.unit_id() & 3; // MXU instance → table index
if (vxpose == 3) ud1; // instance 3 has no table column
return cell[fromType][toType][vxpose];
```text
两个关键行为:**同 MXU `CHECK`**(`unit_id()` 是 `WORD[v+0xb]` 的 MXU 实例 bits 8-9,由 bit `0x400` 表示存在)意味着本表只定价*一个 MXU 实例内*的冲突 — 否则触发 `LOG(FATAL)`;**final-transpose 旁路**会把结束一个序列的 transpose 路由到动态的、高度/宽度相关的 `XposeXLUReservationLatency` 虚拟函数(`vtable[+0x10]`),而不是静态单元。该虚拟函数记录在 [Transpose-Reservation Latency](xpose-reservation-latency.md);在 Viperfish(`@0x1c8a4e60`)上它是 `XluConflictPenaltyBetween(fromType, toType, vxpose) + max(0, width − height) + 7` — 即静态单元**加上**数据形状项。
> **易错点 —** `vxpose` 索引是 MXU 实例 id,并且读取器会在实例 **3** 上陷入(`ud1`)。表只有三个 `vxpose` 列,但一个 TensorCore 可能有第 4 个 MXU 实例。冲突模型假设两个冲突操作永远不会同时落在实例 3;落在那里的一对操作会崩溃。(安装序列中没有 instance-3 路径可达 — VF/PF/GL 只填充列 0-2 — 因此这是调用方必须维持的真实不变量,不是死防御代码。)
---
## 调度器如何收取惩罚
冲突单元是 `LatencyTableViperfish::LatencyBetweenInternal` @ `0x1c8a4ac0` 在非真实依赖、非(MXU∧MXU)边上用**普通 MAX** 归约的几个 XLU 冒险项之一 — 与 MXU 结构冒险路径类似。XLU 分支逐字节如下:
```c
long LatencyBetweenInternal(LloValue* A, LloValue* B): // VF sub_1C8A4AC0 (vtable+0x18)
if ((WORD[A] - 233) < 4) return 0; // ops 0xe9..0xec: no edge
base = GetLatency(A); // intrinsic op latency
true_dep = this->vtable[+0x20](A, B); // IsTrueDependencyBetween (vtable+0x20)
if (!true_dep && LloOpcodeUsesMxu(A) && LloOpcodeUsesMxu(B))
return MxuLatencyTable::GetLatencyBetween(this+0x1d8, A, B);// not-true-dep ∧ both-MXU → MXU matrix
// --- the XLU arm (this page) ---
// true-dependency does NOT early-return; it only suppresses the MXU branch.
// r seeds at base when true_dep, else at 0 (the not-true-dep, non-both-MXU case).
r = (true_dep ? base : 0);
if (HasSetPermutePatternReservation(A, B)): // both involve a set-permute push, same MXU
rsv = GetXluPathReservation(A); // XLU-slot occupancy gate
r = max(base, rsv);
r = max(r, XluConflictPenaltyBetween(GetXluInstrType(A), GetXluInstrType(B), A.unit_id()&3));
if (IsFinalTransposeFollowedByResult(A, B)):
r = max(r, this->vtable[+0x30](A, B, base)); // LatencyBetweenXposeInstrAndResult
if (ArePushesToSameXluFifo(A, B)): // both push the same XLU FIFO, same MXU
r = max(r, XluConflictPenaltyBetween(A, B)); // the LloValue* form (final-transpose aware)
if (IsIndexedStoreFollowedByLoad(A, B)) r = max(r, 5);
if (IsSetIarFollowedByIndexedLoad(A, B)) r = max(r, 8);
if (WORD[A] == 21 && WORD[B] == 22 && r < 31) r = 30; // cross-lane-max → max-index fixup
if (WORD[A] == 25 && WORD[B] == 26 && r < 37) r = 36; // cross-lane-min → min-index fixup
return max(r, GetResourceLatency(A, B));因此,当两个跨 lane 操作在同一个 MXU 上 push 同一个 XLU FIFO 时,会收取 XLU 冲突惩罚 — 即相邻 permute/reduce/transpose 操作之间的结构冒险成本。它两次进入 MAX:一次通过类型索引单元(在 HasSetPermutePatternReservation 下),一次通过 LloValue* 形式(在 ArePushesToSameXluFifo 下;当生产者结束 transpose 序列时会分派到 final-transpose 虚拟路径)。真实数据依赖(IsTrueDependencyBetween,vtable+0x20)不会短路 XLU 分支 — 它只抑制 both-MXU 分支,并把运行 MAX r 的初值从 0 改为 base,因此冲突单元仍会被 MAX 折入。both-MXU MXU 矩阵分支只在该边不是真实依赖且两个操作都使用 MXU 时采用。
门控 helper(全部按字节确认):
| Helper | 地址 | 门控 |
|---|---|---|
HasSetPermutePatternReservation | 0x1c89fe00 | A/B 之一是 kVectorSetPermutePattern(0x8b),LloInstructionPushesToXluFifos,同 unit id |
ArePushesToSameXluFifo | 0x1c8a05a0 | 二者都 LloInstructionPushesToXluFifos,同 unit id |
IsFinalTransposeFollowedByResult | 0x1c8a0500 | A 是 final-in-sequence transpose,且喂给 B 的 result |
GetXluPathReservation (VF) | 0x1c8a3200 | op 0x8b → 1(若字段 +0x40 置位则 8);否则 ViperfishPerformance::GetResourceUsage(instr, 14) |
注意 —
GetXluPathReservation(@0x1c8a3200)特殊处理 op0x8b(kVectorSetPermutePattern):它返回1,或在+0x40字段非零时返回8;其他所有 op 返回ViperfishPerformance::GetResourceUsage(instr, /*resource=*/14)。0x8b分支还运行一个软LloCheckForFailure,确认 opcode 是kVectorSetPermutePattern。
交叉引用
- XLU 操作清单 —
GetXluInstrType分类的 LLO 跨 lane opcode(0x36/0x3a/0x3b/0x8b/0xa6/0xa7/0xf5..0x101/…),以及在收取该成本前运行的 XLU 操作合并流水线。 - Transpose-Reservation 延迟 —
XposeXLUReservationLatency(vtable[+0x10])、final-transpose 旁路路由到的动态高度/宽度 transpose 路径,以及VxposeMode枚举。 - XLU Combine / Source-Bus —
ComputeCombinablePairs/AssignSourceBus与独立的PreXluAssignmentLatencyTable(ceil(base / xlu_count))优化器边模型 — 不是本表。 - XLU Reemit Cost —
CyclesAddedByXluOperation,XLU 优化器通过自身延迟表消费的边际延迟函数。 - MXU 延迟概览 — 本表对应的 MXU 结构冒险同级项(
MxuLatencyTable::GetLatencyBetween);LatencyBetweenInternal的第二个分支。 - 资源枚举 — 23 槽
ResourceVector,其中R[2] = Xlu是该更细 per-(op,op) 冒险所细化的粗周期权重桶。 - LLO Opcode 枚举 —
GetXluInstrTypeswitch 所用的LloOpcode编号。