Skip to content

MxuSequence / SequenceInfo

地址适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(BuildID md5 89edbbe81c5b328a958fe628a9f2207d,未 strip,包含完整 C++ 符号)。其他版本会有所不同。

摘要

MxuSequence 是 TPU 后端在把 dot / convolution 累加链降低到脉动阵列时,为每个 matmul 组构建的记录。每条链,也就是一次权重 latch、消费它的 matmul 步骤,以及排空 accumulator 的结果 pop,都会成为一个 MxuSequenceMXU 分配阶段会遍历 Span<unique_ptr<MxuSequence>>,把这些链装箱到物理 MXU pass 上。这个记录本身是扁平且很小的:五个 {ptr, count, cap} 指令列表,总计 0x78 字节。有意思的逐指令状态,也就是 latch mode、latch index、MSR bank、MXU-quadrant unit-id,存放在 MxuSequence 上。它们分散在成员 LloInstruction 上,由分配 pass 在遍历列表时写入。

熟悉 LLVM 的读者可以把 MxuSequence 想成一个 MachineInstr bundle,它唯一的 payload 是哪些指令属于同一组,再加上调度器盖到每个成员上的一小组字段。latch-index 分配(SetLatchIndices)类似于对单一架构资源做寄存器分配:序列中的每个 latch 都获得一个按程序顺序单调递增的索引,并且第一个 latch 有一个由每代 overrun handshake 控制的特殊情况。LatchLhs 是生产者,它把 LHS gain matrix 划分到每个 MXU 的 latch grid 上,并发出 vlatch.lsf / vmatmul / vmatres op,每个 op 都标记其 MXU quadrant。第二个独立记录 MxuStat::SequenceInfo 是 bin-packer 的输出:两个 owning vector 加上 latch_latencyaccumulated_latency 快照;一旦 greedy min-makespan 选择确定序列落在哪个物理 MXU 上,就写入每个 MXU 的 btree_map<int, SequenceInfo>sequences_)。

本页是这两个结构的规范位置:字节精确的 MxuSequence 字段映射(来自 deleter 和 builder)、SequenceInfo 布局(来自 btree-insert 字段写入)、SetLatchIndices 中的 latch-index 分配、LatchLhs 中的 gain-matrix 划分,以及 set_mxu commit。填充这些列表的 op family,也就是 latch 0x8d..0x96、matmul 0x9b..0xa5、matres 0x152/0x153,以及它们的 LloInstruction 字段偏移,由 Matprep / IAR / LatchMXU Slot 页面负责;这里仅引用,不重新推导。

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

  • MxuSequence 布局:五个固定偏移的自定义 {ptr, count, cap} vector,sizeof 0x78,以及每个列表保存哪个 opcode family。
  • 逐指令的 "latch state" 模型:分配 pass 盖到每个成员 LloInstruction 上的字段(latch mode、latch index、MSR、unit-id),而不是序列上的扁平标量。
  • SetLatchIndices:单调的 latch-index 分配,以及第一个 latch 的 GainLatchModeHasOverrunChecks gate。
  • LatchLhs:按 transpose 对 LHS 分组、ChunksPerTile × num_mxus ≥ ΣPackingFactor 容量保护,以及在重建的 latch/matmul/matres op 上逐 quadrant 写入 set_unit_id
  • MxuStat::SequenceInfo:bin-packer 的逐序列输出记录(两个 owning vector + latch_latency + accumulated_latency),以及 makespan cost 读取的 latency delta。
