跨 Host Barrier
本页所有地址均适用于来自
libtpu-0.0.40-cp314wheel 的libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d,x86-64,781,691,048 bytes)。其他版本会不同。
摘要
Megascale 跨 host barrier 是 DCN 级别(data-centre-network,host 间)的 rendezvous,多 slice XLA/TPU 作业中的每个 host 都通过它在一个命名点同步。它是一次单独的一元 gRPC 调用 — MegaScaleTransport.Barrier — 汇聚到一个 coordinator 进程;coordinator 统计到达者,并一次性释放所有调用方。这是 host-network barrier;它与片上 SFLAG barrier(Barriers 概览)以及跨 core ICI tree-barrier 是不同机制、位于不同 fabric 范围。那些 barrier 通过 TensorCore sequencer 在 chip / slice 内部运行;本 barrier 通过 gRPC/protobuf 在 host 之间运行。本页内容都不触及 SFLAG — 这里的等待原语是一个 absl::Notification 加上一组 gRPC reply callback vector。
形态是 星形、集中式、一次往返:每个 host 向唯一的 coordinator 端点发送一个 Barrier RPC;coordinator 将每个 (slice_id, host_id) 的到达累计进 flat_hash_set,直到 set 大小等于声明的 participant count,然后向每个排队调用方广播一个缓存的 BarrierResponse。没有 peer-to-peer 阶段,没有 tree reduction,没有 butterfly,也没有 dissemination — 拓扑形状与它共享基类的 topology-discovery rendezvous 完全相同。server 对象 xla::megascale::runtime::BarrierCoordinator(vptr 0x21c9bb70,0xf8 = 248 bytes)是通用 Coordinator<Req, Resp, CB> 模板的第二个特化;该模板也产生 TopologyCoordinator。它继承了该基类的状态机、pending-callback fan、Notification 和周期性 status alarm(Bootstrap 收敛)。
对于重新实现,契约是:
BarrierCoordinator对象 — 其0xf8布局、共享的Coordinator<>基类偏移、(slice_id, host_id)tuple set,以及 9 槽 vtable。BarrierRPC 线路格式 — 4 字段BarrierRequest、仅 id 的BarrierResponse,以及 server callback chainGrpcAsyncCbServiceImpl::Barrier → OnBarrierRequestReceived → AddRequest。- 集中式算法 — 按
barrier_id惰性分配 coordinator、到达记账、count/duplicate 验证、在seen.size() == num_participants_时提前触发,以及 callback fan-out 释放。 barrier_idkeying 和两种风格 — 以 id 为 key 的flat_hash_map、带 replay protection 的命名前执行 barrier,以及自动生成的__global-auto-<N>collective barrier。- 超时与失败 — coordinator 侧 没有 timeout;唯一边界是来自
FLAGS_tf_tpu_preexecution_barrier_timeout的每 RPC gRPC deadline(默认 30 s),带有 10 秒客户端重试循环,以及一条 "Unable to wait for all slices to connect" 析构日志。
| Service / method | /xla.megascale.runtime.MegaScaleTransport/Barrier(一元;6 个方法中的第 3 个) |
| Coordinator object | xla::megascale::runtime::BarrierCoordinator,vptr 0x21c9bb70,0xf8 = 248 bytes |
| Base class | xla::megascale::runtime::Coordinator<BarrierRequest, BarrierResponse, AnyInvocable<…>>(base vtable off_21C33918) |
| Coordinator map | flat_hash_map<string, unique_ptr<BarrierCoordinator>> @ CommunicationBackend +0x1b0 |
| Arrival store | flat_hash_set<tuple<int,int>>(slice_id,host_id)@ BarrierCoordinator +0xd8 |
| Server entry | GrpcAsyncCbServiceImpl::Barrier 0x1ce74280 → OnBarrierRequestReceived 0x1ccac5c0 → AddRequest 0x1ccb42a0 |
| Client entry | Communicator::Barrier(id, opt<int>, opt<Duration>) 0x1cca8ee0 → GetBarrierResponse 0x1ce79cc0 → SendRPC 0x1ce79de0 |
| Timeout flag | FLAGS_tf_tpu_preexecution_barrier_timeout 0x22256500,absl::Duration 默认 30 s |
| Source TU | platforms/xla/megascale/runtime/communication/{topology_coordinator,communication_backend,grpc_transport}.{h,cc} |
注意 — 嵌入的源码字符串即使在 barrier 路径上也把 coordinator 逻辑命名为
topology_coordinator.h/.cc,因为BarrierCoordinator和TopologyCoordinator共享同一个模板。下文引用的topology_coordinator.cc:323这样的行号是位于该共享 TU 中的 barrier 代码,不是 topology 代码。
1. Barrier RPC
目的
MegaScaleTransport.Barrier 是跨 host barrier 的单一线路操作。它是一个阻塞的一元 gRPC 调用:host 发起它,coordinator 保持回复打开(不调用其 finish callback),直到 barrier 完成,然后结束它。从调用方视角看,RPC 只是阻塞到 rendezvous 释放。
线路格式
请求通过 BarrierRequest::Clear 0x1cf7f220 构建和解析(has-bits 位于 C++ +0x10);响应通过 BarrierResponse::Clear 0x1cf7f760 / MergeImpl 0x1cf7f660。
| 字段 # | 名称 | 类型 | C++ 偏移 | 含义 |
|---|---|---|---|---|
| 1 | barrier_id | string | +0x18 | rendezvous key;ArenaStringPtr(低位带 tag,mask & ~3) |
| 2 | slice_id | int32 | +0x20 | (slice, host) 到达 tuple 的低半部分 |
| 3 | host_id | int32 | +0x24 | 到达 tuple 的高半部分 |
| 4 | num_participants | int32 | +0x28 | 预期到达数,会与 coordinator 存储的 count 校验 |
响应只携带 barrier_id(字段 1,+0x18),由 CreateResponse 回显。C++ 对象在 +0x20… 有 int 字段(has-bit 位于 +0x10),镜像请求形状,但 coordinator 从不写入它们 — 因此响应的字段 2-4 线路编号是根据请求对称性推断的,而不是观察到被设置(LOW)。
注意 — 字段 4 是
num_participants,不是 timeout。ProcessRequest读取*((int*)req + 10)(+0x28处的 proto 字段),并将其与存储的num_participants_比较;不匹配会以INVALID_ARGUMENT拒绝。没有 timeout proto 字段 — barrier timeout 完全通过带外的 gRPC client deadline 携带(见 §5)。
Server Callback Chain
GrpcAsyncCbServiceImpl::Barrier(ctx, req, resp) 0x1ce74280
│ VLog(3) "calling barrier_callback_ with request: <req>" (grpc_transport.cc:1325)
│ wrap the gRPC reactor-finish closure as
│ AnyInvocable<void(StatusOr<BarrierResponse> const&)> (writes resp, Finish()es)
└─ barrier_callback_(req, cb) [transport +80/+104]
└─ CommunicationBackend::OnBarrierRequestReceived 0x1ccac5c0
└─ BarrierCoordinator::AddRequest(req, cb) 0x1ccb42a0 [vtable +0x10]
```text
server 侧在 `MegaScaleTransport` callback-service chain 中使用 `WithCallbackMethod_Barrier`;client 侧通过 pooled channel 派发 `MegaScaleTransport::Stub::Barrier` `0x1ce9a7c0`。
---
## 2. BarrierCoordinator 对象
### 目的
coordinator 进程上的每个 live `barrier_id` 都对应一个 `BarrierCoordinator`。它累计到达者,并在最后一个预期到达者到达时释放所有等待中的调用方。它是 topology coordinator 的 sibling — 相同的 `Coordinator<>` 基类,但使用 `(slice, host)`-tuple counter,而不是每个 slice 的 host map。
### 对象布局(`0xf8` = 248 bytes)
在 `OnBarrierRequestReceived`(`0x1ccac5c0`)中通过 `operator new(0xf8)` 分配。偏移由 ctor `0x1ccb3fa0` 加上 `ProcessRequest` / `IsComplete` / dtor 中的成员访问确认。
| 偏移 | 大小 | 字段 |
|---|---|---|
| `+0x00` | 8 | vptr(`0x21c9bb70`) |
| `+0x08`–`+0x57` | 0x50 | 嵌入的 `Coordinator<>` 基类:`TracedMutex(kind=9)` |
| `+0x58` | 1 | `state_`(0 init / 1 ready / 2 completed / 3 error) |
| `+0x60`–`+0x87` | 0x28 | `StatusOr<BarrierResponse>`(init = `UNAVAILABLE` "IN_PROGRESS") |
| `+0x88` | 8 | `response_setters_.data` — `vector<AnyInvocable<void(StatusOr<BarrierResponse> const&)>>` |
| `+0x90` / `+0x98` | 8 / 8 | `response_setters_.size` / `.capacity` |
| `+0xa0`–`+0xbf` | 0x20 | `absl::Notification` |
| `+0xb0` | 8 | status-report cancellable alarm handle |
| `+0xb8`–`+0xcf` | 0x18 | `std::string barrier_id`(SSO;cap byte 位于 `+0xcf`) |
| `+0xd0` | 4 | `num_participants_`(`topology_coordinator.h:299` 处 CHECK `> 0`) |
| `+0xd8`–`+0xf7` | 0x20 | `flat_hash_set<tuple<int,int>> seen_workers`(size = `*(this+0xe0) >> 17`) |
基类偏移(`+0x58` `state_`、`+0x60` `StatusOr`、`+0x88` callbacks、`+0xa0` `Notification`、`+0xb0` alarm)与 `TopologyCoordinator` 完全共享;只有派生类尾部(`+0xb8` 之后)不同。
### 算法 — 构造
```c
// BarrierCoordinator::BarrierCoordinator(string_view id, int num_participants) 0x1ccb3fa0
function ctor(this, id, num_participants):
this.vptr = &off_21C33918 // base vtable first
TracedMutex_ctor(this + 0x08, /*kind*/ 9) // embedded mutex
this.state_ = 0 // +0x58
this.StatusOr_ = MakeErrorImpl<14>("Coordinator in IN_PROGRESS") // +0x60, cat 14 = UNAVAILABLE
zero(this + 0x88, 0x20) // callbacks vector + Notification head
this.alarm_ = 0 // +0xb0 (within zeroed range)
this.vptr = &off_21C9BB70 // overwrite with derived vtable
store_sso_string(this + 0xb8, id) // barrier_id (heap if len > 0x16)
this.num_participants_ = num_participants // +0xd0
this.seen_workers_ = empty flat_hash_set // +0xd8 (+0xd8 = 1, growth-info word @ +0xe0 = 0)
CHECK(num_participants > 0) @ topology_coordinator.h:299 // FATAL otherwise两次 vtable 写入(先 base 后 derived)是构造期间的标准 C++ vtable 转换;反编译显示先写入 off_21C33918,然后在基类成员初始化后写入 off_21C9BB70。
Vtable(vptr 0x21c9bb70,9 槽)
由 .data.rel.ro 的 R_X86_64_RELATIVE relocation 解析得出。
| 槽 | VA | 方法 |
|---|---|---|
+0x00 | 0x1cf55760 | ~BarrierCoordinator(D2/D1) |
+0x08 | 0x1cf55960 | ~BarrierCoordinator(D0,deleting) |
+0x10 | 0x1ccb42a0 | Coordinator<Barrier>::AddRequest(req, cb)(base) |
+0x18 | 0x1ccb4a80 | Coordinator<Barrier>::GetState() const(base) |
+0x20 | 0x1cf54e60 | ProcessRequest(req) — 到达记账 |
+0x28 | 0x1cf559a0 | IsComplete() const |
+0x30 | 0x213b7e20 | CreateResponse() |
+0x38 | 0x213b7ce0 | ReportStatus() const |
+0x40 | 0x22048600 | base accessor(GetNumWorkers-style;不在 barrier hot path 上) |
注意 —
AddRequest(+0x10)和GetState(+0x18)是与TopologyCoordinator共享的 base-class entry;ProcessRequest/IsComplete/CreateResponse/ReportStatus(+0x20…+0x38)是赋予基类 barrier-specific 行为的 derived override。基类通过 vtable 调用它们 — 这是 template-method pattern。
3. barrier_id Keying
目的
每个 barrier_id 都是一个独立的 rendezvous。coordinator 进程拥有一个 live barrier map,第一次看到某个 id 时惰性创建一个 BarrierCoordinator,并将该 id 的所有后续到达路由到同一个对象。
算法 — Lazy Insert
// CommunicationBackend::OnBarrierRequestReceived(req, reply_cb) 0x1ccac5c0
function OnBarrierRequestReceived(backend, req, reply_cb):
VLog(3) "Received barrier request from (SliceId, HostId, barrier_id) <s>,<h>,<id>" // :954
lock(backend.mutex_) // +0xe0
slot = backend.barrier_map_[req.barrier_id] // flat_hash_map @ +0x1b0
if slot == null: // first sight of this id
slot = new(0xf8) BarrierCoordinator(req.barrier_id, req.num_participants)
cb = move(reply_cb)
slot->AddRequest(req, cb) // vtable +0x10
```text
*ctor* 的 participant count 取自 **第一个** 请求的字段 4;每个后续请求的字段 4 都会在 `ProcessRequest` 中校验为相等([§4](#4-arrival-accounting-and-release))。
> **陷阱 —** 这里没有 **coordinator-only** guard。如果命中非 coordinator 进程,topology discovery 会以 "TopologyCoordinator not initialized." 拒绝;barrier 路径没有这种检查 — *任何* 持有 barrier map 的进程都可以托管 `BarrierCoordinator`。假设只有选出的 coordinator 会分配 `BarrierCoordinator` 的重新实现会发生偏离;门控纯粹是调用方发送到 coordinator 的端点,而不是 server-side identity check。
在 `CommunicationBackend` 中,barrier map 位于 `+0x1b0`(`operator[](backend+432, id)`,通过 `operator new(0xf8)` = 248 bytes 分配每个 coordinator);相邻的 `TopologyCoordinator*` 位于 `+0x1a0`,`ErrorReporter*` 位于 `+0x1a8`,auto-barrier counter 位于 `+0x1d0`。
### 两种风格,一个机制
两种风格都通过相同的 3 参数 `Communicator::Barrier`(`0x1cca8ee0`)和相同的 server-side `BarrierCoordinator`。只有 id 生成和 replay 语义不同。
| 方面 | Pre-execution barrier | Collective-rendezvous barrier |
|---|---|---|
| 调用方 | `Communicator::Barrier(id, n, timeout)` | `Communicator::Barrier()`(无参数)`0x1cca8de0` |
| `barrier_id` | 显式,由 executor 提供 | 自动 `"__global-auto-<N>"`(counter @ backend `+0x1d0`) |
| Replay protection | 每进程 once-per-process used-id set(`ALREADY_EXISTS`) | 从不碰撞 — 每次调用 fresh id |
| `num_participants` | 调用方提供或默认 `NumHosts()` | 默认 `NumHosts()` |
| 驱动方 | `tpu_execute.cc`,由 `FLAGS_tf_tpu_enable_preexecution_barrier` 门控 | cross-host collective 开始处的 collective layer |
无参数形式在 backend mutex 下读取并递增 backend `+0x1d0` 处的单调 counter,构建 `StrCat("__global-auto-", N)`,然后以未设置的 `num_participants`/`timeout` 委托给 3 参数形式。由于每个 collective 都生成 fresh id,collective 从不会触发 replay protection。
命名形式首先将 `barrier_id` 插入位于 `Communicator +0xa8` 的每进程 used-id set;重复使用会在发送任何 RPC 之前返回 `MakeErrorImpl<6>`(`ALREADY_EXISTS`)"Barrier ID: $0 has already been used."(`communication_backend.cc:379`)。
> **特性 —** 默认 participant count 是 **整个作业**。当调用方省略 `num_participants` 时,它默认取 `MultiSliceTopologyAndLocation::NumHosts()` — 在 bootstrap topology exchange 期间解析出的总 host 数([Bootstrap 收敛](bootstrap/convergence.md))。因此未参数化的 barrier 只有在 multi-slice 作业中的 *每个* host 都到达时才会完成。机制支持任意 count(例如 sub-group barrier),但在此二进制中未定位到传入非默认 count 的调用方(LOW 表示 sub-group barrier 在实践中被使用)。
---
## 4. 到达记账与释放 {#4-arrival-accounting-and-release}
### 目的
coordinator 必须对每个不同 host 精确计数一次,拒绝错误声明的 count 和重复到达,在最后一个预期 host 到达的瞬间触发,并向所有等待者广播一个响应。这四件事都发生在 `ProcessRequest` 和基类 `AddRequest` 内。
### 算法 — ProcessRequest(验证 + 计数)
```c
// BarrierCoordinator::ProcessRequest(req) 0x1cf54e60 [vtable +0x20]
function ProcessRequest(this, req):
if req.num_participants (+0x28) != this.num_participants_ (+0xd0):
return INVALID_ARGUMENT,
"Mismatched number of barrier participants: Expected: $0 Msg: $1" // :323
key = tuple<int,int>(req.slice_id (+0x20), req.host_id (+0x24))
if this.seen_workers_.find(key): // flat_hash_set @ +0xd8
return INVALID_ARGUMENT,
"Extra barrier participant. Expected: $0 Message $1" // :331
this.seen_workers_.insert(key) // find_or_prepare_insert_large
return OK该 set 是 flat_hash_set<tuple<int,int>>,不是 "slice:host" 字符串 set。每个 unique host 只计数一次,因此由 10 秒重试循环(§5)重新发出的 host RPC 不会重复计数 — 它的第二次到达会命中 find 并被作为 duplicate 拒绝,随后基类路径会将其折叠进缓存响应。
算法 — IsComplete(提前触发)
// BarrierCoordinator::IsComplete() const 0x1cf559a0 [vtable +0x28]
function IsComplete(this):
return (*(uint64*)(this + 0xe0) >> 17) == this.num_participants_ // +0xd0
```text
`>> 17` 从 `+0xe0` 处的 control-block growth-info word 提取 SwissTable element count。完成条件在 `seen.size() == num_participants_` 的瞬间触发 — 即最后一个预期 host 到达时,与 topology coordinator 的 “all slots seen” early-fire 相同。
### 算法 — AddRequest(状态机 + 释放)
```c
// Coordinator<Barrier>::AddRequest(req, cb) 0x1ccb42a0 [vtable +0x10, base]
function AddRequest(this, req, cb):
VLog(5) "AddRequest: <req>" // topology_coordinator.h:75
lock(this.mutex_) // +0x08, TracedReleasableMutexLock
if this.state_ == 3: // poisoned (sticky error)
cb(this.StatusOr_); return // +0x60
rsp = this.vtable[+0x20](req) // ProcessRequest -> OK or INVALID_ARGUMENT
if this.state_ == 2: // already completed
cb(per-request response/error); return // serve cached, fast path
push cb onto this.response_setters_ (+0x88)
if rsp is error:
this.state_ = 3; this.StatusOr_ = rsp; serve rsp // poison the id
if this.vtable[+0x28](): // IsComplete()
this.state_ = 2
this.StatusOr_ = this.vtable[+0x30]() // CreateResponse()
this.Notification_.Notify() // +0xa0
VLog(5) "Num responses to send: <n>" // h:131
for cb_i in response_setters_: cb_i(this.StatusOr_); VLog(5) "Response sent <i>" // h:135
clear(response_setters_)
else if this.state_ == 0:
this.state_ = 1; ScheduleStatusReport() // arm 1-second alarm @ +0xb0
VLog(5) "Done processing Request received at: <t> Duration: <d>" // h:141
if duration > ~5 s: LOG "Long running topology coordinator AddRequest: Duration: <d>" // h:144释放机制
完成时(在 mutex 下):state_ = 2;StatusOr_ := CreateResponse()(带有从 +0xb8 回显的 barrier_id 的 BarrierResponse,并在 topology_coordinator.cc:364 记录 "MegaScale Barrier completed for id <id>");Notification.Notify();然后迭代 response_setters_,用缓存响应调用每个排队的 AnyInvocable<void(StatusOr<BarrierResponse> const&)>。每次调用都会结束对应的 gRPC 调用并解除远端 host 的 SendRPC 阻塞。gRPC framework 在其 callback 触发前不会向 client 发信号 — 因此 “release” 精确地就是这一轮单次 fan-out pass,之后 vector 被销毁。
+0xa0 处的 Notification 还会唤醒绕过 gRPC 的 进程内调用方(coordinator 自身的 host),并被析构函数的 settle 路径使用;对于远端 host,只有 callback fan 重要。这与 Bootstrap 收敛 中描述的 dual signalling 相同。
5. Timeout
目的
barrier 必须限制 host 等待其他 host 的时长。该限制完全位于 client 侧:每 RPC gRPC deadline 加上重试循环。coordinator 自身不施加 timeout。
Flags
| Flag (VA) | 类型 | 默认值 | 影响 |
|---|---|---|---|
FLAGS_tf_tpu_enable_preexecution_barrier 0x222564a0 | bool | (gate) | executor 中 pre-execution barrier 的 master switch |
FLAGS_tf_tpu_preexecution_barrier_timeout 0x22256500 | absl::Duration | 30 s | 应用于 Barrier RPC 的 client gRPC deadline |
FLAGS_xla_tpu_enable_megascale_barrier 0x223b4d08 | bool | (gate) | megascale barrier 路径的更高层 enable |
30 秒默认值由 AbslFlagDefaultGenFortf_tpu_preexecution_barrier_timeout::Gen 0xe6fd5c0 写入为 absl::Duration {sec=30, ns=0}(*this = 30; *(this+2) = 0)。timeout 和 enable flag 都在 learning/45eac/tfrt/tf_tpu/tpu_execute.cc(TFRT TPU executor)中注册,确认 pre-execution barrier 由每次 launch 的 execute 路径驱动。
算法 — Client Deadline + Retry Loop
// GrpcTransport::SendRPC<BarrierRequest, BarrierResponse>(...) 0x1ce79de0
function SendRPC(method, peer, req, opt_timeout):
deadline = opt_timeout ? Now() + opt_timeout : +inf (0x7fff..ff)
VLog(3) "Calling GetBarrierResponse with deadline: <t>" // grpc_transport.cc:2197
while Now() < deadline:
ctx = ClientContext(); ctx.set_deadline(Timepoint2Timespec(deadline))
stub = GetOrCreateClient(peer) // channel-pooled, round-robin
status, resp = IssueSyncRPC(stub, req) // Stub::Barrier (blocking unary) 0x1ce739c0
if status.ok():
VLog(3) "<method> succeeded." // :2221
return resp
LOG "<method> to <peer> returned with status: <s>. Sleeping for 10 seconds." // :2216
AbslInternalSleepFor(10 s) // retry
return last sticky error status // typically DEADLINE_EXCEEDED
```text
`IssueSyncRPC`(`0x1ce739c0`)在一组预打开的 stub 之间 round-robin(80-byte entry,`index = call_count % pool_size`,在 client mutex 下),并通过 `GrpcStatusToAbslStatus` 转换 gRPC status。使用 30 s budget 和 10 s retry sleep 时,调用方在 deadline 到期前大约会尝试三次。
> **陷阱 —** coordinator 的 progress path 上没有 `WaitForNotificationWithTimeout`。coordinator 永远不会按自己的时钟中止 barrier;未完成的 `BarrierCoordinator` 会保持 callback pending,并每秒重新记录进度([§6](#6-failure-handling))。*唯一*边界是每个调用方的 gRPC deadline。添加 server-side timeout 的重新实现不会匹配观察到的行为,后者是 coordinator 只是无限等待并记录日志,直到被拆除。
### Status Report Cadence
当 `AddRequest` 将 coordinator 从状态 0 → 1 时,它通过 `ScheduleStatusReport` lambda `0x1ccb4ea0` 在 `+0xb0` 处启动周期性 alarm:在 mutex 下调用 `ReportStatus`(vtable `+0x38`),如果仍未完成,则通过 `DefaultFiberExecutor` 上的 `thread::AddCancellableAt` 在 `Now() + 1 s` 重新 arm。
> **注意 —** re-arm 无条件是 `Now() + absl::Seconds(1)`,没有 backoff multiplier,因此节奏固定为 **1 秒**。虽然 [Bootstrap 收敛](bootstrap/convergence.md) 将通用 alarm 描述为 "seconds to a minute",但 barrier 的具体 re-arm 始终是 1 s。
`ReportStatus`(`0x213b7ce0`)在完成时记录 "MegaScale Barrier completed."(`:353`),否则记录 "MegaScale Barrier in progress. Seen <K> of <N> expected participants. Seen hosts: <list>"(`:356`)。host list 来自 `GetSeenHosts`(`0x1cf55280`),它将 `(slice, host)` set 压缩为每个 slice 的 `gtl::IntervalSet<int>` range — 例如 `slice0.hosts[0-3,5], slice1.hosts[0-7]`。
---
## 6. 失败处理 {#6-failure-handling}
### 算法 — 失败模式
| 失败 | 检测位置 | 结果 |
|---|---|---|
| 某个 host 永不到达 | client gRPC deadline(30 s) | 每个等待者的 RPC 返回 `DEADLINE_EXCEEDED`,其 callback 被取消;coordinator 保持 `state_=1`,每 1 s 重新记录 "in progress" |
| 未完成的 coordinator 被拆除 | derived dtor `~BarrierCoordinator` `0x1cf55760` | 记录 "BarrierCoordinator: Unable to wait for all slices to connect. Saw <K> of <N> expected participants. Seen hosts: <intervals>"(`:371`) |
| Participant-count 不匹配 | `ProcessRequest` `0x1cf54e60` | `INVALID_ARGUMENT`;`AddRequest` 设置 `state_=3`,poison 该 id,并把错误返回给此 id 的当前和所有未来调用方 |
| 重复到达(distinct host) | `ProcessRequest` | `INVALID_ARGUMENT` "Extra barrier participant";同样 poisoning |
| 命名 id 被重复使用 | `Communicator::Barrier` `0x1cca8ee0` | client-side `ALREADY_EXISTS`,在任何 RPC 之前 |
| 未设置 coordinator 端点 | `Communicator::Barrier` | `INTERNAL` "Barriers not available without coordinator set."(`:384`) |
| Transport 未初始化 | `Communicator::Barrier` | `INTERNAL` "Transport not initialized."(`:1473`,source-location stamped at `:929`) |
### “Unable to wait” 析构路径
derived destructor 检查 `seen.size() >= num_participants_`;如果不满足,它会发出 canonical "barrier failed" 日志 "BarrierCoordinator: Unable to wait for all slices to connect. Saw <K> of <N> expected participants. Seen hosts: <intervals>"(`topology_coordinator.cc:371`),取消 status-report alarm(对 `+0xb0` 调用 `thread::Cancel`),然后链接到 base destructor — 如果 `state_ == 2`,base destructor 会通过 `WaitForNotificationWithTimeout` settle 任意 in-flight notification。
### 传播与 Poisoning
失败的 barrier 向 executor 返回非 OK `absl::Status`,并通过 PJRT 暴露给 JAX。在 coordinator 进程上,周边 runtime 还可能通过 `MegaScaleTransport.ReportError → ErrorReporter → RapidEye`([ErrorAggregator](error-aggregator.md))路由失败,其中 barrier hang 通常分类为 `HANG_DETECTED` / `PROGRAM_NOT_QUEUED`。poisoned barrier(count/duplicate 不匹配后 `state_=3`)在该 `BarrierCoordinator` 的生命周期内保持 poisoned;作业重启会构造 fresh map 和 fresh coordinator。
> **特性 —** `BarrierRequest` **不**携带 `incarnation_id`(不同于有字段 5 的 `GetMultiSliceTopologyRequest`)。staleness 以结构方式处理:collective 每次调用都会生成 fresh `__global-auto-<N>` id,因此上一轮 launch 的 barrier 永远不会与当前轮混淆;命名 id 依赖 client-side once-per-process used-id set。barrier 自身没有跨进程 generation counter。
---
## 7. 与其他 Barrier 的关系
Megascale 跨 host barrier 是 libtpu 中三个不同 barrier 层级之一,每个层级位于不同 fabric 范围,并构建于不同原语之上。它们彼此独立 — 没有哪个叠加在另一个之上。
| 方面 | **本页 — DCN cross-host** | ICI cross-core tree | tpunetd in-slice |
|---|---|---|---|
| 范围 | cross-**host**(整个 multi-slice 作业,通过 DCN) | chip/slice 内 cross-**core**(通过 ICI) | in-**slice**(一个 slice 的 peer) |
| Transport | gRPC / protobuf | TensorCore sequencer Vsync ops on SFLAGs | gRPC(`TpuNetworkSessionBarrier`) |
| 拓扑 | 集中式星形(coordinator 上的 counter) | up-sweep/down-sweep routing-table tree | 分布式(master Notifies all,每个 peer WaitForReady) |
| 等待原语 | `absl::Notification` + gRPC callback fan-out | `VWaitGeOp` on an SFLAG | `CancellableCondVar` / `Mutex::AwaitCommon` |
| Keying | `(slice_id, host_id)` tuple set,count to N | reserved GLOBAL SFLAG `base+count+4` | 单个共享 `barrier_id` 字符串 |
| Timeout | gRPC deadline(`FLAGS_tf_tpu_preexecution_barrier_timeout`,30 s) | 无(hardware rendezvous) | `absl::Duration` arg |
| 实现 | `xla::megascale::runtime::BarrierCoordinator` | `net_util::BarrierCoresTree` `0x1c6a75c0` | `superpod::tpunetd_client::BroadcastBarrier` |
tpunetd `BroadcastBarrier::SyncWithTimeout`(`0x1ff9bce0`)确认 in-slice barrier 是通过 `CancellableCondVar` 发信号的 master-driven Notify/Wait pattern。Megascale barrier 从不调用它;依赖关系纯粹是时间上的 — tpunetd 的 in-slice rendezvous 必须在 Megascale runtime 构造 `CommunicationBackend` 之前完成(bootstrap [ICI handoff](bootstrap/convergence.md))。
---
## 相关组件
| 名称 | 关系 |
|---|---|
| `TopologyCoordinator` | sibling `Coordinator<>` specialisation;共享基类、状态机、`Notification` 和 alarm |
| `CommunicationBackend` | 拥有 barrier map(`+0x1b0`)、auto-id counter(`+0x1d0`),并派发 `OnBarrierRequestReceived` |
| `MegaScaleTransport` | 承载 `Barrier` RPC 的 gRPC service(6 个一元方法中的第 3 个) |
| `ErrorReporter` / RapidEye | barrier hang 的 downstream sink(`HANG_DETECTED` / `PROGRAM_NOT_QUEUED`) |
---
## 交叉引用
- [Megascale 概览](overview.md) — DCN vs ICI,以及 cross-host barrier 在作业启动和每次 launch 执行中的位置
- [Bootstrap 收敛](bootstrap/convergence.md) — 此 barrier 继承的通用 `Coordinator<>` 基类(状态机、callback fan、Notification、alarm);初始 topology barrier
- [ErrorAggregator](error-aggregator.md) — barrier failure 传播进入的 `ReportError → ErrorReporter → RapidEye` 路径
- [tpunetd 协议](tpunetd-protocol.md) — 其 `TpuNetworkSessionBarrier` 在此 barrier *之前*运行、且范围不同的 in-slice daemon protocol
- [Barriers 概览](../barrier/overview.md) — 片上 SFLAG barrier 模型;cross-host barrier 是一个从不触及 SFLAG 的独立层级
- [Tree-Barrier Vsync](../barrier/tree-barrier-vsync.md) — ICI cross-core up-sweep/down-sweep tree barrier;此 host-network barrier 的 on-pod analogue
- [Global-Barrier SFLAG Window](../barrier/global-barrier-window.md) — ICI tree barrier rendezvous 所用的 reserved GLOBAL SFLAG slot,与此 barrier 的 `Notification` 形成对比