Skip to content

SC 核心选择

本页中的每个地址、偏移、字符串和常量都从 libtpu-0.0.40-cp314 wheel 中的 libtpu.so 逐字节读取(BuildID md5 89edbbe81c5b328a958fe628a9f2207d,build libtpu_lts_20260413_b_RC00)。.text VMA == file offset(0xE63C000);.rodata VMA == file offset(0x84A0000)。其他版本会不同。

摘要

本页描述 SparseCore 后端在把 embedding/collective async op 分配给一组具体物理 SparseCore 核心时运行的物理核心选择策略。它由 xla::jellyfish::SparseCoreQueueAssignment 的两个成员函数拥有:GetAllowedCores0x10FDA3C0)为一个 collective 计算候选核心 maskSelectCores0x10FDC4E0)把该 mask 转换成一个有序核心列表。产物是 absl::Span<long const>,调用方会对其进行数值排序,并通过 AddCollectivePhysicalCoreIndices0x1C868500)写入 op 的 physical_core_indices backend-config 字段。

重新实现者需要的心智模型是,SelectCores 不是数值 scorer —— 在其 0x28C1 字节主体(nm -S)的任何位置,都没有闭式 score(core),也没有 core→queue 双射算术。它是一个确定性的贪婪优先级过滤器:它把允许的 btree-set 物化为 flat vector,将该 vector 一次性排序为逐核心 cost 升序(这是 double cost 参数唯一被消费的位置),然后对 cost 排序后的候选运行五个有序 pass。每个 pass 在候选第一次满足该 pass predicate 时追加它,而 predicate 会针对已经分配的 async collective 的运行中 vector<Info> 求值。因此候选的最终 rank 是 (pass index 1..5) 为主、(ascending cost) 为次。五个 predicate 依次为:(P1)核心已经在同一个 ND plane 上运行 collective;(P2)持有该核心的已分配 op 与当前 op 存在数据依赖(通过 HloReachabilityMap 探测);(P3)该 op 是预定 assignment group 的成员,且该 group 其他成员持有该核心;(P4)核心没有不同 ND plane 上运行 collective;(P5)fallback —— 追加所有剩余的允许核心。P5 保证所选集合覆盖整个 allowed set;P1–P4 只确定顺序,调用方按 device/megachip 数量截断后,该顺序决定成员。

GetAllowedCores 从两个输入构建候选 mask,并通过两个 Swiss table 求交:megachip 逐轴 chip IDGetChipIDsFromParallelismConfigGetMegaChipParallelism)和逐 collective scheduling resource-type ID {0, 23, 24, 25, 26, 27, 28}GetSparseCoreResources:对 offload-config collective-type enum 的 7 分支 switch,其中六个分支插入固定常量,enum-4 分支则返回 unwrapped async op root opcode 上的 AsyncTracker::GetResourceTypeForOp)。逐 resource 的线程本地 reservation budget(通过 __tls_get_addr 读取,逐次分配递减,由 >= 2 gate)会排除该 resource 的 reservation 已耗尽的核心 —— 这是 embedding-device / reserved-core 效应。输出是允许的 SC core ID 的 btree_set<long>

