Skip to content

感知 Bundle 的成本

本页中的每个偏移、数值和地址都按字节精确地读取自 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(BuildID md5 89edbbe81c5b328a958fe628a9f2207d)。其他版本会不同。所有 .rodata 地址都是虚拟地址;对这个二进制而言,.rodata VMA == 文件偏移(section [11] at 0x84a0000),且 .text VMA == 文件偏移。

摘要

TPU TensorCore 每个周期发射一个 VLIW bundle,把每个已填充的 slot 同时发往不同的功能单元。成本模型也按此给 bundle 定价:它不会把各 slot 的 op 成本相加,而是取它们的最大值,也就是最慢的已占用功能单元 lane 会阻塞发射,其他 lane 都免费重叠。本页记录这个最大值如何计算(ResourceVector::MaxResourceCycles),每个 op 的吞吐周期如何在归约前存入各 lane 累加器(通过 AccumulateInstructionUsage 调用 Acc),当多个 op 或子计算被打包在一起时两个成本向量如何组合(Add),每次迭代成本如何按循环 trip count 缩放并摊销 DMA 启动成本(ScaleResource / GetSubset),以及最终的 bundle 周期数如何进入依赖延迟轴(LatencyBetween)和 LatencyHidingScheduler

这是 slot 模型在 bundle 层级的消费者。23-slot ResourceVectorAcc 写入,以及 MaxResourceCycles 归约的三组结构在 Resource Enum 页面介绍;被写入的各类吞吐整数来自 CycleTable / Per-Opcode Cycle Constants 页面。本页把它们连接起来:展示完整的 op 级 → bundle 级 → loop 级 → schedule 级流水线,在每一步用字节地址锚定,并固定归约所编码的重叠语义。

熟悉 LLVM 的读者可以保留一个类比和一个差异。类比是:MaxResourceCycles 相当于 TPU 版的 LLVM MCSchedModel 资源压力查询,问题是“给定这个 group 消耗的 ProcResource 占用,瓶颈周期数是多少?”差异是:这里没有 scoreboard,也没有乱序窗口,bundle 字本身就是发射包,因此每个 bundle 只在固定的 23-slot 向量上取一次 max;唯一会串行而非重叠的资源是内存传输项和双 lane vector-ALU 端口平衡。其他所有内容,包括连续的 MXU push,都重叠。

对重新实现而言,契约是:

  • Bundle 成本是在各 lane 占用上取 max,不是 sum MaxResourceCycles 用三条规则把 23-slot ResourceVector 归约为一个标量:vector-ALU 三元组上做 50% 端口平衡混合,四个内存传输项做串行求和,其他所有项(MXU、XLU、vector ports、ICI、SparseCore)做普通 max
  • 每个 op 的写入先于归约。 每个 LLO op 把 (double)GetCyclesForThroughput(class) 加入 slot GetResource(class);bundle 的向量是其中各 op 写入的按 slot 求和,之后 MaxResourceCycles 再将其折叠。
  • 跨向量组合会 ADD 每个 slot,除了两个 DMA-latency slot 用 max 组合。 Add(以及 loop-prologue 的 GetSubset)把 DMA 启动视为只付一次,把 DMA 带宽和计算视为按单位付费。
  • 循环缩放按 trip count 乘以每个 slot 成本,但跳过两个 DMA-latency slotScaleResource),因此软件流水化循环只支付一次 DMA 启动,而每次迭代支付带宽/计算。
  • 延迟轴是独立的,也按每个 op-pair 用 max 组合。 LatencyBetween 返回两个 LloValue 之间的读后写停顿,它是在适用的每代 latency-array 字段上取 max,并带硬性下限;调度器组合吞吐成本(本页)和依赖延迟来把 op 放进 bundle。