MxuSequence 大小0x78(5 个列表 × 0x18)— default_delete<MxuSequence>::operator() 0x14504c00
列表元素{LloInstruction** @+0x00, int64 count @+0x08, int64 cap @+0x10}(count 是整数,不是 end-ptr)
列表偏移+0x00 head,+0x18 latches,+0x30 matprep/MUBR aux,+0x48 matmuls,+0x60 matreses
BuilderCollectAndTransformSequencesInternal 0x14500800(逐列表 grow-realloc append,mxu_sequence_collector.cc
Latch-index 分配MxuAssigner::SetLatchIndices 0x10f3b4c0WORD[op+0x42]
Gain-matrix 划分MxuAssigner::LatchLhs 0x10f3b5e0(const)
SequenceInfoMxuStat::SequenceInfo0x40 字节,btree value sequences_
CommitAssignMxusForSequenceGroupInternal 0x10f77ca0sequences_[seq_key] = SequenceInfo
逐指令状态latch_mode BYTE[+0x40],latch_index WORD[+0x42],MSR BYTE[+0x44],unit-id WORD[+0x0b]

MxuSequence 记录

用途

MxuSequence 把一条 matmul 累加链的 LLO 指令分组,使 MXU-assignment pass 可以把这条链作为一个单元处理:加载权重的 latch、驱动阵列的 matmul 步骤,以及排空结果的 matres op。它是 bin-packer 遍历的容器,也是 MRB/MSR placement 消费的容器。它自身不携带调度标量,只包含五个列表和列表里的指令。

布局(sizeof 0x78

deleter std::default_delete<MxuSequence>::operator()0x14504c00)是主要布局证据:它按反向顺序释放五个 vector backing-store(seq[12]seq[9]seq[6]seq[3]seq[0],也就是 +0x60/+0x48/+0x30/+0x18/+0x00ptr word),在释放前把每个列表的第二个 word(count,位于 ptr+0x08)清零,然后对整条记录执行 free(seq)

c
function default_delete_MxuSequence(seq):     // 0x14504c00, demangled symbol present
    if seq == nullptr: return
    if seq[12]: seq[13] = 0; free(seq[12])     // list @ +0x60 (matreses) : ptr @+0x60, count @+0x68
    if seq[ 9]: seq[10] = 0; free(seq[ 9])     // list @ +0x48 (matmuls)
    if seq[ 6]: seq[ 7] = 0; free(seq[ 6])     // list @ +0x30 (matprep / MUBR aux)
    if seq[ 3]: seq[ 4] = 0; free(seq[ 3])     // list @ +0x18 (latches)
    if seq[ 0]: seq[ 1] = 0; free(seq[ 0])     // list @ +0x00 (head / setup)
    free(seq)                                  // free(seq, 0x78)

每个列表都是自定义的三 word vector,即 {ptr, count, cap}不是 libc++ 的 {begin, end, end_cap} 三元组:count word 是整数元素计数,不是 end-pointer。SetLatchIndices 通过循环 idx < seq[+0x20] 并索引 seq[+0x18][8*idx] 证明了这一点(这是一个作为循环上界使用的 count,而不是从 base pointer 减出来的值)。deleter 的“把每对的第二个 word 置零”模式(seq[1]seq[4]seq[7]seq[10]seq[13])与在释放 backing store 前清除 count 相一致;列表间隔为 0x18ptr word 位于 seq[0]/seq[3]/seq[6]/seq[9]/seq[12]),五个列表合计 0x78。builder(mxu_sequence_collector.cc)用更新 {ptr@+0x00, count@+0x08, cap@+0x10} 的 grow-realloc 向这些列表追加元素,确认第三个 word 是 capacity。

偏移列表元素 opcode / 消费者
+0x00list0 — head / setup推断的序列头 setup
+0x18latches0x8d..0x96SetLatchIndices count @+0x20
+0x30matprep / MUBR aux与每个 matmul 一起追加(builder line ~1190,"Adding vmatprep MUBR")
+0x48matmuls (matmuls)0x9b..0xa5 — builder 追加 matmul op;LatchLhs ΣPackingFactor + seq->matmuls.size() CHECK,count @+0x50
+0x60matreses (matreses)0x152/0x153 — builder 在 kVectorMatres CHECK 后 emplace_back,count @+0x68
0x78sizeoffree(seq, 0x78)