本页分为四个单元:端到端流水线(选择位于候选 mask 和 proto 写入之间的位置)、GetAllowedCores(候选 mask 构建和 reservation budget)、SelectCores(五阶段过滤器、Info struct、cost tie-break),以及 tensor_split_factor 交互(所选核心的数量如何被下游 collective ring strategy 消费)。约束逐 chip 核心数量的 4:1 SC:TC 比例见 SparseCore Hardware Architecture

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

  • 选择是 physical_core_indices 的两阶段 producer,而不是单个 scorer。 GetAllowedCores(mask)→ SelectCores(order)→ 调用方数值 __sortAddCollectivePhysicalCoreIndicesSelectCores 返回其 vector 时未排序;数值排序发生在调用方。
  • SelectCores 是五阶段贪婪优先级过滤器。 Pass 顺序为 {same-plane, data-dep, assign-group, not-different-plane, fallback};在一个 pass 内,cost 升序 stable_sort 是 tie-break。成员由已经分配的 collective 状态决定,而不是由任何逐候选数字决定。
  • 唯一的 "cost" 是 tie-break,不是 placement metric。 double cost 参数供给一个逐核心权重,stable_sort comparator($_0vucomisd)读取它来给同优先级候选排序。较低 cost 先追加。
  • GetAllowedCores 是 resource-reservation 交集。 允许核心 = 由逐 collective resource-type ID {0,23..28} 分组的 megachip chip ID,减去逐 resource 线程本地 reservation budget 耗尽的核心。
  • tensor_split_factor 是 consumer,不是选择输入。 它位于 collective offload-config 中,控制 ring strategy 如何分区所选核心;该 strategy 强制 color_strategies_size() % tensor_split_factor == 0。它不会在 SelectCoresGetAllowedCores 内部分支。
候选 maskSparseCoreQueueAssignment::GetAllowedCores(HloInstruction*)0x10FDA3C0)→ btree_set<long,…,256>
有序选择SparseCoreQueueAssignment::SelectCores(hlo, allowed, devcount, cost, assigned, reach, assign_groups)0x10FDC4E0)→ StatusOr<vector<long>>
Mask 输入GetChipIDsFromParallelismConfig0x10FDBF40)∩-by GetSparseCoreResources0x10FDC0A0
Resource-type 集合{0, 23, 24, 25, 26, 27, 28}.rodata 0xAC0A8E0..0xAC0A910,逐字节读取;jump table @0xAC0A82C
Reservation budget逐 resource 线程本地 long__tls_get_addr(&qword_22048D78)),递减,由 >= 2 gate
选择 passP1 same-plane · P2 data-dep(HloReachabilityMap)· P3 assign-group · P4 not-different-plane · P5 fallback
Tie-breakcost 升序 stable_sort comparator $_0vucomisd0x10FE8AE0
调用方 / sinkAssignQueueIDsToAsyncStart0x10FDF480)→ __sortAddCollectivePhysicalCoreIndices0x1C868500)→ physical_core_indices

端到端流水线

目的

核心选择是 queue assignment 的一个子步骤。对每个 async-start collective,SparseCoreQueueAssignment pass 必须决定它占用哪些物理 SC core;该决策成为一个 long core ID 列表(截断为 int32),存储在 op 的 physical_core_indices backend-config 字段中,下游 emitter 会读取它。选择本身从不接触 proto —— 它产生一个 absl::Span<long const>,由调用方提交。

入口点

调用方 AssignQueueIDsToAsyncStart0x10FDF480)中的链条已逐字节确认:

text
AssignQueueIDsToAsyncStart (0x10FDF480)               ── per-collective driver
  ├─ GetAllowedCores(hlo) (0x10FDA3C0)                ── candidate btree_set<long>          [call @ caller line ~236]
  ├─ SelectCores(hlo, allowed, devcount, cost,        ── ordered vector<long> (UNSORTED)    [call @ caller line ~406]
  │              assigned, reach, assign_groups) (0x10FDC4E0)
  ├─ std::__u::__sort<__less<long>>(begin, end)        ── numeric ascending sort of the result [caller line ~459]
  └─ AddCollectivePhysicalCoreIndices(hlo, span)       ── write physical_core_indices       [caller line ~521]
       (backend_config_util::AddCollectivePhysicalCoreIndices, 0x1C868500, absl::Span<long const>)
```text

### 算法

```text
allowed   = GetAllowedCores(hlo)                       // btree_set<long> candidate mask
selected  = SelectCores(hlo, allowed, devcount, cost,  // 5-phase greedy filter; UNSORTED
                        assigned, reach, assign_groups)
sort(selected, ascending)                              // numeric — in the CALLER, not SelectCores
AddCollectivePhysicalCoreIndices(hlo, span(selected))  // long -> int32, write proto field