Bundle 归约ResourceVector::MaxResourceCycles() @ 0x1c89b9e0 — 23-slot → 标量 max
每 op 写入ResourceVector::Acc(Resource, double) @ 0x1c89adc0; 驱动 AccumulateInstructionUsage::operator() @ 0x144fd720
跨向量组合ResourceVector::Add(rv, AddOptions) @ 0x1c89b820; 默认值 AddOptions::Defaults @ 0x1c89ada0
循环缩放ResourceVector::ScaleResource(Resource, double, ScaleOptions) @ 0x1c89b6a0; 默认值 ScaleOptions::Defaults @ 0x1c89ad80
循环子集ResourceVector::GetSubset(SubsetOptions) @ 0x1c89bb00
Bundle 组装CostModel::GetCycles @ 0x130aade0GetHloResourcesImpl + Add
延迟轴LatencyTable::LatencyBetween(LloValue,LloValue) @ 0x1c89f820; JF LatencyBetweenInternal @ 0x1c8a0d60
调度入口CostModelLatencyEstimator::GetLatencyBetween(HloGraphNode,HloGraphNode) @ 0x10ff8f00
混合常量qword_A2DF5C8 = 0.5(vector-ALU 端口平衡因子)

三层成本流水线

一个已调度区域的成本自底向上分三层计算,每层都有自己的组合规则。重新实现者必须区分它们:在二进制使用 max 的地方使用 sum,或者缩放二进制保持不变的 slot,都会给整个 op 类定错价。

text
op level     ── per LLO op: Acc(GetResource(class), GetCyclesForThroughput(class))
   │              into one shared 23-slot ResourceVector  (slot-wise SUM across ops)

bundle level ── MaxResourceCycles(rv)  →  ONE scalar = slowest occupied lane
   │              (plain MAX, except VectorAlu blend + MemXfer serial sum)

loop level   ── Add(rv_iter)            combine two regions  (per-slot SUM, DMA-latency MAX)
                ScaleResource(rv, trip)  scale per-iteration  (per-slot ×trip, DMA-latency skipped)
                GetSubset(rv, opts)      prologue/tail split  (keep input-DMA / keep compute+output)


schedule     ── LatencyBetween(a,b)     RAW dependency stall (per-op-pair MAX over latency fields)
                + bundle cycle cost   →  LatencyHidingScheduler list priority
```text

> **NOTE —** op 级写入和 bundle 级归约使用*同一个* `ResourceVector` 对象:op 累加到 slot 中(`+=`),然后整个向量只归约一次。向量绝不会按 op 单独归约。因此,不同单元上的两个 op 成本是两者周期的 max(一个 bundle),而同一单元上的两个 op 成本是它们的和(该 slot 累加了两者)。slot 布局见 [Resource Enum](resource-enum.md)。

---

## 每 op 写入 — 通过 `AccumulateInstructionUsage` 调用 `Acc`

在任何归约之前,每个 LLO op 的吞吐周期都会落入恰好一个 slot。规范驱动是 SDC sequence-checker 用来重新推导 bundle usage 的 lambda;它是最清晰的、gen-invariant 写入路径反编译,字节精确:

```c
// AccumulateInstructionUsage(...)::$_0::operator()  @ 0x144fd720 (decompiled, exact)
int operator()(CycleTable *ct, ResourceVector *rv, CycleTable::Instruction cls) {
    Resource r = CycleTable::GetResource(ct, cls);          // op→slot  (flat LUT 0xb438aec)
    int64 cyc  = ct->vtable[+0x10](ct, cls);                // GetCyclesForThroughput(cls)
    ResourceVector::Acc(rv, r, (double)cyc);                // rv[r*8] += cyc
    return 1;
}

这里固定了三点。(1) op→slot 映射 GetResource 是单个平坦 .rodata 查表,在各代之间相同(CycleTable Family)。(2) 吞吐周期是 vtable slot +0x10 上的每代 virtual(GetCyclesForThroughput)。(3) 写入是 Acc,即对命名 slot 做 +=,不是覆盖,因此同一 lane 上的多个 op 会累加。Acc0x1c89adc0)会边界检查 resource < 0x17(23 slots),失败时用 trapping ud1,并按 8-byte double stride 执行 rv[resource*8] += cycles