+0x18+0x48+0x60 这些身份是字节精确的:deleter 证明这几个偏移处有五个列表,独立消费者又用 opcode-checked access 索引它们。SetLatchIndices 读取 +0x18/+0x20 并断言 0x8d..0x96。在 CollectAndTransformSequencesInternal 中,matmul-family op((uint16)(op-0x9b) <= 0xa)被追加到 +0x48 列表(成对的 matprep/MUBR 值追加到 +0x30 列表),matres-family op((op & ~1) == 0x152,由 opcode == kVectorMatres gate)则被 emplace_back+0x60 列表。随后 LatchLhs 把同一个 +0x48 列表作为 seq->matmuls 读取:其容量循环遍历 seq[+0x48](count @+0x50)上的 opcode 0x9b/0xa3,源字段名由 CHECK "...== seq->matmuls.size()" 锚定,该 CHECK 读取 *(seq+0x50)+0x00(head/setup)和 +0x30(matprep/MUBR aux)的身份较弱:+0x30 是与 +0x48 同步增长的列表,并被记录为 "vmatprep MUBR"(HIGH);而 +0x00 会被填充,但其 opcode 成员没有逐 cell 隔离出来(MEDIUM)。

陷阱 — 每个序列的 latch state 在成员指令上,而不在序列上。 deleter 只释放五个 vector 和一条扁平的 0x78 记录,MxuSequence 上没有用于 latch mode、latch index、MSR 或 quadrant 的标量字段块。这些字段存在于每个成员 LloInstruction 上:latch_mode BYTE[+0x40],latch_index_in_sequence WORD[+0x42],MSR BYTE[+0x44],unit-id pack WORD[+0x0b]。把它们作为 MxuSequence 成员添加的重新实现,会偏离二进制的数据流:调度器是逐 op 通过列表读写它们的。完整字段图见 Matprep / IAR / Latch

Builder

CollectAndTransformSequencesInternal0x14500800,源码 mxu_sequence_collector.cc)是生产者。它遍历 region 的 LLO value,按 opcode 分类((uint16)(op - 0x9b) <= 0xa → matmul family 追加到最后一个序列的 +0x48 matmuls 列表,并把成对的 matprep/MUBR 值追加到 +0x30(op & 0xfffe) == 0x152 且由 opcode == kVectorMatres gate → emplace_back+0x60 matreses 列表;latch family → +0x18 列表),通过逐列表 grow-realloc 把它们填入 array<Span<unique_ptr<MxuSequence>>, 4> quadrant slot。它用 "Matres before matmul" FailedPrecondition 强制顺序。它检查的平衡不变量 ExpectedMatresesPerMatmuls(last_sequence->matmuls) >= total_matreses_countmxu_sequence_collector.cc,读取 +0x48/+0x50 处的 last_sequence)就是 LatchLhs 稍后依赖的 matmul/matres 数量关系。


SetLatchIndices — Latch-Index 分配

用途

一旦某个序列的 latch op 按程序顺序位于 +0x18 列表中,每个 latch 都需要一个latch index in sequence,也就是它在链中的位置,写入 WORD[op+0x42]。下游的 bundle packer 把冲突的 latch index 作为 slot-legality 约束,因此该索引是把一个 op 绑定到一个 latch slot 的 commit。SetLatchIndices0x10f3b4c0)负责分配它,但有一个特殊情况:第一个 latch 只有在其 GainLatchMode 携带 overrun check 时才会被索引。

入口点

text
MxuAssigner::VisitRegion                     0x10f3a640
  └─ MxuAssigner::SetLatchIndices            0x10f3b4c0  ── per-sequence latch ordering
       ├─ LloInstruction::latch_mode         0x1d4e7500  ── BYTE[op+0x40] (GainLatchMode)
       ├─ Target::GainLatchModeHasOverrunChecks  vtbl+0x358  ── first-latch gate
       └─ LloInstruction::set_latch_index_in_sequence  0x1d4e7960  ── WORD[op+0x42] = idx

算法

c
function SetLatchIndices(span_ptr, span_count):    // 0x10f3b4c0; arg = Span<unique_ptr<MxuSequence>>
    if span_count == 0: return
    for seq in span_ptr[0 .. span_count):           // 8-byte stride, seq = *span_ptr
        if seq[+0x20] == 0: continue                // empty latch list — skip
        for idx = 0 .. seq[+0x20) - 1:              // latch count @ seq+0x20
            op = seq[+0x18][idx]                     // latch list @ seq+0x18, 8-byte stride
            check (uint16)(opcode(op) - 0x8d) < 0xa  // 0x8d..0x96, else FATAL
                                                     //   "LloOpcodeIsVectorLatch(opcode)" mxu_assigner.cc:420
            target = op.region.module.target         // [[op+0x10]+0x38]+0x10
            glm    = latch_mode(op)                   // BYTE[op+0x40]
            has_overrun = target.vtbl[+0x358](glm)    // GainLatchModeHasOverrunChecks
            if idx == 0 and not has_overrun: break    // first latch, no overrun ⇒ abandon this sequence
            set_latch_index_in_sequence(op, idx)      // WORD[op+0x42] = idx (check idx <= 65535)