GOTCHA — SelectCores 返回的 vector 处于 {phase, cost} 顺序,不是数值顺序。生成最终 physical_core_indices 的数值 __sort 位于调用方(0x10FDF480),在 SelectCores 返回之后。若重新实现者在选择例程内部排序,或把选择顺序当成 proto 顺序,当列表随后被截断到 device/megachip 数量时会错误地给核心排名 —— 因为调用方排序后截断会丢弃编号最高的核心,但决定哪些核心会被追加的是phase-and-cost 顺序。

相关函数

FunctionAddress作用
SparseCoreQueueAssignment::GetAllowedCores0x10FDA3C0候选核心 mask(btree_set)
SparseCoreQueueAssignment::SelectCores0x10FDC4E0五阶段有序选择
SparseCoreQueueAssignment::AssignQueueIDsToAsyncStart0x10FDF480逐 collective driver;调用二者,然后排序
backend_config_util::AddCollectivePhysicalCoreIndices0x1C868500Span<long const> 写入 physical_core_indices

GetAllowedCores — 候选核心 Mask

目的

GetAllowedCores0x10FDA3C0)是一个成员函数(sret),签名为 GetAllowedCores(HloInstruction*) → btree_set<long,less<long>,allocator<long>,256>。它返回 SelectCores 可为一个 collective 选择的物理 SC core ID 池。该池是 collective 的 megachip parallelism 覆盖的 chip 与该 collective scheduling-resource reservation 下仍可用核心的交集。

入口点

text
GetAllowedCores (0x10FDA3C0)                           ── sret = btree_set<long>; rdx = hlo
  ├─ GetChipIDsFromParallelismConfig(hlo) (0x10FDBF40) ── vector<long> megachip per-axis chip IDs
  │    └─ GetMegaChipParallelism (0x1C867B00)          ── StatusOr<InlinedVector<long,4>>; per-axis split
  ├─ walk device-assignment member set                  ── Swiss-table SOO: control [hlo+0x90], slots [hlo+0x98], slot stride 0x68 (13 qwords)
  │    └─ GetSparseCoreResources(member-op) (0x10FDC0A0)── btree_set<long> resource-type IDs {0,23..28}
  ├─ flat_hash_map<resource_id, btree_set<chip_id>>     ── @ policy global 0x2181D940 (group chips by resource)
  ├─ flat_hash_map<chip_id, refcount>                   ── @ policy global 0x21639C10 (per-chip occupancy)
  ├─ per-resource thread-local budget                   ── __tls_get_addr(&qword_22048D78); dec; gate `>= 2`
  └─ second pass over [hlo+0xC0]                         ── re-apply budget; assemble output btree_set
```text

### 算法 — 两个输入

**`GetChipIDsFromParallelismConfig`**(`0x10FDBF40`)读取 `GetMegaChipParallelism`(`0x1C867B00`)—— 也就是 queue-assignment driver 使用的同一个 proto reader —— 并把每个逐轴 `MegaChipParallelism` 值复制进 `RepeatedField<long>`。空 `StatusOr` 是硬错误。因此这里的 "chip IDs" 是逐轴 megachip split 列表,不是 device 列表。

**`GetSparseCoreResources`**(`0x10FDC0A0`)返回 collective 占用的 AsyncTracker scheduling resource-type ID 的 `btree_set<long>`,逐字节确认如下:

```c
function GetSparseCoreResources(op):                   // 0x10FDC0A0
    if op.opcode == 0x11 /* custom-call */:
        cfg = GetSparseCoreConfig(op)                  // 0x1C868D20 (backend_config_util)
        if (cfg.has_type() /* type hasbit */):         // byte-exact: `(cfg_byte[0x10] & 4) != 0`
            switch (collective_type_enum - 1) of {0..6}:  // `dec eax; cmp 6; ja default`, jump table @0xAC0A82C
                case 0 (enum 1): insert 28 (.rodata 0xAC0A910)
                case 1 (enum 2): insert 23 (.rodata 0xAC0A8E8)
                case 2 (enum 3): insert 24 (.rodata 0xAC0A8F0)
                case 3 (enum 4):                       // NOT a constant — derive from the wrapped op:
                    body = op.async_wrapped_instruction()  // 0x1E5AA300
                          .called_computations()           // 0x1E5885A0  (root instruction)
                    insert AsyncTracker::GetResourceTypeForOp(root.opcode)  // 0x13612240
                case 4 (enum 5): insert 25 (.rodata 0xAC0A8F8)
                case 5 (enum 6): insert 26 (.rodata 0xAC0A900)
                case 6 (enum 7): insert 27 (.rodata 0xAC0A908)
                default:         insert 0  (.rodata 0xAC0A920)
        // else (type hasbit clear): insert 0 (.rodata 0xAC0A920)
    else:  // not a custom-call
        insert 0 (.rodata 0xAC0A8E0)                   // single resource-type 0
    return result                                      // btree_set<long>, insert_hint_unique