对非 MXU op,emitter 的 Record* 路径会直接写入:cost_model_util::RecordInputMemXferCycles / RecordOutputMemXferCycles 写入四个 MemXfer* slot(R[9..12]),RecordHloCycles0x130bbfe0)为 HLO 级定价包装 Acc(GetResource, GetCyclesForThroughput) 路径,matmul/matprep 家族则经过每代 MxuLatencyTable。它们全部写入同一个稍后由 MaxResourceCycles 归约的 ResourceVector


Bundle 归约 — MaxResourceCycles

MaxResourceCycles @ 0x1c89b9e0 是感知 bundle 成本的核心。它接收填好的 23-slot ResourceVector,返回一个 double:bundle 的发射成本(周期)。反编译是一个平坦汇编块;缩减后是三条规则:

c
// xla::jellyfish::ResourceVector::MaxResourceCycles()  @ 0x1c89b9e0 (decompiled, exact shape)
double MaxResourceCycles(ResourceVector *rv) {
    double a = rv[3], b = rv[4], c = rv[5];        // VectorAlu0 / VectorAlu1 / VectorAluAny
    if (c > 0.0) {                                  // (A) 2-lane vector-ALU port balance
        double d = min(a - b, c);                   //   move "any" work to the less-busy lane
        c -= d; b += d;                             //   (vsubsd/vminsd/vsubsd/vaddsd, [+0x18..+0x28])
        c *= 0.5;                                    //   residual overlaps at 50% (qword_A2DF5C8)
        a += c; b += c;
    }
    double acc = max(a, b);                          //   vec_alu bottleneck
    double mem = rv[9] + rv[10] + rv[11] + rv[12];   // (B) MemXfer serial sum [+0x48..+0x60]
    acc = max(acc, mem);
    // (C) plain MAX over every other slot, in physical order:
    acc = max(acc, rv[0]);   // R[0]  Matpush      [+0x00]
    acc = max(acc, rv[1]);   // R[1]  Matmul       [+0x08]
    acc = max(acc, rv[2]);   // R[2]  Xlu          [+0x10]
    acc = max(acc, rv[6]);   // R[6]  VectorEup    [+0x30]
    acc = max(acc, rv[7]);   // R[7]  VectorLoad   [+0x38]
    acc = max(acc, rv[8]);   // R[8]  VectorStore  [+0x40]
    for (r in 13..22)        // R[13..18] ICI, R[19..21] SparseCore, R[22] reserved
        acc = max(acc, rv[r]);                       // [+0x68..+0xb0]
    return acc;
}
```text

反编译会按这个物理顺序读取 slot:`[+0x18]`/`[+0x20]`/`[+0x28]` 是混合三元组,`[+0x48]`+`[+0x50]`+`[+0x58]`+`[+0x60]` 是内存求和,然后是一串对 `[+0x00]`、`[+0x08]`、`[+0x10]`、`[+0x30]`、`[+0x38]`、`[+0x40]` 以及 `[+0x68]` 到 `[+0xb0]` 的 `vmaxsd`。`0.5` 是字面量 `qword_A2DF5C8`。

这三条规则编码了三种硬件重叠模型:

|| Slots | 规则 | 硬件含义 |
|---|---|---|---|
| (A) vector-ALU | `R[3]`,`R[4]`,`R[5]` | 端口平衡 + 50% residual | 两条专用 ALU lane + 一条会负载均衡的 "any" lane;无法平衡的 residual 以一半重叠 |
| (B) memory | `R[9]`,`R[10]`,`R[11]`,`R[12]` | 串行 **sum** | input-DMA startup + input bytes + output-DMA startup + output bytes 顺序发生 |
| (C) everything else | `R[0..2]`,`R[6..8]`,`R[13..22]` | 普通 **max** | 独立功能单元(MXU pipes、XLU、vector ports、ICI links、SparseCore)完全重叠 |

> **GOTCHA — MXU pipes 位于普通 `max` 组。** `R[0]`(Matpush)和 `R[1]`(Matmul)会重叠,因此同一个 bundle 中连续的 MXU gain-push 和 matmul issue 成本是其周期的 max,而不是和。把 MXU op 串行化的成本模型会显著高估每个 matmul-bound bundle;在 `6acc60406` 上,单个 bf16 matmul/latch 成本为 212 cycles([Per-Opcode Cycle Constants](per-opcode-cycle-constants.md)),相加得到 424,而硬件(以及此模型)收取 212。相信 max。
>
> **GOTCHA — 只有内存传输会串行化。** 整个归约中唯一的 `sum` 是四个 `MemXfer*` 项。其他所有内容都是 `max`/blend。任何把计算 lane 串行化的重新实现都会立即偏离二进制。

### 示例

一个 bundle 包含以下项(所有周期整数都取 [Per-Opcode Cycle Constants](per-opcode-cycle-constants.md) 的 `6acc60406` 列):一个 bf16 matmul/latch issue(class `0x05`,向 `R[1]` 写入 212),一个 primary matrix-result read(class `0x1b`,向 `R[2]` 写入 127),以及一个 input DMA(向 `R[9]` 写入 30 startup + 向 `R[10]` 写入 64 bandwidth;DMA 项只是说明性示例,并非字节锚定)。

```text
R[1]=212  R[2]=127  R[9]=30  R[10]=64                  (other slots 0)
mem  = R[9]+R[10]+R[11]+R[12] = 30 + 64 + 0 + 0 = 94    (group B serial sum)
acc  = max( vec_alu=0, mem=94 ) = 94
acc  = max( 94, R[0]=0, R[1]=212, R[2]=127, ... ) = 212  (group C plain max)
bundle cost = 212 cycles