每个 latch 都获得一个等于其在 +0x18 列表中位置的单调索引。first-latch gate 是唯一分支:如果 idx == 0 该代的 GainLatchModeHasOverrunChecks(glm) 为 false,循环会 break,不索引第一个 latch,并转到下一个序列。在 +0x358 override 恒为 FALSE 的四代上,第一个 latch 永远不会被索引;只有 Viperfish 有实际 handshake。store 本身会限制索引范围:set_latch_index_in_sequence0x1d4e7960)重新检查 opcode family((uint16)(opcode - 0x8d) >= 0xa → FATAL LloOpcodeIsVectorLatch(opcode()) at llo_instruction.cc:3399),断言 index <= (65535)llo_instruction.cc:3400),然后写入 *((uint16*)op + 33) = idx,也就是 WORD[op+0x42]

怪癖 — index 0 可能会被刻意留空。 总是给每个 latch(包括第一个)盖索引的重新实现,会在四个非 Viperfish 代上过度约束 bundle packer;这些代的第一个 latch 会被有意留作未索引,因为它的 mode 不携带 overrun handshake。这个 gate 是逐代数据(vtbl+0x358),不是编译期常量。完整的逐代 GainLatchModeHasOverrunChecks 真值表和 overrun-reservation cost 见 Latch Assignment & Overrun

reader latch_index_in_sequence 是对称 accessor(同一个 WORD[op+0x42],同一个 opcode gate);latch_mode0x1d4e7500)对 latch family 或 kVectorMatprepSubr 返回 BYTE[op+0x40],其他情况 FATAL "Unsupported opcode"


LatchLhs — Gain-Matrix Latch 划分

用途

LatchLhs0x10f3b5e0,一个 const 成员)是 latch / matmul / matres op 的生产者;这些 op 稍后会由 SetLatchIndices 索引,并由 MRB/MSR allocator 盖章。它接收一个 matmul 组的 LHS(stationary gain / weight operand),把其 packed column 划分到每个 MXU 的 latch grid 上,并在新 region 中重建 latch+matmul+matres 序列,同时给每个 op 标记其 MXU quadrant。它是 input/gain 侧,在 VisitRegion 中运行于 result-side AllocateMrb / Bounce 之前。

入口点

text
MxuAssigner::LatchLhs                        0x10f3b5e0  (const)
  ├─ BuildXposeSequences                     0x10f813a0  ── group LHS by transpose op
  ├─ MatmulDataFormatPackingFactor           0x1d629300  ── column-pack factor (table @0xb53c6bc)
  ├─ Target::ChunksPerTile                   0x1d60f2c0  ── hwcfg[+0x198] / hwcfg[+0x1a0]
  ├─ LloRegionBuilder::VlatchLsf             0x1d573ec0  ── emit kVectorLatchLsf
  ├─ LloRegionBuilder::Vmatmul               0x1d575a60  ── emit matmul (K-tile loop)
  ├─ LloRegionBuilder::Vmatres               0x1d5761a0  ── emit matres
  ├─ LloValue::set_unit_id (inlined)         0x12698c00  ── WORD[op+0x0b] quadrant stamp
  └─ ExpectedMatresesPerMatmul               0x145005e0  ── matmul/matres balance

算法