Resource-type long 常量直接从 .rodata 读取(VMA == file offset):0xAC0A8E0 = 00xAC0A8E8 = 230xAC0A8F0 = 240xAC0A8F8 = 250xAC0A900 = 260xAC0A908 = 270xAC0A910 = 28,以及全零 slot 0xAC0A918 = 0 / 0xAC0A920 = 0default / hasbit-clear 分支)。每个非零值都是一个不同的 AsyncTracker scheduling resource;每个 budget unit 中一个核心可为每个 resource 持有一个 collective,这正是限制候选集合的机制。注意结构:六个 switch 分支(enum 1,2,3,5,6,7)插入固定常量 {28,23,24,26,27}{25}enum-4 分支不插入常量 —— 它 unwrap async op(async_wrapped_instructioncalled_computations root),并插入 AsyncTracker::GetResourceTypeForOp(root.opcode) 返回的值。非 custom-call 路径和 default / hasbit-clear 路径插入单个 resource-type 0

算法 — mask 构建和 reservation budget

主体遍历 device-assignment / replica-group 结构,并通过两个 Swiss table 累积候选池:

c
function GetAllowedCores(hlo):                         // 0x10FDA3C0
    chips     = GetChipIDsFromParallelismConfig(hlo)   // megachip per-axis chip IDs
    by_res    = flat_hash_map<long, btree_set<long>>{} // resource_id -> chip_set   (policy @0x2181D940)
    occupancy = flat_hash_map<long, long>{}            // chip_id     -> refcount   (policy @0x21639C10)
    budget    = __tls_get_addr(&qword_22048D78)        // &(per-resource thread-local reservation budget)

    for assignment in device-assignment (Swiss-table SOO @[hlo+0x90]/[hlo+0x98], slot stride 0x68):
        resources = GetSparseCoreResources(member-op)  // {0,23..28}
        for (res, chip) in (resources × chips):
            v = (*budget)--                            // post-decrement the in-place TLS long
            if v >= 2:                                 // gate (pre-decrement value): chip still reservable
                by_res[res].insert(chip)               // insert_hint_unique
                occupancy[chip] += 1                   // inc [slot+8]

    // second pass re-applies the budget over [hlo+0xC0]
    result = btree_set<long>{}
    for chip surviving the budget across by_res / [hlo+0x70] device count:
        result.insert(chip)                            // internal_emplace / insert_hint_unique
    return result