matmul lane 是瓶颈;matrix-result read 和 DMA 免费重叠在它下面。这就是该模型的全部要点。


Bundle 组装 — CostModel::GetCycles

CostModel::GetCycles @ 0x130aade0 是单条 HLO 指令的 per-op resources 被收集并组合进调用者将要归约的累加器的位置。它是调度器按节点调用的 bundle 级入口。

c
// xla::jellyfish::CostModel::GetCycles(hlo, opts, *out_rv)  @ 0x130aade0 (decompiled, paraphrased)
ResourceVector tmp = {};                                 // 184-byte zeroed RV (v31)
Status s = CostModel::GetHloResourcesImpl(&tmp, hlo, opts, ...);  // fill slots via Acc/Record*
if (!s.ok()) return s;                                   // AddSourceLocationImpl(cost_model.cc:2025)
double scalar = tmp.scalar_cycles;                       // var_220 → var_430, the cross-bundle scalar
AddOptions ao = ResourceVector::AddOptions::Defaults();
ResourceVector::Add(out_rv, &tmp, &ao);                  // combine tmp into the running accumulator
out_rv->scalar = scalar;                                 // [rbx+8]
```text

`GetHloResourcesImpl` 会逐 slot 填充 `tmp`(上面的 `Acc`/`Record*` 写入家族)。随后 `GetCycles` 用默认 `AddOptions` 调用 `Add`,把 `tmp` 合入调用者的累加器。`[rbx+8]` 处的标量携带非向量化周期项(例如固定 transcendental 估计)并与向量并行存在。调用者之后对累加后的向量调用 `MaxResourceCycles` 并加上标量;显式的 `MaxResourceCycles + scalar` 序列见下面的 loop emitter。

---

## 跨向量组合 — `Add`

当两个区域被打包在一起(融合计算中的两个 op,两个 sub-bundle)时,它们的成本向量由 `Add` @ `0x1c89b820` 组合。规则是**几乎所有 slot 按 slot 求和,但两个 DMA-latency slot 取 `max`**

```c
// xla::jellyfish::ResourceVector::Add(this, other, AddOptions)  @ 0x1c89b820 (decompiled, paraphrased)
//   most slots:     this[i] += other[i]
//   slots 9 and 11: if (AddOptions.flag == 1)  this[i] = max(this[i], other[i])   else  +=
for (int i = 0; i < 23; ++i) {
    bool is_dma_latency = (i & 0x1D) == 9;               // selects exactly i==9 and i==11
    if (is_dma_latency && AddOptions.flag == 1)
        this[i] = max(this[i], other[i]);                // DMA startup paid once, not twice
    else
        this[i] += other[i];                             // bandwidth + compute accumulate
}
AddToOperandBytesAccessed(this, other);                  // merge per-operand byte maps (+0xb8)
AddToOutputBytesAccessed(this, other);                   // merge per-output  byte maps (+0xd8)