c
function LatchLhs(target, lhs_span, sequences):    // 0x10f3b5e0
    xpose = BuildXposeSequences(lhs_span)           // 0x10f813a0
    //   vec1 = ops with (opcode & 0xfffe) == 0xa6  → {0xa6 kVectorTranspose, 0xa7 kVectorTransposeBinary}
    //   vec2 = ops with opcode == 0x154            → {kVectorTransposeResult}
    //   xpose.size == 1 ⇒ single-xpose fast path; else multi/no-xpose path

    for seq in sequences:
        // --- capacity guard: packed columns must fit the per-MXU latch grid and tile-align ---
        acc = 0
        for op in seq.matmuls[+0x48 .. count@+0x50):     // matmul-family ops
            if op != 0x9b and op != 0xa3: abort           // also requires matmul_data_format(op)-1 <= 1
            acc += MatmulDataFormatPackingFactor(matmul_data_format(op))
        num_mxus = target[+0x4ac]                          // target int[299]
        check( ChunksPerTile() * num_mxus >= acc )    // else abort
        check( acc % ChunksPerTile() == 0 )           // tile.size()*PackingFactor == ChunksPerTile()

        // --- rebuild per quadrant, stamping the MXU-quadrant unit-id ---
        for each matmul in seq.matmuls[+0x48 .. count@+0x50):
            q   = program_order & 3                    // the MXU quadrant 0..3 (cmp 0x4 bound)
            glm = byte_table_0xac0913e[matmul_op - 0x9b]   // {0×8, 0xb, 0xb}: plain→0, packed→0xb
            latch = VlatchLsf(builder, lhs_value, glm, 0)  // emit kVectorLatchLsf (0x8d)
            WORD[latch+0x0b] = (WORD[latch+0x0b] & 0xf8ff) | ((q << 8) + 0x400)   // = set_unit_id(q)
            for k = 0 .. MatmulDataFormatPackingFactor(fmt) - 1:   // K-tile split
                m = Vmatmul(builder, fmt, ...); WORD[m+0x0b] = set_unit_id(q)
            r = Vmatres(builder, fmt, ...);   WORD[r+0x0b] = set_unit_id(q)
        check( ExpectedMatresesPerMatmul-balance )     // matres_index <= matreses.size() - pushes

三个组成部分都是字节精确的:

  • BuildXposeSequences0x10f813a0 扫描 LHS span 并构建两个 vector:vec1 包含 (opcode & 0xfffe) == 0xa6 的 op(transpose-prep pair 0xa6/0xa7),vec2 包含 opcode == 0x154 的 op(0x154 = 340,即 transpose-result)。LatchLhs 测试结果大小是否 == 1,以选择 single-transpose fast path。
  • 容量保护遍历 +0x48 matmuls 列表(count @+0x50),拒绝任何不是 0x9b/0xa3 的 op(或 matmul_data_format - 1 > 1 的 op),对其求和 MatmulDataFormatPackingFactor,并要求 ChunksPerTile() * target[+0x4ac] >= accacc % ChunksPerTile() == 0MatmulDataFormatPackingFactor0x1d629300)读取 int32 table @0xb53c6bc = {1,2,4,4,4,4,8,8,4,4},索引为 fmt - 1(带 bounds-check,FATAL "Unsupported MatmulDataFormat" at matmul_data_format.cc:197)。ChunksPerTile0x1d60f2c0)是 target[119]->[+0x198] / target[119]->[+0x1a0](由 lane-count 派生的 tile granule)。
  • quadrant stamp.rodata byte table @0xac0913e = {0,0,0,0,0,0,0,0,0xb,0xb} 读取 glm(op 0x9b..0xa2 → GainLatchMode 0 bf16;op 0xa3/0xa4 packed → 0xb = GLM_PACKED_BF16),通过 VlatchLsf 发出 latch,并对每个发出的 op(VlatchLsfPackingFactorVmatmulVmatres)写入 WORD[op+0x0b] = (WORD[op+0x0b] & 0xf8ff) | ((q & 3) << 8) + 0x400

该 stamp 与 LloValue::set_unit_id(int)0x12698c00)字节一致:

c
function set_unit_id(v, unit):                 // 0x12698c00
    WORD[v+0x0b] = ((unit & 3) << 8) + (WORD[v+0x0b] & 0xf8ff) + 0x400;   // bits 8-9 quadrant, bit 10 has-mxu
    check unit <= 3                            // "unit_id_ == unit_id" llo_value.h:408