```text

> **NOTE —** budget gate 是两个位置上的 **post-decrement-then-compare** 模式 `v = (*budget)--; if (v >= 2)`(每个 map-fill pass 一个;TLS `long` 通过 `__tls_get_addr(&qword_22048D78)` 加载,后递减,然后测试 `>= 2`)。**写入并初始化**该线程本地 `long` 的 writer —— 因而它的初始值 —— 在此函数中不可见。Reservation 是 embedding-device / reserved-core knob:这是把为 embedding device 保留的核心(`sc_dev − num_embedding_devices` split)从候选池中排除的机制;seed 是 `NumEmbeddingDevices`、固定的逐 resource 核心上限,还是 `LogicalDevicesPerChip(SC)`,这里无法确定。该 budget 所属的 resource→limit map 见 [SC Queue Assignment & Reservation](sc-queue-assignment-reservation.md)。
>
> **GOTCHA —** 两个 map global 是*策略* singleton,不是逐调用状态:`flat_hash_map<long, btree_set<long,…,256>>::GetPolicyFunctions()::value` 位于 `0x2181D940`,`flat_hash_map<long, long>` 位于 `0x21639C10`。btree empty-node sentinel 是 `0x2181D930`。重新实现者实例化这些 map 时,必须匹配 resource→chip-set value type 的 `btree_set<...,256>` node fan-out(256);`256` 是类型的一部分,btree-node walk stride 依赖它。

### 函数图

| Function | Address | 作用 |
|---|---|---|
| `SparseCoreQueueAssignment::GetAllowedCores` | `0x10FDA3C0` | 候选 mask 构建(sret btree_set) |
| `(anon)::GetChipIDsFromParallelismConfig` | `0x10FDBF40` | megachip 逐轴 chip ID → `vector<long>` |
| `(anon)::GetSparseCoreResources` | `0x10FDC0A0` | 逐 collective resource-type ID `{0,23..28}` |
| `GetMegaChipParallelism` | `0x1C867B00` | `StatusOr<InlinedVector<long,4>>` 逐轴 split |
| `backend_config_util::GetSparseCoreConfig` | `0x1C868D20` | offload-config 读取(type enum + hasbit) |
| `AsyncTracker::GetResourceTypeForOp` | `0x13612240` | enum-4 switch 分支:从 unwrapped async-op root opcode 得到 resource-type |
| reservation budget | `__tls_get_addr(&qword_22048D78)` | 逐 resource 排除计数器 |

---

## `SelectCores` — 五阶段贪婪过滤器

### 目的

`SelectCores`(`0x10FDC4E0`)是一个成员函数(sret),返回 `StatusOr<vector<long>>`。它接收 allowed btree-set、device count、一个 `double cost`、已分配 collective 的运行中 `vector<Info>`、`HloReachabilityMap` 和 assignment-group vector,并按贪婪优先级顺序产生核心。符号表中的 demangled 签名:

```text
SelectCores(HloInstruction const* hlo,
            btree_set<long, less<long>, allocator<long>, 256> const& allowed,
            long devcount, double cost,
            vector<Info>& assigned,
            HloReachabilityMap& reach,
            vector<vector<HloInstruction*>>& assign_groups)
    -> StatusOr<vector<long>>