掩码 (i & 0x1D) == 9 是二进制用于选择索引 9 和 11 的紧凑写法:9 & 0x1D == 911 & 0x1D == 9,而 [0,22] 中其他索引都会映射到别处。这两个索引是 MemXferInputLatencyR[9])和 MemXferOutputLatencyR[11]),即 DMA startup 项。反编译中的前导 SIMD 块(在 [+0x40]/[+0x50] 上的 vcmpnltpd + vmaskmovpd)正是对 R[8..11] 窗口执行同一个“latency slot 取 max、bandwidth slot 相加”决策的向量化形式;标量尾循环处理其余部分。

NOTE — 摊销 DMA 模型。 组合两个 sub-vector 不应重复计算 DMA 的固定启动延迟:如果两个子区域从同一个输入流读取,引擎只支付一次 startup,但支付两次 bandwidth。Add 正是这样编码的:两个 latency 项取 max,两个 bandwidth 项(R[10]R[12])以及所有计算项求 sumAddOptions.flag(其 DefaultsAddOptions::Defaults @ 0x1c89ada0)切换这一行为;关闭该 flag 时,latency 项也会 sum


循环缩放 — ScaleResourceGetSubset

软件流水化循环体会定价一次并乘以 trip count,但 DMA startup 不应缩放。ScaleResource @ 0x1c89b6a0 用一个标量乘以一个 slot,跳过两个 DMA-latency slot

c
// xla::jellyfish::ResourceVector::ScaleResource(Resource r, double factor, ScaleOptions)  @ 0x1c89b6a0
if ((r & 0xFFFFFFFD) == 9 && !ScaleOptions.flag)         // r==9 or r==11  (DMA latency)
    return;                                              //   startup is paid once → do not scale
if (r >= 0x17) trap();                                   // bound: 23 slots
this[r] *= factor;                                       // bandwidth/compute scale linearly
// r==10 / r==12: also scale the per-operand / per-output byte-map entries (flat_hash_map walk)
```text

`(r & 0xFFFFFFFD) == 9` 同样是“这是否为 DMA-latency slot”的测试(清除 bit 1,因此 `9` 和 `11` 都匹配 `9`)。当缩放 *bandwidth* slot(`r==10`、`r==12`)时,函数还会遍历 per-operand / per-output bytes-accessed 的 `flat_hash_map` 并缩放每个条目,因为字节量随 trip count 缩放。

`GetSubset` @ `0x1c89bb00` 把每迭代向量划分为 prologue / steady-state / tail 子集,由四个 boolean `SubsetOptions` flag(`a3[0..3]`)控制。它先把目标清零,然后按相同的 `(idx & 0x1D) != 9` 在某些 flag 组合中排除 DMA-latency 项,并在条件满足时对每个 source slot 做 `+=` 累加:

```c
// xla::jellyfish::ResourceVector::GetSubset(SubsetOptions)  @ 0x1c89bb00 (decompiled, paraphrased)
//   dest := 0
//   for each slot i in 0..22, gated by the four flag bits:
//       if slot selected:  dest[i] += src[i]
//   the per-flag predicates exclude the DMA-latency slots (i==9, i==11) via (i & 0x1D) != 9
//   flag[1] also merges operand-bytes (+0xb8);  flag[3] also merges output-bytes (+0xd8)

结合 ScaleResource 后,净效果是循环总成本为 prologue (input-DMA startup, paid once) + trip_count × (compute + DMA bandwidth) + tail (output-DMA, paid once)MaxResourceCycles 把每个组合后的向量归约为标量 bundle 成本。


上下文中的归约 — 一个真实调用者

PartialReduceEmitter::EstimateCycles @ 0x10eac440 展示了端到端的完整模式,是 MaxResourceCycles 作为终端 bundle-cost 步骤的最清晰字节锚定证明:

c
// xla::jellyfish::PartialReduceEmitter::EstimateCycles(op_windows)  @ 0x10eac440 (decompiled, paraphrased)
ResourceVector rv = {};                                  // local 23-slot vector (v45..)
int64 compute = 0;
for (each input window) {
    compute += per_window_cycles * Product(window_shape); // scalar compute term (v62)
}
CycleTable *ct = CycleTable::Create(target);
for (each operand) {                                      // deposit memory cost into rv slots
    RecordInputMemXferCycles(rv, param, ...);             // → R[9], R[10]
    RecordOutputMemXferCycles(rv, param, ...);            // → R[11], R[12]
}
double bundle = ResourceVector::MaxResourceCycles(&rv);   // reduce 23 slots → scalar
int64  cyc    = (int64)bundle;                            // vcvttsd2si
result->cycles = cyc + compute;                           // bundle bottleneck + compute term
```text

这些写入填充 memory slot;`MaxResourceCycles` 将向量折叠为瓶颈 lane;结果是 `(int)max-lane + compute`。整数转换是 `MaxResourceCycles` 调用后紧跟的字面 `vcvttsd2si rax, xmm0`,加法(`rax + v62`)写入 emitter 的 cycle 字段。同样的 `MaxResourceCycles → (int) → + scalar` 形态也出现在 `BaseCostModelMetricCalculator::Calculate`(`0x1304ee00`)、`CostModel::GetLoopFusionOrUnfusedHloCycles`(`0x130b2bc0`)、`CostModel::GetCyclesIfFused`(`0x130aba40`)、`CostModel::GetOutputFusionOrConvolutionCycles`(`0x130aede0`)和 `ParamInput::EstimateComputeCycles`(`0x1126ee20`)中。唯一不是成本 emitter 的调用者是 SDC sequence-checker 的 `MaybeInjectMxuSequences::$_3`(`0x144fc5c0`),它调用 `MaxResourceCycles` 在验证注入的 MXU sequences 时重新推导候选 bundle 的 issue cost;它与上面的 `AccumulateInstructionUsage` 写入 lambda 位于同一个 translation unit。这七个函数是二进制中 `MaxResourceCycles` 的完整调用者集合。

---

## 依赖延迟轴 — `LatencyBetween`

Bundle 吞吐成本(上文)是调度器的两个输入之一。另一个是两个 op 之间的读后写依赖延迟,即调度器在 consumer 读取 producer 结果前必须插入的最小 cycle gap。这是一个*不同*的表家族([CycleTable Family](cycletable-family.md) 记录了这个拆分),且按每个 op-*pair* 组合,而不是按每个 bundle。

`LatencyTable::LatencyBetween(LloValue, LloValue)` @ `0x1c89f820` 是公开 dispatcher。它调用每代的 `LatencyBetweenInternal` virtual,然后应用两个跨代一致的修正:

```c
// xla::jellyfish::LatencyTable::LatencyBetween(a, b)  @ 0x1c89f820 (decompiled, paraphrased)
int lat = this->vtable[+0x18](this);                     // base: per-gen LatencyBetweenInternal
if (this->jitter_bitgen)                                  // optional: +Uniform[0,101) jitter (+0x10)
    lat += UniformInt(0, 101);
uint16 oa = a->opcode, ob = b->opcode;
if (oa == 132 && ob == 132)                               // matmul→matmul: floor from AutoOr<int>
    lat = max(lat, autoOr.value_or(16));
else if (oa == 130 && (ob - 130) <= 2 && lat < 3)         // matprep→matres family: floor 2
    lat = 2;
return lat;                                               // VLOG(2): "resolved latency from <a> to <b>: N"

每代的 LatencyBetweenInternal 本身是在每代 latency array 的相关字段上,按每个 op-pair 取 max。Jellyfish 形式 LatencyTableJellyfish::LatencyBetweenInternal @ 0x1c8a0d60 按字节精确读取为:从 v15 = 1 开始,然后对每个匹配的 producer/consumer opcode 关系,把 v15 提升到对应 latency-array 字段(store-then-load 链用 v5[6],pseudo-EUP 用 v5[7],RPU/result-FIFO 类用 v5[8]/v5[11]/v5[19]/v5[20],按 latch-mode 高位键控的 matprep/matmul/transcendental 类用 v5[9..18]),并有硬性下限 4(indexed-store-followed-by-load 和 set-IAR-followed-by-indexed-load 会提升到 5)。整个组合运算都是 if (v15 <= field) v15 = field;,也就是 max。因此两个 op 之间的依赖延迟是所有适用 RAW-hazard 字段的最大值,精确镜像 bundle-cost 的 max