表 / callee地址作用
BuildXposeSequences0x10f813a0对 LHS 分组:vec1 {0xa6,0xa7},vec2 {0x154}
MatmulDataFormatPackingFactor0x1d629300int32[fmt-1] @0xb53c6bc = {1,2,4,4,4,4,8,8,4,4}
Target::ChunksPerTile0x1d60f2c0hwcfg[+0x198] / hwcfg[+0x1a0]
num_mxusTarget+0x4ac每个 region 的 MXU 数量
GLM byte table0xac0913e(op-0x9b)→GLM{0×8, 0xb, 0xb}
LloRegionBuilder::VlatchLsf0x1d573ec0发出 vlatch.lsfLloValue*,GainLatchMode,int)
LloRegionBuilder::Vmatmul0x1d575a60发出 matmul(K-tile loop,每个 latch 有 PackingFactor×)
LloRegionBuilder::Vmatres0x1d5761a0发出 matres
LloValue::set_unit_id(inlined)0x12698c00WORD[v+0x0b] quadrant pack

怪癖 — matmul 循环运行 PackingFactor 次,而不是一次。 Packed/nibble format(PackingFactor 为 2、4 或 8)会为每个 latch 发出多个 Vmatmul op,也就是把 packed contracting dimension 跨 systolic pass 拆分的 K-tiling。对 bf16(fmt 1)以外的所有 format,只为每个 latch 发出一个 matmul 的重新实现都会少算 systolic 步骤。下游的 ExpectedMatresesPerMatmul 平衡检查依赖这个计数。


MxuStat::SequenceInfo — Bin-Packer 输出记录

用途

如果说 MxuSequence 是 assignment pass 遍历的输入,那么 MxuStat::SequenceInfo 就是它产生的输出。bin-packer(AssignMxusForSequenceGroupInternal0x10f77ca0)为每个物理 MXU 维护一个 MxuStat 结构(stride 0x28),其中 sequences_ 成员是一个 absl::btree_map<int, SequenceInfo>。对每个 MxuSequence,当 greedy min-makespan select 选择它落在哪个 MXU 后,pass 会把以 sequence index 为 key 的 SequenceInfo 插入该 MXU 的 sequences_ btree,记录该序列的两个 owning vector,以及一个 latch_latency 和 per-MXU accumulated_latency 快照。

布局(0x40 字节)

从 btree-insert 字段写入点恢复而来(btree_map_container<…map_params_impl<int, MxuStat::SequenceInfo>>::operator[]<int,0> 返回 value slot,随后在 0x10f77ca0 lines ~1064-1079 处写入 8 个 qword 字段)。value object 为 0x40 字节,布局为两个 owning vector 后跟两个 long,每个 vector 的前两个 word 会在 overwrite 时被释放:

c
struct SequenceInfo {              // 0x40 = 64 bytes; btree value
    void* vec1_begin;              // +0x00  owning vector #1 begin (freed on overwrite)
    long  vec1_end;                // +0x08  vector #1 end count
    long  vec1_cap;                // +0x10  vector #1 capacity
    void* vec2_begin;              // +0x18  owning vector #2 begin (freed on overwrite)
    long  vec2_end;                // +0x20  vector #2 end count
    long  vec2_cap;                // +0x28  vector #2 capacity
    long  latch_latency;           // +0x30  per-sequence latch latency (the `free`/new_val arg)
                                   //        CHECK "latch_latency == prev_it->second.latch_latency"
    long  accumulated_latency;     // +0x38  per-MXU accumulated_latency snapshot at select time
};

两个 vector 都是 owning 的(通过 operator new 分配,从 caller buffer 用 memcpy 填充,在 overwrite 时释放)。它们的元素类型是未验证为 int+0x18 vector 的 __throw_length_error 命名为 vector<CycleTable::Instruction>(元素 stride 0x28),而 +0x00 vector 从一个 8 * count 大小的 buffer 复制。因此二者都不是普通的 vector<int>哪个列表保存序列的 instruction set,哪个保存 result/cycle record,是推断出来的,而不是具名源码字段。两个尾随 long 是字节精确的:+0x30 是 cost function LatchLatencyChangeAfterAdding 通过 CHECK "latch_latency == prev_it->second.latch_latency"mxu_latency_balancing.cc:236)与 predecessor 比较的值,+0x38 是一同写入的 per-MXU accumulated_latency 快照(*(stat_array_base))。

