二项式/递归加倍
此页面上的所有地址、符号和偏移量均适用于
libtpu-0.0.40-cp314轮中的libtpu.so(构建 ID89edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,未剥离,.textVA == 文件偏移量)。其他版本会有所不同;将每个 VA 视为版本固定。
摘要
二项式单相 AllReduce 发射器是 ICI 结构上的 延迟最佳 轴内集合:一个 递归加倍蝶形,其中每个核心在步骤 k 上与其环位置位 k 不同的伙伴进行交换和归约,因此在 log₂(N) 对称之后每个核心的步骤都保存完整的总和。没有单独的全部聚集——蝴蝶是自我完成的。这是 BinomialSinglePhaseRingSumEmitter::EmitAllReduceOnSpan (0x13769be0) 发出的算法,它与 SelectND策略选择器 为其选择的带宽最佳环旋转(2·(N−1) 步骤,请参阅 分层 AllReduce / Pincer)相反。
该页面记录了重新实现者无法仅从操作表面猜测到的三个耦合的事情:
- 两个循环形状并排 — 二项式蝴蝶(
log₂(N)步骤,距离加倍1,2,4,…)与环的减少分散 + 全聚集(N−1+N−1步骤,固定+1邻居,块索引)旋转)——因此延迟/带宽的权衡是具体的。 - 每步伙伴数学 -
partner = my_pos ± (1<<step)具有无分支模块化包装选择,在构建时间(副本表编写器)和发出时间(每步循环)字节相同。符号选择错误会悄悄地将步骤引导至错误的核心。 - 二项式副本表 —
CreateStaticBinomialReplicaInfoTable(0x1375efa0) 将整个每秩蝶式调度表预先计算到由(rank×8 + step)索引的int32[N×8]表中,具体化为 HLOLiteral常量(ConstantMapper 类型 7)并加载每个核心在发射时,因此发射器永远不会重新计算合作伙伴 - 它遍历CoreLocationBase[]对应向量。该表是平面星势垒表的结构逆(设备→序数,1 列)。
共享归约原语(RingSumEmitter::ReduceShardInPlace、0x1375d460、bf16-accumulate-in-f32 归约器)和每步线 DMA(EnqueueDmaInGranules → DMA_TYPE_REMOTE_WRITE_UNICAST)由 ICI AllReduce 原语 在系列级别进行记录;本页引用了它们,而不是重新推导它们,并集中于合作伙伴时间表——这是二项式与环的唯一区别。
| 二项式发射器 | BinomialSinglePhaseRingSumEmitter::EmitAllReduceOnSpan(bool phase, b) — 0x13769be0(414行反编译行) |
| 初始化/对应安装 | BinomialSinglePhaseRingSumEmitter::EmitInitialization — 0x137699c0 |
| 生存能力门 | IsBinomialSinglePhaseAllReduceViable — 0x1375ed80 (popcnt(N)==1 && (bsr(N)^7)<8) |
| 副本表构建 | CreateStaticBinomialReplicaInfoTable — 0x1375efa0 (int32[N×8]) |
| 每行蝴蝶写入器 | $_2 lambda — 0x1375fb20 (partner = my_pos ± (1<<step)) |
| 设备 ID 解析器 | $_5 lambda — 0x13761260(ReplicaGroup ids / DeviceAssignment 扁平化) |
| HLO-恒定包装 | CreateBinomialReplicaInfoTable — 0x1375ee40 (LiteralUtil::CreateR1<int>) |
| 每核表读取 | LoadBinomialReplicaInfoTable — 0x1375fca0(replica_id×8+col、LoadInfoTable count=8) |
| 保留接收标志 | receive_sync_flags_needed() — 0x1376a680(返回 7) |
| 环搭档 (1-D) | UniDirection1DRingStrategy::CwCore — 0x137d7680(固定 +1 CW 邻居) |
| 环搭档(N-D) | BaseStrategyND::TorusStrideNPhasekNeighbor — 0x137c5960 ((pos+stride·step) mod N) |
| 帽 | kBinomialCounterpartsCount = 8 ⇒ N ∈ {2,4,8,16,32,64,128} |
| 来源 TU | platforms/xla/service/jellyfish/lowering/ring_sum_emitter.cc |
两种并排的环形状
五个 ICI AllReduce 系列可分解为三个步生成循环形状。此页面涵盖了仅合作伙伴日程安排不同的两个项目;双向钳子记录在 分层 AllReduce / Pincer 中。所有形状共享 ShardAddress(平面 VMEM 偏移到每个颜色划痕中)和 ReduceShardInPlace(VPU 合并);只是伙伴的行走和步数有所不同。
二项式 — 递归加倍蝶形(自完成)
在步骤k,核与环位置不同位k的核交换其当前累加器,然后就地减少接收到的分片。在 log₂(N) 步骤之后,差异位被耗尽,因此每个核心都汇总了其他每个核心的贡献 - 没有单独的全收集。
emit_binomial_all_reduce(span, b): # 0x13769be0
N = ring_size # power of 2, 2..128
assert popcount(N) == 1 and (bsr(N) ^ 7) < 8 # IsBinomial...Viable gate
L = bsr(N) # = log2(N) = number of steps
my_pos = position_in_ring # from the replica table row
if barrier_scope == kAll: # *(this+0x110) == 3
for cp in counterparts: # pre-loop tree barrier
BarrierCoresStart(cp, GetBarrierSyncFlag(cfg, b), 1<<k, b)
src = ShardAddress(self, span, b) # local accumulator (whole slice)
for step in 0 .. L-1:
pcore = counterparts[step] # PRECOMPUTED — table[rank*8+1+step]
EnqueueDmaInGranules(src, pcore, REMOTE_WRITE_UNICAST, ...)
recv = DmaDoneInGranules(...).annotate("shard-recv-wait")
# runtime butterfly (mirrors the build-side $_2 writer, see below)
dist = 1 << step # DISTANCE DOUBLES: 1,2,4,...
lo = my_pos & (0xFFFFFFFFFFFFFFFF >> (63 - step)) # low step+1 bits
partner = (lo < dist) ? my_pos + dist : my_pos - dist
ReduceShardInPlace(src, recv, useBf16=is_bf16, b)
# after L steps every core holds the full reduction (no AG emitted)
```text
### 环 — 减少分散 + 全聚集(带宽最优)
环族从不计算发送方的每步伙伴索引。物理链路目标是**每一步相同的 `+1` (CW) 邻居**;旋转的是每个核心转发的“块索引”。 AllReduce = 减少分散然后全部收集,每个 `N−1` 步骤,由 `bool phase` 参数线程化。
```text
emit_ring_all_reduce(span, b):
for color in 0 .. num_colors-1: # concurrent rings, one per axis
N = ring_size[color]
pos = ring_position[color]
# ---- PHASE 0: reduce-scatter (N-1 steps, with reduce) ----
for i in 0 .. N-2:
cw = CwCore(step=i, ring=color) # fixed +1 neighbour @ strategy+0xd8
EnqueueDmaInGranules(ShardAddress(self, (pos-i) mod N), cw, ...)
recv = DmaDoneInGranules(...).annotate("shard-recv-wait")
ReduceShardInPlace(ShardAddress(self, (pos-i-1) mod N), recv, is_bf16, b)
# ---- PHASE 1: all-gather (N-1 steps, no reduce) ----
for i in 0 .. N-2:
cw = CwCore(step=i, ring=color)
EnqueueDmaInGranules(ShardAddress(self, (pos+1-i) mod N), cw, ...)
DmaDoneInGranules(...).annotate("shard-send-wait")
SafeMemcopyN(recv -> ShardAddress(self, ...)) # plain copy, no merge注意 —
EmitAllReduceOnSpan采用bool phase第一个参数 (esi/r12d)。 “单相”这个名称是指“蝴蝶是一次融合遍历”,而不是指单相参数;同一发射器还包含用于非蝶形跨度情况的环形全聚集SafeMemcopyN路径(0x13769be0线 313/393)。反编译显示从同一函数发出的"shard-recv-wait"(减少端)和"shard-send-wait"(收集端)注释字符串。
权衡
| 二项式蝶形 | 单向环 | |
|---|---|---|
| AllReduce 的步骤 | log₂(N) | 2·(N−1) (RS N−1 + AG N−1) |
| 每个链接的字节数 | 冗余(全切片每一步) | (N−1)/N 的数据(最优) |
| 延迟 | 最优(log₂(N)往返) | 线性于 N |
| 限制 | N 2的幂,N ≤ 128 | 任意 N |
| 自补? | 是(无 AG) | 无(显式 AG 阶段) |
二项式路径是受延迟限制的——几个步骤,每个步骤都携带一个冗余副本——并且环是受带宽限制的。选择器仅针对往返延迟占主导地位的小 2 次幂环选择二项式;大轴或非 2 倍轴会落回环或钳上。请参阅 SelectND策略 和 SPMD 链路计数成本 以了解决定。
生存能力之门
IsBinomialSinglePhaseAllReduceViable (0x1375ed80) 决定二项式路径对于给定的环大小和编译环境是否合法。相同的谓词被内联到表构建序言(0x1375efa0)中,因此构建和调度在构造上是一致的。
// IsBinomialSinglePhaseAllReduceViable — 0x1375ed80 (decompiled, simplified)
bool viable(target, long N, bool use_global_device_ids, DeviceAssignment* da, env):
// env-flag gate: both binomial-AR enables must be set (or a vtable override)
if (env[0xf48] != 1 || env[0x10c8] != 1 || ((N >= 2 && (N & 1)) | target.vtable[0x18]()))
return false; // NB: the (N&1) term is a deopt path
if (!use_global_device_ids) {
if (N < 2) return false;
goto check_pow2;
}
// cross-module / global-device-id path
if (env[0x1006] != 1) return false;
if (da && env[0xe1e] == 0 && N >= 2 && env[0xfa3]) // use_physical_core_ids must be 0
goto check_pow2;
return false;
check_pow2:
if (popcnt(N) != 1) return false; // N is a power of 2
return (bsr(N) ^ 7) < 8; // log2(N) <= 7 ⇒ N <= 128
```text
`popcnt(N) == 1` 强制 `N` 为 2 的幂; `(bsr(N) ^ 7) < 8` 是 clz 型上限 — `bsr(N)` 是 `log₂(N)`,并且 XOR-with-7 然后 `< 8` 测试拒绝 `log₂(N) > 7`,即 `N > 128`。所以二项式环被限制为**`N ∈ {2, 4, 8, 16, 32, 64, 128}`**。上限为`kBinomialCounterpartsCount = 8`(副本表的8列=自身+最多7个蝴蝶伙伴),这就是为什么`receive_sync_flags_needed()`返回精确的`7`(`0x1376a680`)。
| 环境字段(偏移) | 属性名称(来自使用) | 角色 |
|---|---|---|
| `0xf48` | `xla_jf_*binomial_all_reduce` 使能 | 必须是 `1` |
| `0x10c8` | 第二二项式-AR 使能 | 必须是 `1` |
| `0x1006` | 跨模块二项式使能 如果全局 id, | 必须是 `1` 对于全局 ID 路径, |
| `0xe1e` | `use_physical_core_ids` | 必须是 `0` |
| `0xfa3` | 跨模块门 | 必须非零 |
> **注意 —** 偏移量是字节确认的(在 `IsBinomial...Viable` 和建表序言中相同)。标志*名称*(`xla_jf_*` / `xla_tpu_binomial_all_reduce_*`)归因于字节的使用方式,而不是来自恢复的原始描述符 - 将它们视为已确认偏移量上的低置信度标签。
---
## 每步合作伙伴数学
蝴蝶伙伴是 `partner = my_pos ± (1<<step)`,选择符号后,伙伴将留在 `[0, N)`。选择**无分支**:测试`my_pos`的低`step+1`位是否低于步距;如果是则加,否则减。相同的算术出现两次——一次在构建时(表编写器),一次在发出时(循环)——并且它们逐字节相同,这是预计算表和运行时行走一致的证明。
### 构建端:`$_2` 每行写入器 (`0x1375fb20`)
```c
// CreateStaticBinomialReplicaInfoTable::$_2 — 0x1375fb20 (decompiled)
my_pos = use_partition ? (N * partition_count + rel) : rel; // 1D: rel == member index
table[write_pos + 0] = my_pos; // COLUMN 0 = self
step = 0;
while (step < log2N) { // *result == log2(N)
v15 = 1 << step; step++; // dist = 1<<step ; step now = k+1
v16 = (int64)v15;
v17 = -v16;
if ((my_pos & ~(-1 << step)) < v16) // low k+1 bits (step pre-incremented) below dist?
v17 = v16; // -> add
partner = my_pos + v17; // my_pos ± dist
CHECK(partner >= 0); // ring_sum_emitter.cc:1810
CHECK(partner < N); // ring_sum_emitter.cc:1811
table[write_pos + step] = $_5(partner); // resolve to global device-id
}两个 FATAL CHECK 携带文字字符串 "counterpart_ordinal >= 0"(第 1810 行)和 "counterpart_ordinal < num_participants_per_group"(第 1811 行),确认 [0, N) 换行绑定。
发射侧:环路体(0x13769be0)
// EmitAllReduceOnSpan step loop — 0x13769be0 (decompiled, lines 351-360)
v27 = 1LL << v105++; // dist = 1<<step ; step++
mask = SimmU32(0xFFFFFFFFFFFFFFFF >> v47); // step-bit mask
lo = SandU32(my_pos, mask); // low bits of my_pos
cond = SltS32(lo, SimmS32(dist)); // lo < dist ?
up = SaddS32(my_pos, SimmS32(dist)); // my_pos + dist
down = SsubS32(my_pos, SimmS32(dist)); // my_pos - dist
partner = Sselect(cond, up, down); // pick in-range counterpart
addr = ShardAddress(self, span, partner, b);
ReduceShardInPlace(local, recv, useBf16, b);
```text
> **注意 —** 发射侧选择是 `SandU32`/`SltS32`/`SaddS32`/`SsubS32`/`Sselect`(发射到程序中的标量 VPU 操作);构建端选择是主机端 `cmovl`。它们计算相同的函数 - 发射端形式的存在是因为二项式路径*可以*还从实时 `position_in_ring` 寄存器重新计算伙伴,而表路径则预先计算它。在二项式数据路径中,表获胜:`EmitInitialization` 安装已解析的 `CoreLocationBase` 向量,循环读取*物理*核心的 `counterparts[step]`(24 字节步长记录),而该算术解析用于分片地址的*逻辑*位置。
N 维环完全使用不同的伙伴计算 - 模环旋转,而不是蝴蝶:
```c
// BaseStrategyND::TorusStrideNPhasekNeighbor — 0x137c5960
color = dim_to_color[axis]; // *(this+0xd0+axis*0x18)
ring_pos= ring_position[color]; // *(this+0x160+color*8)
offset = stride * step;
partner = ModuloRingSize(ring_pos + offset, ring_size[color]); // 0x137c61a0ModuloRingSize (0x137c61a0) 是无分支单步换行,在 [−N, 2N): if (x<0) x+=N; if (x>=N) x-=N 上有效。网格变体(MeshStrideNPhasekNeighbor、0x137c5cc0)用夹具/反射代替了包裹,因为网格没有包裹边缘——这是环面和网格环系列之间唯一的结构差异。
二项式副本表
蝶式调度不会在每次发射时重新计算;每个程序都会将其预先计算为常量。 CreateStaticBinomialReplicaInfoTable (0x1375efa0) 构建一个由 (rank × 8 + step) 索引的 int32[N × 8] 数组。每个rank行的第0列是该rank自己的逻辑位置;列 1 .. log₂(N) 是该排名的蝴蝶伙伴在每一步的全局设备 ID(上面的 $_2 编写器填充该行,$_5 解析器映射 partner_pos → device-id)。
column: 0 1 2 3 ... 7
+--------+------------+------------+--------+ +--------+
rank 0 | self=0 | partner@±1 | partner@±2 | ... | | unused |
rank 1 | self=1 | partner@±1 | partner@±2 | ... | | unused |
... | | | | | | |
rank N-1 | self | partner@±1 | partner@±2 | ... | | unused |
+--------+------------+------------+--------+ +--------+
8 columns per rank (kBinomialCounterpartsCount); rows 1..log2(N) used
```text
该表是平面星形屏障表的**结构逆**:屏障表是 `int32[device]` 存储组内*序数*(一列,仅成员资格);二项式表是 `int32[rank × 8 + step]`,存储*合作伙伴设备 ID*(8 列,预先计算的蝶形时间表)。它们存在于不同的翻译单元中,被键入不同的常量注册表中,并提供不同的执行器。
| 方面 | 平板障碍桌 | 二项式表 |
|---|---|---|
| 生成器 | `CreateStaticReplicaInfoTable` (`net_util.cc`) | `CreateStaticBinomialReplicaInfoTable` `0x1375efa0` (`ring_sum_emitter.cc`) |
| 宽度 | `N` 条目(1 列) | `N × 8` 条目(8 列) |
| 指数 | 扁平化 device_id | `rank × 8 + step` |
| 储值 | 组内序号 `k` | 合作伙伴设备 ID (col0 = self) |
| 合作伙伴数学 | 无(会员) | `my_pos ± (1<<step)`,无枝包裹 → `$_5` |
| 活力门 | 无 | `popcnt(N)==1 && (bsr(N)^7)<8` |
| 恒定载体 | ProgramSharedRegistry 关闭 | ConstantMapper **类型 7** → `xla::Literal` R1 int |
| 运行时读取 | `GetReplicaGroupCoreInfo` | `LoadBinomialReplicaInfoTable` |
| 执行器 | 平星护栏 | 二项式蝶形发射器 |
### 构建 → 实现 → 注册
1. **构建** - `CreateStaticBinomialReplicaInfoTable` (`0x1375efa0`) 分配 `N×8` `int32` (`_Znwm(N<<5)` = `N×32` 字节),memsets 0,运行每组/每成员`$_2`作家。
2. **Materialise** - `CreateBinomialReplicaInfoTable` (`0x1375ee40`) 通过 `LiteralUtil::CreateR1<int>` 将数组包装到 `xla::Literal` R1 int 中并释放划痕。
3. **注册** — `AllReduceEmitter::GenerateConstants` (`0x1373cb60`) 调用 `ConstantMapper::AddConstant(Type=7, …)`。水母 ConstantMapper 枚举的类型 7 *是*二项式副本表。
### 每核读取(`LoadBinomialReplicaInfoTable`、`0x1375fca0`)
```c
// LoadBinomialReplicaInfoTable — 0x1375fca0 (decompiled, simplified)
replica_id = net_util::GetReplicaId(b); // current core's replica id (SMEM)
index = SaddS32(SmulU32(replica_id, SimmS32(stride=8)), col_offset); // rank*8 + col
LoadInfoTable(b, index, /*count=*/8, table); // load this rank's 8-entry row
for (step = 1; step < 8; ++step)
counterparts[step] = FromGlobalCoreId(loaded[step]); // -> CoreLocationBase[] (0x18 stride)
return BinomialGroupData{ shard_span, counterparts };每个核心仅读取自己的行(偏移量 replica_id × 8)并将其 8 列蝶式调度恢复为 CoreLocationBase[] 向量。 EmitInitialization (0x137699c0) 将该向量存储在发射器上({ptr, count, cap} 三重、24 字节步长记录),并且步骤循环直接索引 counterparts[step] — 无需对物理核心进行发射时间蝶形重新计算。反编译确认使用文字计数 8(0x1375fca0 第 128/242 行)调用 LoadInfoTable,并且解析器循环边界位于 step < 8。
注意 — 跨模块路径被门控:如果设置了
module()->[0xfa3](跨模块)并且module()->[0xe1e](use_physical_core_ids) 不是,则加载程序会因"!is_cross_module || use_physical_ids"(ring_sum_emitter.cc:1875) — 跨模块二项式环而致命需要物理核心 ID。$_5设备 ID 解析器 (0x13761260) 处理一维ReplicaGroup.replica_ids[idx]情况和二维DeviceAssignment多维展平(idiv/div由partition_count分割,然后是多项式)压平);该展平的精确二维暗淡排序是未固定的残差(低)。
还原和电线 — 与家人共享
每个蝶形步骤都以与环和钳系列相同的 VPU 合并和相同的线 DMA 结束:
RingSumEmitter::ShardAddress(0x1375d3a0) —CHECK(span.memory_space == VMEM)然后span_base + shard_index × ChunkBytes。分片索引是二项式路径上的蝴蝶位,环形路径上的旋转块。RingSumEmitter::ReduceShardInPlace(0x1375d460) — 每个元素的合并闭包fmerge_(vtable+0xd0)。当 dtype 为 BF16 并且NotUseBf16ArithInReduction成立时(TPU gen < 4 或超精度 env 标志),它执行VunpackF32→fmerge_×2 →VpackBf16(bf16-accumulate-in-f32);否则它会本地运行fmerge_。EnqueueDmaInGranules→DMA_TYPE_REMOTE_WRITE_UNICAST,后跟DmaDoneInGranules注释为"shard-recv-wait";二项式发射器保留 7 个接收同步标志(receive_sync_flags_needed()=7、0x1376a680)。
每个步骤目标的远程标志写入地址由 EncodeRemoteSyncFlagAddress (0x1d54da40) 计算:它验证操作数是否在 MemorySpace::kSflag 中,将对等方的逻辑芯片 ID 映射到物理芯片 (MapLogicalToPhysicalChipId、0x1d519f40),然后通过TpuVersion(Target+0x398 gen 字段)通过 FunctionRegistry<TpuVersion,…> 到每个代号编码器。 Jellyfish/Dragonfish 编码器 JfDf (0x1d5aa620) 是字节确认的:
addr = sflag
| (peer.chip_x << 0x14) // chip X coord, bit 20
| (phys_chip_id << 0x15) // physical chip, bit 21
| 0x40000 // bit 18 fixed remote marker
| (DefaultSyncFlagSegmentId() << 0xc) // 0x40 << 12 = segment, bits 12..17
| (multicast ? 0x80000 : 0) // bit 19 multicast/atomic target
```text
完整的握手、描述符格式和每代编码器表记录在 [ICI AllReduce 原语](../ici/all-reduce-primitive.md) 和 [路由概述](../routing/overview.md) 中;本页仅指出二项式路径使用相同的原语 - 时间表是唯一的区别。
> **注意 —** 钳子系列消耗一组*单独的*保留的 AllReduce sflag 插槽 (`GetAllReduceSyncFlagNumber`) 和平面 `net_util::GetRingLocation` 调度;完整的 `.text` 交叉引用显示 `GetAllReduceSyncFlagNumber` 的唯一调用者是 `AsyncPincerInstance::InitSflags` 和 `RotatedPincer*::InitSyncFlags` - 二项式发射器中**无**。二项式表*仅*通过 `BinomialGroupData` 提供程序读取到二项式发射器中。这是三拓扑分割:二项式蝶形(表+7个recv标志)、环旋转(`GetRingLocation`,无表)、钳形(`GetRingLocation`+保留槽)。
---
## 钳融合相对于蝴蝶的位置
AllReduce 钳式融合(reduce-arm/broadcast-arm fusion)是与二项式蝴蝶“不同”的发射器,这种区别对于重新实现者选择在何处融合周围计算很重要。二项式蝴蝶是**自完成**:没有可分离的reduce-scatter臂来融合生产者,也没有可分离的全收集臂来融合消费者——每一步都发送和减少,并且在`log₂(N)`步骤之后,结果已经广播到所有核心。相比之下,钳形融合将减少分散和全聚集的两半暴露为不同的可融合臂。
| | 二项式蝶形 | 钳融合 |
|---|---|---|
| 发射器 | `BinomialSinglePhaseRingSumEmitter` `0x13769be0` | `RotatedPincerFusionEmitter`(`EmitAllReduceScatterFusion`、`EmitAllGatherFusion`) |
| 减缩臂/广播臂 | 融合为一次遍历(不可分离) | 可分离 — RS 臂和 AG 臂暴露用于开窗 |
| 时间表来源 | 二项式副本表 (`Type 7`) | `net_util::GetRingLocation` |
| 标志 | 7 通用接收标志 | 保留AllReduce插槽(`GetAllReduceSyncFlagNumber`) |
| 方向/步骤 | 一只(蝴蝶伙伴) | 二(顺时针旋转+逆时针反向旋转) |
| 适合时 | 小型 2 次幂环,延迟受限 | 大环、带宽限制、窗口 einsum 重叠 |
钳子在每步*两个*方向上运行带宽最优系列的环旋转(每个方向覆盖环的一半,`⌈N/2⌉` 每个方向步进),具有重叠的发送/接收窗口,并且其每步簿记是一个 2-D `[dim][color]` 标志表 - 双向性的确凿证据,因为它为旋转和旋转保留单独的标志。反向旋转的碎片。减少臂/广播臂的分割使得周围的 matmul 块可以被窗口化到*集合中(windowed-einsum)。完整的钳环及其融合臂记录在 [分层 AllReduce / Pincer](allreduce-hierarchical-pincer.md) 中;跨两个 TC 核心共享每个臂的超级核心拆分位于 [巨核融合](megacore-fusion.md) 中。
> **注意 —** 拾取器在这些之间的选择不是质量旋钮 - 它是正确性和性能的叉子。二项式*仅*对于 2 的幂 `N ≤ 128`(生存门)有效;对于所有其他形状,以及对于带宽主导延迟的任何情况,选择器都会发出铃声或钳子声。将生产者融合到“二项式归约分散臂”中的重新实现者误读了拓扑:该臂不存在 - 蝴蝶没有可分离的分散阶段。
---
## 重新实施清单
- 拒绝二项式路径,除非 `popcnt(N)==1 && bsr(N) ≤ 7` (`N ∈ {2..128}`) 和 env-flag 门通过。
- 将每等级计划预先计算到 `int32[N×8]`、`table[rank×8+0]=rank`、`table[rank×8+1+step]=device_id(my_pos ± (1<<step))` 中,并强制执行 `0 ≤ partner < N`。
- 在运行时,读取`table[replica_id×8 .. +8)`,将通过`FromGlobalCoreId`的每个条目解析为`CoreLocationBase[]`向量,遍历`step ∈ [0, log₂(N))`。
- 每一步:`EnqueueDma(REMOTE_WRITE_UNICAST)` 到 `counterparts[step]`,等待 `shard-recv-wait`,然后是 `ReduceShardInPlace`(第 4 代上的 bf16-in-f32)。没有全聚集阶段——蝴蝶自我完成。
- 保留7个接收同步标志位;当屏障范围为 `kAll` 时,发出可选的预循环树屏障。
- 运行时逻辑位置选择 (`SandU32`/`SltS32`/`Sselect`) 必须逐位等于构建时 `cmovl` 选择,否则分片地址和物理伙伴不一致。
---
## 交叉引用
- [集体概述](overview.md) — 策略选择器和系列分类。
- [SelectND策略](strategy-nd-picker.md) — 选择二项式、环式、钳式(以及降级轴处理)的拾取器。
- [SPMD 链路计数成本](spmd-link-count-cost.md) — 选择器将延迟与带宽成本降至最低。
- [分层 AllReduce / Pincer](allreduce-hierarchical-pincer.md) — 双向钳形环形状(第三步生成形式)。
- [巨核融合](megacore-fusion.md) — 缩减臂/广播臂如何跨两个 TC 内核进行融合。
- [ICI 全归约原语](../ici/all-reduce-primitive.md) — 共享步骤生成原语:`ReduceShardInPlace`、`EnqueueDmaInGranules`、sflag 握手。
- [路由概述](../routing/overview.md) — 对等点 `CoreLocationBase` 如何成为 ICI 可路由的远程地址。