Info Struct(sizeof 0x60

assigned vector 的每个元素描述一个已经放置的 async collective。该 struct 为 0x60 字节(元素 stride 计算为 lea rax,[rax+rax*2]; shl rax,5,即 ×3 ×32 = ×96)。字节布局由逐阶段字段比较确认:

text
[Info+0x00]  HloInstruction*   the already-assigned async collective op
[Info+0x08]  vector<long> tag  low bit (bit0) = is-heap flag; if heap, size = tag>>1
[Info+0x10]  vector<long> data the physical cores this assigned op holds (inline if <=4, else heap ptr)
[Info+0x30]  NDPlaneInfo       the SC ND-plane descriptor:
             +0x30 int32 plane#1 | +0x34 int32 #2 | +0x38 int32 #3
             +0x3C int32 size_x  | +0x40 bool has_x
             +0x44 int32 size_y  | +0x48 bool has_y
             +0x4C int32 size_z  | +0x50 bool has_z
             +0x54 bool          (NDPlaneStrideInfo trailing byte)
             +0x58 bool          across_cores_on_chip (megacore / both-cores flag)
```text

`NDPlaneInfo` 子布局与 SC collective 栈其他位置使用的 descriptor 匹配;`NDPlaneInfo::ToString`(`0x10FDF2A0`)是 VLOG 调用的 formatter(不存在时用字面量 `"None"`)。

### 算法 — Phase 0 setup 和 cost tie-break

```c
function SelectCores(hlo, allowed, devcount, cost, assigned, reach, assign_groups):  // 0x10FDC4E0
    // a) count allowed btree elements (leaf-node walk, 0x100 stride)
    // b) materialize allowed IDs into a heap vector<long>  (allowed_vec)
    allowed_vec = materialize(allowed)
    // c) the ONLY use of `cost`: order candidates by ascending per-core weight
    stable_sort(allowed_vec, $_0)        // $_0(a,b): vucomisd weight[a], weight[b]  (0x10FE8AE0 / cmp @0x10FE8B34)
    // d) get THIS collective's ND plane (the target plane the predicates test against)
    target_plane = TryGetNDPlaneInfoForSparseCoreCollectives(hlo, this->target_)  // 0x10FDEDC0
    if !target_plane.ok(): return Error(src line 269)   // AddSourceLocationImpl(..., 269, "…sparse_core_queue_assignment.cc")
    selected = []
    for phase in [P1, P2, P3, P4, P5]:
        for core in allowed_vec:          // ascending cost
            if core in selected: continue
            if phase.predicate(core, assigned, reach, assign_groups, target_plane):
                selected.append(core)     // grow via _Znwm + memcpy
                VLOG(phase.msg)
    return Ok(selected)                   // sret: [+0]=1 ok-tag, [+8]=data, [+0x10]=size, [+0x18]=cap

QUIRK — double cost 参数只在一个位置被消费:stable_sort comparator $_0(对逐核心 double weight array 执行 vucomisd)。它只是 tie-break。在同等满足某个 phase predicate 的候选中,低 cost 核心先追加。没有决定核心是否被选中的逐候选 score —— 只有 phase predicate 决定。填充每个核心 weight 的算术(调用方中的 LatencyEstimator / queue-occupancy feed)在此函数中不可见;这里确定的只是 comparator 的使用方式(升序排序)。

算法 — 五个 predicate

每个 pass 都扫描 assigned;SIMD vpcmpeqq(4-wide)搜索会测试候选核心是否出现在某个 Info 的物理核心列表 [Info+0x10..] 中,然后应用逐 phase predicate。VLOG 字符串已在反编译中按源码顺序逐字节确认。

text
P1  SAME ND PLANE          (lambda $_1, src line 279):
    exists Info holding `core` whose NDPlaneInfo[+0x30] == target_plane (all 3 ints, the
    per-axis has/size optionals, and across_cores[+0x58]) -> append.
    VLOG: "Adding core <c> to selected cores for <hlo> because it is running <NDPlaneInfo|None>
           on the same ND plane."

P2  DATA DEPENDENCY        (lambda $_2, src line 302):
    probe HloReachabilityMap `reach`: crc32 Swiss find keyed on (GetModule()->[module+0xAC4],
    HloInstruction::unique_id()) for BOTH the Info's op and the target hlo; on hit, test the
    reachability bit  (row = bitidx>>0xA, bound [reach+0x40]; col = bitidx&0x3FF scaled by
    [reach+0x28]; base [reach+0x38]; `bt` test). Reachable in either direction -> append.
    VLOG: "Adding core <c> ... because an assigned op <op> has data dependency with it."

P3  ASSIGNMENT-GROUP HINT  (lambda $_3, src line 329):
    for each group in assign_groups containing `hlo`: for each group member an Info holds and
    that appears in allowed_vec -> append that core (this is a PIN).
    VLOG: "Adding core <c> ... due to hint from pre-determined assignment groups."

P4  NOT A DIFFERENT PLANE  (lambda $_5 add, src line 362 / lambda $_4 skip, src line 352):
    same field compares as P1 but the success branch is the negation (`setne; and across_cores`).
    candidate NOT found running a collective on a DIFFERENT ND plane -> append.
    VLOG (add): "Adding core <c> ... because it is not running a collective on a different ND plane."
    VLOG (skip): "Core <op> is running <NDPlaneInfo|None> on a different ND plane."

P5  FALLBACK FILL          (no VLOG):
    every candidate in allowed_vec not already selected -> append unconditionally.
```text

| Phase | Src line | Predicate(候选核心 `c`,扫描 `assigned`) | VLOG |
|---|---|---|---|
| P1 | 279 | ∃ `Info` 持有 `c`,且其 `NDPlaneInfo == target`(**same plane**) | "… because it is running … on the same ND plane." |
| P2 | 302 | ∃ `Info`,其 op 与 `hlo` 可达 ↔(**data dependency**) | "… has data dependency with it." |
| P3 | 329 | `hlo` ∈ 某个 assignment group ∧ 某个成员的 `Info` 持有 `c` | "… due to hint from pre-determined assignment groups." |
| P4 | 362 | **没有**持有 `c` 的 `Info` 位于**不同** ND plane | "… because it is not running a collective on a different ND plane." |
| P4 (skip) | 352 |(`c` **在**不同 plane 上 → skip) | "Core … is running … on a different ND plane." |
| P5 | — | always(追加每个剩余 allowed core) |(无) |

> **NOTE —** `SelectCores` 内部的 assignment-group VLOG 是**正向** pin("Adding core … due to hint …",第 329 行)。互补诊断 "Not pinning … due to hint from pre-determined assignment groups." **没有**在 `SelectCores` 或 `GetAllowedCores` 内部被引用;它位于更高层的 assignment-group 决策(group construction / `AssignQueueIDsForComputation` 层)。`SelectCores` 自己的逐候选 "skip" 诊断是 P4 的 "Core … is running … on a different ND plane."(第 352 行)。

### 返回形状

`SelectCores` 把 `StatusOr<vector<long>>` 写入其 sret:`[sret+0] = 1`(ok tag),`[sret+8] = data ptr`,`[sret+0x10] = size`,`[sret+0x18] = capacity`。Vector **未排序** —— 其顺序精确为 `{P1 same-plane} ++ {P2 data-dep} ++ {P3 assign-group} ++ {P4 not-different-plane} ++ {P5 all-remaining}`,每个 pass 内 cost 升序。调用方在把它复制(截断 `long → int32`)到 `physical_core_indices` 前,会对它进行数值排序。

> **GOTCHA —** `SelectCores` 中**没有**闭式逐候选数值 score,也**没有**显式 core→queue 双射算术。Placement 是确定性贪婪优先级:候选的位置为 `(phase index 1..5, then ascending per-core cost)`,其中 phase 成员由持有该核心的已分配 async collective 是否 same-plane / data-dependent / 在同一 assignment group / 在不同 plane 上决定。重新实现者必须复现*pass 顺序*和*cost tie-break*,而不是发明标量 score。

### 函数图

| Function | Address | 作用 |
|---|---|---|
| `SparseCoreQueueAssignment::SelectCores` | `0x10FDC4E0` | 五阶段有序选择(sret StatusOr) |
| `…::SelectCores(...)::$_0`(在 `__stable_sort` 中) | `0x10FE8AE0` | cost 升序 comparator(`vucomisd` @ `0x10FE8B34`) |
| `(anon)::TryGetNDPlaneInfoForSparseCoreCollectives` | `0x10FDEDC0` | predicate 使用的目标 ND-plane |
| `NDPlaneInfo::ToString` | `0x10FDF2A0` | VLOG plane formatter(或字面量 `"None"`) |
| `HloReachabilityMap` probe | — | P2 bit-matrix `bt` test(crc32 Swiss find) |
| `HloInstruction::async_wrapped_instruction` | `0x1E5AA300` | 用于 resource 派生的 unwrap(与 mask 共享) |

---

## `tensor_split_factor` 交互

### 目的

相关字段是 collective offload-config 的 **`tensor_split_factor`**(此 build 中没有 `tensor_split_mode` 字段 —— “mode” 由 `tensor_split_factor() > 1` 是否成立隐含)。它**不会**出现在 `SelectCores` 或 `GetAllowedCores` 内部。相反,它在 collective ring-strategy 层的*下游*被消费,控制*所选*核心如何分区一个 collective 的 tensor。因此它与核心选择的关系是数量约束,而不是选择输入。

### 它位于哪里

字符串 `tensor_split` 和 `tensor_split_factor` 解析到 SC collective strategy 代码,而不是 queue-assignment pass。两个逐字节确认的位置:

```text
SinglePhaseAGStrategy::AdjustStrategiesForSingleCoreIfNeeded (0x1338C1E0):
  gate:  "offload_config().use_single_sparse_core()
          || (offload_config().has_tensor_split_factor()
              && offload_config().tensor_split_factor() > 1)"
  error: "Ring adjustment is only supported either for single core or tensor-split mode."

emitter_helpers::CreateRingStrategiesForNdFromExplicitTable (0x13390900):
  RET_CHECK: "ici_strategy_config.color_strategies_size() % tensor_split_factor.value_or(1) == 0"
           (offload_collective_strategies.cc:3966)

因此 mode 是:single-coreuse_single_sparse_core() → collective 折叠为一个 SC core)与 tensor-splittensor_split_factor() > 1 → tensor 跨那么多个核心拆分)。Ring strategy 要求 color strategy 数量能被 tensor_split_factor 整除(默认 1)。