未验证 — 两个尾随 long 不是 busy interval。SequenceInfo 建模为 {latch_latency, vec1, vec2, busy_start, busy_end},并把两个 long 视为 scheduled busy interval 很有诱惑力;但写入点不支持这种解释。value object 的两个尾随 longlatch_latency/new-value arg 和 per-MXU accumulated_latency 快照,而不是 start/end pair,并且 +0x00 是 vector begin pointer(overwrite 时释放),不是 latch_latency 标量。cost function 通过 absl btree-node-internal 偏移(9*idx+99*idx+10 qword)读取 predecessor 的 latch_latency 和 interval endpoint,这些是node偏移,不是 value-struct 偏移,并且尚未完全解析到两个尾随 long。请把 cost-function 读取的精确含义视为未验证。

字段偏移类型含义
vec1+0x00 / +0x08 / +0x10owning vector(8-byte elem)每序列列表 #1
vec2+0x18 / +0x20 / +0x28owning vector(CycleTable::Instruction0x28 elem)每序列 cycle/result record
latch_latency+0x30long每序列 latch latency(由 CHECK 锚定)
accumulated_latency+0x38long每 MXU accumulated-latency 快照

set_mxu Commit

AssignMxusForSequenceGroupInternal0x10f77ca0)持有一个 vector<InlinedVector<MxuAssignment, 4>>;每个 MxuStat(stride 0x28,即 5 * idx qword)携带它的 accumulated_latencysequences_ btree。commit 是一次 greedy min-makespan select,随后进行 sequences_ insert:

c
function AssignMxusForSequenceGroupInternal(stats, sequences, cycle_table, ...):   // 0x10f77ca0
    for seq in sequences:                               // stats: vector<InlinedVector<MxuAssignment, 4>>
        best = +INF; argmin = current
        for i = 0 .. num_mxus - 1:                      // MxuStat stride 0x28 (5 qwords)
            score = stats[i].accumulated_latency        // *(stat_base)
                  + stats[i].LatchLatencyChangeAfterAdding(seq_key, new_val, free)  // 0x10f7f3e0
                  + stats[i].free_extra                 // stat[5*i + 0]
            if score < best: best = score; argmin = i    // smaller-index tiebreak (>= keeps prior)
        check( sequences_.find(seq_index) == sequences_.end() )   // mxu_latency_balancing.cc:256, not yet assigned
        // operator[]<int,0> returns the value slot, then the 8-qword write:
        stats[argmin].sequences_[seq_key] = SequenceInfo{ vec1, vec2, latch_latency, accumulated_latency }

per-MXU cost delta 是 MxuStat::LatchLatencyChangeAfterAdding0x10f7f3e0),它在 sequences_(btree)中执行 lower_bound 和 predecessor lookup,并返回 interval-extension delta。其算术是字节精确的,但两个 btree-node-internal 读取位于 node offset 9*idx+9 / 9*idx+10 qword(未直接解析到 value-struct 字段):

c
function LatchLatencyChangeAfterAdding(this, seq_key, new_val, free):   // 0x10f7f3e0
    a = btree_lower_bound_field(seq_key)      // node qword [9*idx+9]
    pred = predecessor(seq_key)
    if pred.key == seq_key:                   // pred.latch_latency read
        check( new_val == pred.latch_latency )  // "latch_latency == prev_it->second.latch_latency"
                                                //   mxu_latency_balancing.cc:236
        new_val = 0
    b = pred_field                            // node qword [9*idx+10]
    c  = max(0, new_val - b)
    x  = max(0, a - free)
    y2 = max(0, a - b)
    return c + x - y2                          // the delta the makespan sums (return v21 + v22 - v23)

makespan select 会在 MXU 间跟踪当前最小值,并在 tie 时保留较小索引的 argmin(0x10f77ca0 lines ~938-946 处的 v78 <= v83 / v78 >= v83 不对称)。cost 只读取 btree interval 字段和 per-MXU accumulated_latency;两个 owning vector 不被 cost path 读取。