NOTE — 两个 max,两个轴。 MaxResourceCycles一个 bundle 内的功能单元 lane 上取 max(吞吐/占用)。LatencyBetween一个 op pair 的 hazard 字段上取 max(依赖/停顿)。两者都是 max,两者都来自同一个每代 Performance 对象(Performance Family Overview),但它们回答的是不同问题,调度器会同时消费两者。


送入调度器

CostModelLatencyEstimator::GetLatencyBetween(HloGraphNode, HloGraphNode) @ 0x10ff8f00LatencyHidingScheduler 查询的 HLO 图级 estimator。它在 flat_hash_map<HloInstruction*, double>(estimator +0x18/+248)中 memoize 结果;对带计算的 opcode pair,它通过 CostModel::GetCycles(上面的 bundle 组装路径)再经过 per-bundle MaxResourceCycles 归约。对 async / collective 边,它改用 collective overlap 模型,包括 PCIe 和 DCN 带宽项(available_pcie_bandwidthkDcnEstimatedBandwidthkPcieMinimumTimekDcnMinimumTime,都在首次调用时通过 __cxa_guard 懒计算一次)以及 SparseCore custom-call 估计(GetSparseCoreCustomCallCostEstimateLatencyGetRadixSortEstimatedExecutionCycles、gather/scatter offload estimators)。它返回的 cycle count 会通过 Target::TensorCoreFrequencyInMegaHertz 转为 wall-clock seconds(kCyclesPerMicrosecond guard variable,等于 TC clock in MHz;见 Cost Model Overview)。

随后调度器使用这两个量:每节点 bundle 成本(本页的 MaxResourceCycles)和每条边依赖延迟(LatencyBetween),来计算 list-scheduling priority。当依赖延迟允许时,它会把 async work 重叠在 compute 下面,并把独立 op 打包到 bundle 中;bundle 成本是 max-lane occupancy,而不是 sum。调度器内部(list priority、async tracking、独立的 47-ID ResourceType concurrency model)见 Scheduler OverviewResourceType Taxonomy 页面。

GOTCHA — Bundle 成本是 per-lane max,绝不是全局 sum。整个流水线中唯一的加法是归约内部四项 memory group 以及 packed vectors 之间的 per-slot Add(它本身又对两个 DMA-startup 项做 max 组合)。把 bundle 定价为 slot 成本之和会给每个 multi-lane bundle 定错价。


交叉引用

  • Resource Enum — 23-slot ResourceVectorAcc 写入,以及本页消费的 MaxResourceCycles 三组归约介绍。
  • CycleTable Family — 向每 op 写入提供输入的 GetResource(op→slot)和 GetCyclesForThroughput(per-class throughput);吞吐与延迟的拆分。
  • Per-Opcode Cycle Constants — 归约前写入 slot 的每代 cycle 整数。
  • Cost Model Overview — 三个类家族、Target clock-frequency wiring,以及每个 cycle number 背后的 factory dispatch。
  • Performance Family Overview — 吞吐轴和延迟轴共同读取的每代 Performance grid。
  • MXU Latency Overview — 为 matmul/matprep 写入提供的 per-(MatmulModifier × Resource) reservation matrices。
  • ResourceType Taxonomy — 调度器独立的 47-ID concurrency-cap enum(不是成本模型的 Resource)。
  • Scheduler Overview — 消费 bundle cost 和 dependency latency 的 LatencyHidingScheduler
  • Bundle Model Overview — 本页定价其 issue cost 的 VLIW bundle 字。
  • Learned Cost-Model Client — 为什么这里的每个数字都是静态 .rodata 常量,而不是 ML prediction。
  • Binary: extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d
  • Index entry: Part VII — Cost & Latency Model / Core model — 返回索引