它如何关联选择

SelectCores/GetAllowedCores 产生一个 collective 可用的物理核心集合tensor_split_factor 决定单个 collective 的 ring 实际扇出到其中多少个核心,整除性 RET_CHECK 确保 strategy table 能均匀分区。选择策略对 split mode 无感 —— 它既不读取 tensor_split_factor,也不根据 use_single_sparse_core 分支。重新实现者应把 tensor_split_factor 当作发出的 strategy 的属性处理,它必须与所选核心数量一致,并通过 offload-config 传播;该 offload-config 由 GetSparseCoreResources 用于 resource-type enum 的同一个 GetSparseCoreConfig reader 读取。

NOTE — tensor_split_factor ↔ selected-core-count 一致性的 gate 字符串和整除性 RET_CHECK 都位于消费 physical_core_indices 的 collective strategy 层。tensor_split_factor 为 N 时映射到所选核心列表中哪 N 个具体条目的精确算术,属于 ring-strategy 构造的一部分,超出本选择页面范围(它属于 offload-strategy emitter)。tensor_split_factor 是所选核心集合的 consumer,不是选择输入。

函数图

FunctionAddress作用
SinglePhaseAGStrategy::AdjustStrategiesForSingleCoreIfNeeded0x1338C1E0single-core vs tensor-split gate
SinglePhaseRSStrategy::AdjustStrategiesForSingleCoreIfNeeded0x1338A7A0reduce-scatter analogue
emitter_helpers::CreateRingStrategiesForNdFromExplicitTable0x13390900color_strategies_size() % tensor_split_factor == 0
backend_config_util::GetSparseCoreConfig0x1C868D20offload-config reader(与 mask 共享)