注意 — sequences_ 是按 sequence index keyed 的 btree,不是 MxuSequence* map。 这里确认的 per-MxuStat assignment record 是 btree_map<int, SequenceInfo>(CHECK sequences_.find(seq_index) == sequences_.end();value type MxuStat::SequenceInfooperator[]<int,0>map_params_impl<int, MxuStat::SequenceInfo> 实例化中被命名)。btree insert 和 vector<InlinedVector<MxuAssignment, 4>> storage 由该反编译确定。此前断言的并行 flat_hash_map<MxuSequence*, long> set-mxu commit 没有在此函数中找到,现在视为未验证。LatchLhs 读取 program_order & 3 之前,为每个 matmul 确定程序顺序 quadrant 的上游路径尚未追踪到单个 op(未验证)。


Chain 如何流经 Pass

一个 bf16 LHS gain matrix latch 到 2-MXU region 时,会按以下顺序经过这些结构(VisitRegion0x10f3a640):

text
1. CollectAndTransformSequencesInternal  ── builds per-quadrant MxuSequences (the 5 lists)
2. LatchLhs                              ── partition LHS, emit vlatch.lsf/matmul/matres,
                                            stamp WORD[op+0x0b] = unit_id (MXU quadrant)
3. SetLatchIndices                       ── walk seq[+0x18]; WORD[op+0x42] = program-order index
                                            (first latch only if GainLatchModeHasOverrunChecks)
4. AllocateMrbEntriesAsFifo / Bounce     ── result-FIFO address + MSR-A/B bank (output side)
5. AssignMxusForSequenceGroupInternal    ── greedy min-makespan select → sequences_[key]=SequenceInfo
6. bundle packer                         ── reads WORD[op+0x42] latch index as a slot-legality input

这些阶段完全由逐指令字段描述:unit_idWORD[+0x0b],来自 LatchLhs)= gain matrix latch 到哪个 MXU quadrant;GainLatchModeBYTE[+0x40])= 如何加载它(bf16 / packed);latch_index_in_sequenceWORD[+0x42],来自 SetLatchIndices)= 程序顺序;MSR(BYTE[+0x44],来自 Bounce)= 哪个 staging bank;MRB address = 结果落在哪里。这些都不在 MxuSequence 自身上,它纯粹是分组容器;SequenceInfo 也纯粹是逐序列 assignment 输出。


相关组件

名称关系
MxuAssigner::SetLatchIndices 0x10f3b4c0WORD[op+0x42] latch index 写到 +0x18 列表上
MxuAssigner::LatchLhs 0x10f3b5e0latch/matmul/matres op 和 unit_id stamp 的生产者
CollectAndTransformSequencesInternal 0x14500800构建五个 MxuSequence 列表
AssignMxusForSequenceGroupInternal 0x10f77ca0greedy makespan select + SequenceInfo commit
MxuStat::LatchLatencyChangeAfterAdding 0x10f7f3e0makespan 求和的 latency delta
default_delete<MxuSequence>::operator() 0x14504c00布局证据(五个列表,free(seq, 0x78)

交叉引用

  • MXU 分配 Bin-PackerAssignMxusForSequenceGroup,消费这些记录并产生 SequenceInfo 输出的 greedy makespan 算法。
  • Latch 分配与 Overrun — 深入说明 SetLatchIndices 和逐代 GainLatchModeHasOverrunChecks first-latch handshake。
  • MRB Chain Allocator — 消费 matmul/matres 列表的 accumulation-chain reservation timeline。
  • MRB FIFO / MSR PlacementAllocateMrbEntriesAsFifo / BounceBetweenMsrs,在 LatchLhs 后盖上 MSR(BYTE[op+0x44])的输出侧。
  • MXU Slot — 列表保存的 systolic op family(vlatch/vmatmul/vmatres)以及逐代 bundle encoding。
  • Matprep / IAR / Latch — latch-op builder 和完整 LloInstruction 字段图(latch_mode +0x40、latch_index +0x42、MSR +0x44、unit-id +0x0b)。
  • MXU Latency Overview — 为这些序列调度的 matmul/matprep occupancy 定价的逐代 reservation model。
  • 调度概览 — 完整调度 pipeline 中的 Stage 2(MXU sequence assignment)。