Skip to content

XLU Reemit 成本

本页的每个 opcode、地址、偏移和字段都逐字节读取自 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(BuildID md5 89edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,未 strip — 完整 C++ 符号,nm -C 可解析每个方法)。.text.rodata VMA 等于其文件偏移;.data.rel.ro VMA − 0x200000 = 文件偏移。其他 libtpu 构建会不同。

摘要

XLU 优化器(LloXluGraphOptimizer::Optimizexlu-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-op LogFatal
  • 成本读取的 variant 操作数布局:RpuOperation 将两个源 LloValue* 内联存储在 [+0x00]/[+0x08]TransposeTile 存储其 read-set(ptr [+0x00],size [+0x08])。
  • PerXluOperations 0x60 字段图,以及每个已调度操作更新它的三次写入:[+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-emitterReemitReorderedCombinedXluOperations @ 0x126d5460(大小 0x1c75)→ absl::Status
调度器ReorderToShortenCriticalPath @ 0x126d3460PerXluOperations 步幅 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” — 它们都是同一个表上的同一个数。

签名

c
// 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 惩罚递增。

c
// 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 backward

operands(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 应用两个调整:

c
// 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/toCyclesAddedByXluOperation 定价时使用的同一边界对相同:等于边界值的操作数在成本中是免费的,并会在边界修复时重新连接(而不是重新发射)。成本模型与 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/SetSegmentPattern op 就是该边界值:每个 XLU 发射一次,缓存在 PerXluState 中并复用。成本模型与 emitter 从两侧编码同一种收益 — 成本不支付共享 pattern,Reemit 只发射一次。共享一个 pattern 的 N 个 reduce 只支付一次 setup;成本通过只定价每操作主边和共享 source 来反映这一点。

在每对发射和边界修复之后,Reemit 删除每个被取代的原始 op(按 variant kind 对 reordered 调用 RemoveNode),并返回 absl::Status。发射和 re-home 机制(factory、ReplaceUsesOfInstructionPopInstruction/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 访问的逐字节信息。

OffsetType字段写入者 / 读取者
+0x00 .. +0x38absl::btree_set<long, less, alloc, 256>每 XLU pending op-index setctor 播种两个 EmptyNode ptr(@0x2181d930);Phase A 插入 indices;$_1 删除已调度 index(@0x126d8ccd
+0x38i64剩余运行周期累加器(Σ 尚未调度 op 的成本)Phase A += cost[i]@0x126d3942[+56] += v28);$_1 −= cycles[idx]@0x126d8fdcv20[7] -= …
+0x40LloValue*anchor "from" = 上一个已调度 op 的源操作数 0$_1v65[8] = src0@0x126d8f41);下一个 $_3/CyclesAdded 作为 from 读取
+0x48LloValue*anchor "to" = 上一个已调度 op 的源操作数 1$_1v20[9] = src1@0x126d8f73);下一个成本作为 to 读取
+0x50variant*此 XLU 上的上一个已调度 op(下一次的 prev$_1v20[10] = *op@0x126d8fe3);Phase A 和 $_3 读取(@0x126d92c2
+0x58i64每 XLU 完成时间时钟(关键路径前沿)$_1 [+0x58] = max([+0x58] + cost, finish[idx])@0x126d8fadv20[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

成本如何流经记录

c
// 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)

  1. Combine。 ComputeCombinablePairs 发出 {&R_a, &R_b} — 相同的 RpuOperationMetadata{0xf7, src0, src1}xlu-combine-sourcebus)。
  2. AssignXlu。 CyclesAddedByXluOperation(prev=null, cur=R_a, …) = 0(empty-XLU base,cur != nullprev == 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 延迟的一半。
  3. 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 边。
  4. Reemit。 pair {R_a, R_b},二者都是 RPU,xlu = 0。共享的 Vsetperm pattern op 只发射一次(成本中免费的边界值);两个 reduce 折叠为一个融合跨 lane reduce body。发射周期数匹配定价成本:一个主边 + 非共享 source 惩罚。

注意 — segment 与 permute 改变共享 pattern,而不是成本形状。 segment-reduce 对(0xfcLloOpcodeIsSegmentedReduction(0xfc) = true)共享 Vsetspr prologue(缓存在 PerXluState[+0x18]),而不是 Vsetperm。成本形状相同 — 一个主边 + per-source 边界未命中惩罚 — 因为 CyclesAddedByXluOperation 不按 segment vs. permute 分支;差异只在于 Reemit 共享哪种 pattern op。


尚未钉牢的内容

  • LatencyBetween 随机扰动启用条件(LatencyTable[+0x10] 非空 ⇒ 每条边 UniformDistribution(0,0x65)):算术已 CONFIRMED,默认值为 0(确定性),但切换 BitGen buffer 的 env/flag 尚未隔离。启用条件为 LOW
  • public LatencyBetween 内精确的 0x84/0x82-band 最小延迟 floor:已存在并应用于 XLU 边,但每个 opcode 的覆盖范围随 latency table 记录,本文不枚举。
  • AssignXlucur != 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 集合、PreXluAssignmentLatencyTableceil(base/xlu_count) 边,以及本页成本喂入的 Reemit IR 重写机制。
  • XLU Combine / Source-BusComputeCombinablePairs(消费该成本的 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-id 89edbbe81c5b328a958fe628a9f2207d
  • 索引条目: Part VII — Cost & Latency Model / XLU cost — 返回索引