逐代际说明

SelectCoresGetAllowedCores 的代码中没有按代际分支。候选池随 chip 的 megachip parallelism 和 SC count 变化(通过 GetMegaChipParallelism 和 device assignment 数据驱动),Info[+0x58] 中的 across_cores_on_chip flag 是 plane predicate 读取的 megacore / both-cores 判别符 —— 但策略(五个 pass、cost tie-break、resource-reservation 交集)在所有硅片上相同。约束一个 chip 暴露多少物理 SC core 的 4:1 SparseCore:TensorCore 比例在 SparseCore Hardware ArchitectureSparseCoreCountPerTensorCore0x1C6CB760)中确立;本页消费该数量,而不计算它。


相关组件

Name关系
AssignQueueIDsToAsyncStart0x10FDF480调用 GetAllowedCores + SelectCores,然后排序结果的 driver
AddCollectivePhysicalCoreIndices0x1C868500把已排序 core list 写入 physical_core_indices 的 sink
GetSparseCoreConfig0x1C868D20resource-type enum 和 tensor_split_factor 共享的 offload-config reader
AsyncTracker::GetResourceTypeForOp0x13612240enum-4 switch 分支的 resource-type 派生(来自 unwrapped async-op root opcode),供给 GetSparseCoreResources

交叉引用