XLU Reemit 成本
本页的每个 opcode、地址、偏移和字段都逐字节读取自
libtpu-0.0.40-cp314wheel 中的libtpu.so(BuildID md589edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,未 strip — 完整 C++ 符号,nm -C可解析每个方法)。.text与.rodataVMA 等于其文件偏移;.data.rel.roVMA − 0x200000 = 文件偏移。其他 libtpu 构建会不同。
摘要
XLU 优化器(LloXluGraphOptimizer::Optimize,xlu-op-roster)是五阶段重写:合并相邻的相同跨 lane 操作、在每代 XLU 单元间平衡它们、重排它们以缩短关键路径、将融合/重排后的操作重新发射为真实 LLO,然后打包源总线。前四个阶段是决策;第五个(AssignSourceBus)是布线。每个决策 — 融合哪一对、装载哪个单元、接下来调度哪个 ready 操作 — 都依赖一个边际延迟数,而这个数由一个函数计算:CyclesAddedByXluOperation(@0x126d22a0)。本页是该成本函数闭式形式的权威参考,说明 ReemitReorderedCombinedXluOperations(@0x126d5460)在重新发射时如何收取每个操作的新增周期,以及 PerXluOperations 每 XLU 调度器状态结构(步幅 0x60)如何在调度步骤之间携带运行成本。
成本很小且精确。对于接在该 XLU 上一个操作 prev 后的新操作 cur,成本是在优化器自己的 PreXluAssignmentLatencyTable 上的一条 LatencyBetween(prev, cur) 边(即 ceil(base / xlu_count) 并行度折扣后的边,xlu-op-roster),再加上 — 若 prev 是 RPU — 其两个源操作数中每个不是跨区域边界值(from/to)的操作数对应的一次 LatencyBetween 惩罚;或者 — 若 prev 是 transpose — 第一两个 tile 元素之间延迟的 (read_set_size − 1) 份。XluControlOperation 从不定价(如果它到达成本路径,会硬 LogFatal)。AssignXlu 的最小成本 bin-pack、重排列表调度器的 max-heap 优先级($_3)以及 combine DP 都逐字调用同一个闭式形式 — 锚定这个表达式就锚定了整个 XLU 调度成本模型。
本页按 re-emission 半条流水线如何消费成本来描述。重排列表调度器把每操作成本累积到每 XLU 的 PerXluOperations 记录中,优先调度最长边际成本的 ready 操作,推进每 XLU 完成时钟,并把有序操作向量与可合并对向量交给 Reemit,后者为每一对物化一个融合 LLO 操作。成本记账存在于调度器状态中;Reemit 消费其输出。下文记录这两半。
重新实现时,契约是:
CyclesAddedByXluOperation闭式形式 — variant→anchor 解析、主LatencyBetween边、两个 RPU per-source 边界未命中惩罚、transpose(n−1)乘数、control-opLogFatal。- 成本读取的 variant 操作数布局:
RpuOperation将两个源LloValue*内联存储在[+0x00]/[+0x08];TransposeTile存储其 read-set(ptr[+0x00],size[+0x08])。 PerXluOperations0x60字段图,以及每个已调度操作更新它的三次写入:[+0x38]剩余周期递减、[+0x58]完成时钟 max、[+0x40]/[+0x48]/[+0x50]anchor/last-op 记录。- 每 XLU max-heap 组成:
pair<long,long> {marginal_cost, op_index}使用less<>,因此最长成本 ready 操作先弹出,相同成本由更高 op-index 打破。
| 成本函数 | CyclesAddedByXluOperation @ 0x126d22a0(大小 0x1e0)→ long |
| Anchor 解析器 | GetAnchorInstruction @ 0x126cda00(variant → anchor LloValue) |
| 操作数读取器 | LloValue::operands(int) @ 0x10b8e040(向后存储的 source span) |
| 边表 | LatencyTable::LatencyBetween @ 0x1c89f820(vtable +0x18 = PreXlu ceil(base/xlu_count)) |
| Re-emitter | ReemitReorderedCombinedXluOperations @ 0x126d5460(大小 0x1c75)→ absl::Status |
| 调度器 | ReorderToShortenCriticalPath @ 0x126d3460;PerXluOperations 步幅 0x60 |
| 堆优先级 | $_3 @ 0x126d9240 — 尾调用 CyclesAddedByXluOperation |
| 提交 | $_1 @ 0x126d8b60 — 删除 pending idx,推进时钟,记录 anchors |
| 置信度 | CONFIRMED(按字节锚定),除非某行另有说明 |
成本函数 — CyclesAddedByXluOperation
目的
CyclesAddedByXluOperation 回答一个问题:如果我把操作 cur 立即放在同一个 XLU 上的操作 prev 后面,会增加多少周期? 它是在优化器 PreXluAssignmentLatencyTable 上评估的,将一个跨 lane 操作接在另一个之后的边际延迟。它是 XLU 流水线中唯一的成本函数;combine DP、单元 bin-packer 和重排堆都会调用它。不存在单独的“fuse cost”或“reorder cost” — 它们都是同一个表上的同一个数。
签名
// xla::jellyfish::(anon)::CyclesAddedByXluOperation @ 0x126d22a0
long CyclesAddedByXluOperation(
const variant* prev, // a1 — the XLU's last-scheduled op (or null)
const variant* cur, // a2 — the op being chained after prev (or null)
const LloValue* from, // a3 — cross-region boundary value "from"
const LloValue* to, // a4 — cross-region boundary value "to"
LatencyTable& tbl); // a5 — the PreXluAssignmentLatencyTable
```text
variant 是 `std::variant<TransposeTile, RpuOperation, XluControlOperation>`,判别字节位于 `[v+0x40]`:`0` = `TransposeTile`,`1` = `RpuOperation`,`2` = `XluControlOperation`(该布局在 `GetAnchorInstruction`、`$_3` 堆 lambda 和成本函数体中均已确认)。`from`/`to` 对是 combine 读过的跨区域边界:从 `AssignXlu` 传入时,它们是所选 XLU 记录的 anchor 对;从重排 `$_3` 传入时,它们是 `PerXluOperations[xlu][+0x40]`/`[+0x48]`(上一个已调度操作的两个源操作数)。
### Variant → Anchor 解析
`prev` 和 `cur` 都以相同方式解析为 anchor `LloValue`(内联在成本函数体中,与 `GetAnchorInstruction` @ `0x126cda00` 相同):
```c
// resolve(v) → the variant's anchor LloValue*; op_data(v) = [resolve(v) + 0x10]
LloValue* resolve(const variant* v) {
switch (v->discr /* [v+0x40] */) {
case 1: /* RpuOperation */ return *(LloValue**)(v + 0x10);
case 2: /* XluControlOperation */ return *(LloValue**)(v);
case 0: /* TransposeTile */
// last element of the read-set InlinedVector: ptr [v+0x00], size [v+0x08]
if (size == 0) BUG();
return *(LloValue**)( readset_ptr + 8*size - 8 );
}
}op_data(v) — 延迟表索引的值 — 是anchor instruction 块更深一层的间接引用:op_data(v) = *(LloValue**)(resolve(v) + 0x10)。GetAnchorInstruction 中的 XluControlOperation 分支是 LogFatal("control != nullptr",第 229 行)— control op 只解析其内联值,从不解析 anchor。
算法(闭式形式)
来自反编译的逐字节形式。返回累加器从主边开始,并由 per-source / per-chunk 惩罚递增。
// CyclesAddedByXluOperation @ 0x126d22a0 — closed form
long cost = 0;
if (cur == null) {
// empty-XLU base case: only a TransposeTile prev contributes anything
if (prev == null || prev->discr != 0 /*not TransposeTile*/) return 0;
goto transpose_tail; // prev->discr == 0
}
if (prev == null) CHECK_fail("control != nullptr", 229); // resolve(prev) needs a non-null prev
// MAIN EDGE: latency between cur's anchor op-data and prev's anchor op-data
cost = (int) tbl.LatencyBetween( op_data(cur), op_data(prev) ); // @0x126d236b
if (prev->discr != 0 /*not TransposeTile*/) {
if (cur->discr == 0 /*cur is TransposeTile*/) return cost; // skip operand penalties
CHECK(prev->discr == 1 /*RpuOperation*/, "rpu_op != nullptr", 1012); // control prev ⇒ LogFatal
// RPU prev: two inline SOURCE LloValue* at [prev+0x00], [prev+0x08]
LloValue* s0 = *(LloValue**)(prev + 0x00);
if (s0 != null && s0->operands(0) != from) // operand(0) @0x10b8e040
cost += (int) tbl.LatencyBetween( GetAnchorInstruction(cur), s0 ); // @0x126d23b4
LloValue* s1 = *(LloValue**)(prev + 0x08);
if (s1 != null && s1->operands(0) != to)
cost += (int) tbl.LatencyBetween( GetAnchorInstruction(cur), s1 ); // @0x126d23e6
return cost;
}
transpose_tail: // prev is TransposeTile
{
long n = *(long*)(prev + 0x08); // read-set InlinedVector size
if (n >= 2) {
LloValue* e0 = op_data( readset[0] ); // [ readset_ptr[0] + 0x10 ]
LloValue* e1 = op_data( readset[1] ); // [ readset_ptr[1] + 0x10 ]
cost += (n - 1) * (int) tbl.LatencyBetween( e0, e1 ); // imul @0x126d2418
}
}
return cost;
```text
> **怪异点 — 惩罚的第二个参数是原始 source value,不是 `op_data`。** 主边定价 `LatencyBetween(op_data(cur), op_data(prev))` — 两个参数都经过 `+0x10` anchor-instruction 间接引用解析。两个 RPU 操作数惩罚定价 `LatencyBetween(GetAnchorInstruction(cur), s0)`,其中 `s0 = *(LloValue**)(prev+0x00)` 是内联源操作数指针**本身**(第二个参数没有 `op_data` 间接引用)。若重新实现时把惩罚项中的 source operand 也经过 `op_data`,会计算出不同且错误的边。该不对称位于二进制 `@0x126d23a6`/`@0x126d23b4`(`GetAnchorInstruction` 调用解析 `cur`;source 指针原样传入)。
### 三项的解释
| 项 | 何时 | 值 |
|---|---|---|
| 主边 | always (`cur != null`) | PreXlu 表上的 `LatencyBetween(cur_anchor, prev_anchor)` |
| RPU source-0 miss | `prev` 是 RPU,`cur` 不是 transpose,`s0->operands(0) != from` | `+ LatencyBetween(cur_anchor, s0)` |
| RPU source-1 miss | 同上,`s1->operands(0) != to` | `+ LatencyBetween(cur_anchor, s1)` |
| Transpose chain | `prev` 是 TransposeTile | `+ (read_set_size − 1) * LatencyBetween(elem0, elem1)` |
主导项是每个 `(op,op)` 边 — 在 XLU 的上一个操作之后发射新操作的成本。对于 RPU 操作,**每个不是跨区域边界值**(`from`/`to`)的源操作数都会再加一条边:在 XLU 上物化非边界源的 fan-in 冒险。边界操作数 — 跨过 region 边的值 — 是免费的,因为它们已经驻留在边界处。对于 transpose 链,新增成本是第一个 chunk 之外每个*额外* chunk 的一次元素间延迟:更长的 transpose 序列成本按比例增加。
> **注意 — `XluControlOperation` 按构造不可定价。** 作为 `prev` 的 control op 会落入 `prev->discr == 1` CHECK 并 `LogFatal`(`"rpu_op != nullptr"`,第 1012 行)。这与 control op 永不 combinable 一致([`xlu-combine-sourcebus`](xlu-combine-sourcebus.md)):它们没有成本可定价的源操作数,因此优化器保证这类操作不会到达该分支。
### `LloValue::operands(int)` — 边界未命中探测
两个 RPU 惩罚把 `s->operands(0)` 与边界值比较。`operands` 读取向后存储的 source span(`@0x10b8e040`,逐字节):
```c
// LloValue::operands(this, i) @ 0x10b8e040
int count = *((int*)this - 4); // [this - 0x10]
int n_src = count - (*(u16*)((char*)this + 0xb) & 3);// subtract the 2-bit predication/aux tag
CHECK(i < n_src, "index < operands_size()", 499); // else LogFatal
return *((QWORD*)this + i - count - 2); // [this + (i - count)*8 - 0x10] — stored backwardoperands(0) 是生产者值 s 的第一个源操作数 — 边界测试比较的生产者链头。操作数布局在 value header 之前(负偏移),因此 operands(0) 是存储最深的元素。
边 — LatencyBetween 与 PreXlu 表
成本调用 LatencyTable::LatencyBetween(@0x1c89f820),其第一步是通过 vtable[+0x18] 虚分派 — 即 LatencyBetweenInternal override。在优化器的 PreXluAssignmentLatencyTable 上,该 override 是 XLU↔XLU 对的 ceil(base / xlu_count) 并行度折扣边(记录于 xlu-op-roster);随后 public LatencyBetween 应用两个调整:
// LatencyTable::LatencyBetween @ 0x1c89f820 (after vtable[+0x18] base lookup → v4)
if (this[+0x10] /* random buffer */ != 0)
v4 += UniformDistribution<int>(0, 101); // stochastic perturbation (0..0x65)
if (cur.op == 0x84 && prev.op == 0x84) // both kVectorWeird-band: floor at AutoOr<int> proto (def 16)
v4 = max(v4, proto_floor);
else if (cur.op == 0x82 && (prev.op - 0x82) <= 2 && v4 < 3) // 0x82-band edge: floor at 2
v4 = 2;
return (unsigned) v4;
```text
> **易错点 — 边不是完全确定的。** 当 `LatencyTable[+0x10]`(惰性分配的 `BitGen` buffer)非空时,每条边都会加上 `UniformDistribution(0, 0x65)` 抖动(`@0x1c89f84c`)。由于 `CyclesAddedByXluOperation` 是整个 XLU schedule 依赖的单一成本,非空 random buffer 会使整个 schedule 随机化。启用条件(debug fuzzing 还是生产中始终关闭)未在这里隔离 — `LatencyTable[+0x10]` 默认是 `0`,因此确定性路径是默认路径 — 但重新实现若要复现 schedule 顺序,必须考虑它。启用条件为 **LOW**;扰动算术为 CONFIRMED。
两个 opcode-band floor(`0x84`/`0x82` band → 最小延迟钳制)属于 public `LatencyBetween`,会应用于成本定价的每条 XLU 边。它们在函数体中已 CONFIRMED,但每个 opcode 的覆盖范围随 [latency table](../isa/xlu-op-roster.md) 记录;这里只说明它们是 XLU 边不会低于其 band floor 的原因。
---
## 收取成本 — `ReemitReorderedCombinedXluOperations`
### 目的
`Reemit` 是调度器选择顺序与融合之后运行的 IR 重写步骤。到它运行时,每个成本决策已经做出并提交到 `PerXluOperations` 记录;`Reemit` 消费有序操作向量和可合并对向量并物化 LLO。它**不会**重新定价任何东西 — 成本记账完全在它之前的调度器中。`Reemit` 对成本的作用是遵守它:调度器按一个融合操作定价的可合并对,会被发射为恰好一个跨 lane 操作,因此成本模型收取的周期(一个主边、一次共享 pattern setup)与发射出的操作实际消耗的周期匹配。
### 签名与边界穿线
```c
// xla::jellyfish::(anon)::ReemitReorderedCombinedXluOperations @ 0x126d5460
absl::Status Reemit(
LloDependencyGraph* graph, // a1
LloInstruction* from_instr, // a2 (WORD*) — cross-region boundary "from" instruction
LloValue* from_val, // a3 (WORD*) — boundary "from" value
LloInstruction* to_instr, // a4 — boundary "to" instruction
LloValue* to_val, // a5 — boundary "to" value
vector<variant>* reordered, // a6 — the ReorderToShortenCriticalPath output (stride 0x48)
vector<pair<variant*,variant*>>* pairs // a7 — the combinable-pair vector (stride 0x10)
);传入 Reemit 的边界 from/to 与 CyclesAddedByXluOperation 定价时使用的同一边界对相同:等于边界值的操作数在成本中是免费的,并会在边界修复时重新连接(而不是重新发射)。成本模型与 emitter 对跨越 region 边的内容达成一致。
每操作成本如何映射到发射周期
Reemit 开头有一个 RET_CHECK(第 863 行),要求 reordered 非空当且仅当 pairs 非空,然后构建新的 emission LloRegionBuilder 和每 XLU 的 PerXluState 数组(步幅 0x20)— “当前已设置 pattern” 缓存([+0x00]/[+0x08] permute source/result,[+0x10]/[+0x18] segment source/result;完整机制见 xlu-op-roster)。对于每个可合并对,它发射一个融合操作。成本记账含义如下:
| Pair kind | 成本收取内容 | Reemit 发射内容 | 周期对应关系 |
|---|---|---|---|
| RPU reduce pair (idx 1) | 一个主边 + per-source 边界未命中惩罚 | 一个融合跨 lane reduce,共享单个 Vsetperm/Vsetspr pattern prologue(按 XLU 缓存) | 共享 pattern op 是成本中免费的边界值;融合 body 是一个已定价主边 |
| Transpose pair (idx 0) | 一个主边 + (n−1) 个 chunk 间延迟 | 一个融合 Vxpose,重新安置两个 tile 的指令,每个结果 chunk 一个 Vxposeres | (n−1) 个已定价 chunk 延迟 = 融合 transpose 占用的 chunk 数(受 slot-fit 门控,xpose-reservation-latency) |
注意 — 融合收益正是免费的边界操作数。
CyclesAddedByXluOperation不为等于from/to的源操作数收费。两个可合并 reduce 共享的SetPermutePattern/SetSegmentPatternop 就是该边界值:每个 XLU 发射一次,缓存在PerXluState中并复用。成本模型与 emitter 从两侧编码同一种收益 — 成本不支付共享 pattern,Reemit只发射一次。共享一个 pattern 的N个 reduce 只支付一次 setup;成本通过只定价每操作主边和非共享 source 来反映这一点。
在每对发射和边界修复之后,Reemit 删除每个被取代的原始 op(按 variant kind 对 reordered 调用 RemoveNode),并返回 absl::Status。发射和 re-home 机制(factory、ReplaceUsesOfInstruction、PopInstruction/AppendInstruction)是 xlu-op-roster 的 IR 重写关注点;本页的成本关注点是发射出的指令数量与共享 pattern 复用匹配调度器收取的周期。
调度器状态 — PerXluOperations
目的
ReorderToShortenCriticalPath(@0x126d3460)是一个延迟加权列表调度器。它为每个 XLU 单元持有一个 PerXluOperations 记录(xlu_count 个元素的 vector<PerXluOperations>,在 @0x126d34d1 分配 operator new(96 * xlu_count) → 步幅 0x60)。该记录在调度步骤之间携带运行成本:pending op-index 集合、此 XLU 上仍待调度的剩余周期、上一个已调度操作(下一次 CyclesAddedByXluOperation 作为 prev 使用)、其两个源操作数(下一次成本定价使用的 from/to),以及每 XLU 完成时间时钟(重排要缩短的关键路径前沿)。
字段图(步幅 0x60)
来自每元素构造函数(@0x126d3530/@0x126d3590)、Phase A 累积以及 $_1/$_2/$_3 lambda 访问的逐字节信息。
| Offset | Type | 字段 | 写入者 / 读取者 |
|---|---|---|---|
+0x00 .. +0x38 | absl::btree_set<long, less, alloc, 256> | 每 XLU pending op-index set | ctor 播种两个 EmptyNode ptr(@0x2181d930);Phase A 插入 indices;$_1 删除已调度 index(@0x126d8ccd) |
+0x38 | i64 | 剩余运行周期累加器(Σ 尚未调度 op 的成本) | Phase A += cost[i](@0x126d3942,[+56] += v28);$_1 −= cycles[idx](@0x126d8fdc,v20[7] -= …) |
+0x40 | LloValue* | anchor "from" = 上一个已调度 op 的源操作数 0 | $_1 写 v65[8] = src0(@0x126d8f41);下一个 $_3/CyclesAdded 作为 from 读取 |
+0x48 | LloValue* | anchor "to" = 上一个已调度 op 的源操作数 1 | $_1 写 v20[9] = src1(@0x126d8f73);下一个成本作为 to 读取 |
+0x50 | variant* | 此 XLU 上的上一个已调度 op(下一次的 prev) | $_1 写 v20[10] = *op(@0x126d8fe3);Phase A 和 $_3 读取(@0x126d92c2) |
+0x58 | i64 | 每 XLU 完成时间时钟(关键路径前沿) | $_1 [+0x58] = max([+0x58] + cost, finish[idx])(@0x126d8fad,v20[11]);$_2 关键路径测试 setge(@0x126d91f0) |
[+0x08..+0x38) 区域之前按结构被读作 opaque;它是 absl::btree_set 内部(root + rightmost EmptyNode 指针位于 +0x00/+0x08,然后是 size/height/internal)。每元素析构函数只清除 [+0x00..+0x38) 上的 btree_set;四个调度器字段 [+0x38..+0x60) 是普通 i64/ptr。
成本如何流经记录
// ReorderToShortenCriticalPath — Phase A (cost pre-pass, @0x126d3460 body)
for each op i assigned to xlu:
cost[i] = CyclesAddedByXluOperation(prev=PerXlu[xlu].last_op, cur=op_i,
from=PerXlu[xlu][+0x40], to=PerXlu[xlu][+0x48], tbl); // @0x126d38f8
PerXlu[xlu][+0x38] += cost[i]; // remaining-cycles accumulator
PerXlu[xlu].pending.insert(i); // btree_set
// Phase B — per-XLU max-heap priority_queue<pair<long,long>, …, less<>>
heap.emplace({ cost = $_3(op), op_index }); // @0x126d8980 / push @0x126d3a2c
top = heap.pop(); // longest cost first, ties → higher idx
if ($_2.ready_and_worth(top)) // XluOperationIsReady && [+0x58]+cost >= finish[idx] (setge @0x126d91f0)
$_1.commit(top); // erase pending, advance clock, record anchors+last-op (@0x126d8b60)
```text
`$_3` 边际成本 lambda(`@0x126d9240`)是对 `CyclesAddedByXluOperation` 的一次*直接尾调用*,参数为该 XLU 的上一个已调度操作:
```c
// $_3 @ 0x126d9240 — heap priority for candidate op `cur`
v8 = PerXlu[xlu][+0x50]; // last-scheduled op (prev)
if (v8) {
if (cur.discr == 1 && prev.discr == 1) // both RPU: price with boundary from/to
return CyclesAddedByXluOperation(cur, prev, PerXlu[xlu][+0x40], PerXlu[xlu][+0x48], tbl); // @0x126d92f4
else
return CyclesAddedByXluOperation(prev, cur, 0, 0, tbl); // @0x126d930a
} else { // empty XLU: only a transpose cur contributes
if (cur.discr != 0) return 0;
n = cur.read_set_size; // [cur+0x08]
return (n < 2) ? 0 : (n-1) * LatencyBetween(readset[0], readset[1]);
}这决定性地确认了重排堆优先级、AssignXlu bin-pack key 和 combine DP 权重都是同一张表上的同一个函数。XLU 成本表达式只有一个,$_3 通过尾调用到达它。随后 $_1 commit 会按恰好该定价成本推进完成时钟([+0x58] = max([+0x58]+cost, finish[idx])),并从剩余周期累加器中减去每操作 cycles[idx],使两个成本视角(frontier 与 remaining)在每个已调度操作后保持一致。
每 XLU 堆组成
候选堆是 priority_queue<pair<long,long>, vector<…>, less<pair<long,long>>> — 一个 max-heap。push 的 pair(@0x126d3a2c)是 {first = marginal_cost (from $_3), second = op_index}。在 less<pair<long,long>> 下,pop 返回字典序最大的项:最高边际成本优先;相同成本时,更高 op-index 优先。因此列表调度器是最长边际成本优先,并用更高 index 打破平局 — 它尽早调度新增周期最多的操作,使其后继成本更早暴露,从而缩短关键路径。预测试(cmp heap_top_cost, [PerXlu+0x38]; jl skip,@0x126d494b)只有在堆顶边际成本仍能缩短该 XLU 剩余路径时才尝试重排 — 这是函数名所描述的关键路径缩短门控。
示例 — 两个 kVectorAddReduceF32(Pufferfish v4,xlu_count = 2)
- Combine。
ComputeCombinablePairs发出{&R_a, &R_b}— 相同的RpuOperationMetadata{0xf7, src0, src1}(xlu-combine-sourcebus)。 - AssignXlu。
CyclesAddedByXluOperation(prev=null, cur=R_a, …) = 0(empty-XLU base,cur != null但prev == null⇒ 主边需要prev;empty-XLU 的第一个 op 基值为0)。R_b在 XLU 0 上接在R_a后,定价为LatencyBetween(R_b_anchor, R_a_anchor) + (src0 != from ? LatencyBetween(R_b_anchor, src0) : 0) + (src1 != to ? … : 0)。当xlu_count = 2时,主边是ceil(base/2)PreXlu 值 — 串行 reduce 延迟的一半。 - Reorder。
PerXluOperations[0]初始化:pending btree{R_a_idx, R_b_idx},[+0x38] = Σcost,[+0x58] = 0。堆键为{$_3(R_a), R_a_idx}和{$_3(R_b), R_b_idx};最长成本 ready op 先弹出。$_1提交R_a:删除R_a_idx,[+0x58] = max(0+0, finish),[+0x40]/[+0x48] = R_a.src0/src1,[+0x50] = R_a。随后R_b定价为$_3 = CyclesAdded(R_a, R_b, R_a.src0, R_a.src1)— 链式 reduce 边。 - Reemit。 pair
{R_a, R_b},二者都是 RPU,xlu = 0。共享的Vsetpermpattern op 只发射一次(成本中免费的边界值);两个 reduce 折叠为一个融合跨 lane reduce body。发射周期数匹配定价成本:一个主边 + 非共享 source 惩罚。
注意 — segment 与 permute 改变共享 pattern,而不是成本形状。 segment-reduce 对(
0xfc,LloOpcodeIsSegmentedReduction(0xfc) = true)共享Vsetsprprologue(缓存在PerXluState[+0x18]),而不是Vsetperm。成本形状相同 — 一个主边 + per-source 边界未命中惩罚 — 因为CyclesAddedByXluOperation不按 segment vs. permute 分支;差异只在于Reemit共享哪种 pattern op。
尚未钉牢的内容
LatencyBetween随机扰动启用条件(LatencyTable[+0x10]非空 ⇒ 每条边UniformDistribution(0,0x65)):算术已 CONFIRMED,默认值为0(确定性),但切换BitGenbuffer 的 env/flag 尚未隔离。启用条件为 LOW。- public
LatencyBetween内精确的0x84/0x82-band 最小延迟 floor:已存在并应用于 XLU 边,但每个 opcode 的覆盖范围随 latency table 记录,本文不枚举。 AssignXlu在cur != null, prev == null时的 empty-XLU base-case 语义:函数体只有在cur != null时才解析prev,并会在主边位置若prev == null到达"control != nullptr"CHECK;empty-XLU 价格从调用点观测为0,是结构性读取,而不是对所有三种 variant kind 的逐一证明。对所有三种 variant kind 的 empty-XLU base 值为 MEDIUM。
交叉引用
- XLU 操作清单 — 完整 XLU 流水线、op-factory 集合、
PreXluAssignmentLatencyTable的ceil(base/xlu_count)边,以及本页成本喂入的ReemitIR 重写机制。 - XLU Combine / Source-Bus —
ComputeCombinablePairs(消费该成本的 DP)与AssignSourceBus(post-emit 总线打包)。 - XLU 冲突惩罚表 — shared-bus serialization edge 叠加的非 MXU 冒险表。
- Transpose-Reservation 延迟 —
VxposeMode/ElementCount几何,以及门控 transpose-chain(n−1)成本的 slot-fit 谓词。 - CycleTable 家族 — 延迟轴旁边的成本模型吞吐量半边。
- 二进制:
extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d) - 索引条目: Part VII — Cost & Latency Model / XLU cost — 返回索引