Skip to content

StreamExecutor 平台与执行器模型

本页所有地址均适用于来自 libtpu-0.0.40-cp314 wheel 的 libtpu.so(构建 libtpu_lts_20260413_b_RC00,build-id md5 89edbbe81c5b328a958fe628a9f2207d,781,691,048 字节,ELF x86-64 DYN,未 strip;反混淆后的 C++ 符号按原文引用)。.text VMA 等于文件偏移。其他版本会有所不同。

摘要

stream_executor 是 XLA 所依赖的旧式设备抽象层,角色类似于 CUDA 程序中的 CUDA driver API:一组与设备无关的类族 — PlatformStreamExecutorStreamEventDeviceDescription — 供 XLA service 和 PjRtClient 枚举设备、创建逐设备执行器、分配设备内存并启动有序工作。它早于 PJRT 出现;PJRT 是分层构建在其之上的公共 C ABI(见 PJRT 概览)。本页负责说明平台注册握手与 SE 对象模型:TPU 和 host 后端如何把 Platform 注册到全局 registry,Platform 如何通过 ExecutorCache 为每个 ordinal 发放 StreamExecutor,以及具体的 host 侧 SE 对象(HostExecutorHostStream、host Event)在内存中实际是什么样子。本页有意重新记录逐执行 enqueue 路径,也不记录跨 stream 的 wait 模型 — 这些内容在其他页面中(见下面的范围说明)。

链接进来的后端有两个。TPU 后端tensorflow::tpu::TpuPlatform + stream_executor::tpu::TpuExecutor + TpuStream)是一个很薄的 C-ABI shim:每个 SE 调用都会通过单例 TfTpu_ExecutorApiFn 函数表(由 stream_executor::tpu::ExecutorApiFn() 解析)转发到 libtpu 的 TPU driver core,SE 对象只持有不透明的 SE_StreamExecutor* / SE_Stream* 句柄。host/CPU 后端stream_executor::host::HostPlatform + HostExecutor + HostStream)则是一个真实的二进制内实现,构建在 tsl::thread::ThreadPoolabsl::Notification 之上;它是同步变体(没有 worker queue),用于 host-memory staging 和简单 host ops。这里没有 InterpreterExecutor — XLA 的独立 interpreter device 没有被链接;host-interpreter 角色由 xla::HloEvaluator(编译期)和 CPU thunk runtime 填补,它们都不是 SE Platform

本页按自顶向下的顺序展开:registry(PlatformManager)、安装 TpuPlatform 的注册入口、PlatformExecutorCacheStreamExecutor 创建路径、三级类层次,最后是精确到字节的 host SE 对象模型(executor、stream、event)。每个 Platform/Executor 单元都使用相同的 ### 目的 / ### 入口点 / ### 算法 / ### 函数映射 词汇。

对于重新实现,契约是:

  • registry + 注册握手 — 一个进程全局的 PlatformManager 单例(匿名命名空间 Impl,由 __cxa_guard 构建),以及 RegisterTpuPlatform:它 new 一个 0x98 字节的 TpuPlatform,存入全局变量,注册它,并 CHECK 成功 — 由 once-flag 保护,并受 IsStreamExecutorEnabled 控制。
  • Platform::ExecutorForDeviceExecutorCache::GetOrCreate → factory 路径 — 逐 ordinal 的 StreamExecutor 如何缓存在受 absl::Mutex 保护的 FlatHashMap<int, unique_ptr<StreamExecutor>> 中,并且只在 cache miss 时构建。
  • 三级类层次StreamExecutor(抽象)→ StreamExecutorCommon(持有 const Platform*)→ {HostExecutor, TpuExecutor},包含 vtable 地址和对象大小。
  • host SE 对象模型HostExecutor(thread pool)、HostStream0x80 字节,派生自 StreamCommon,同步)以及 host Event(基于 absl::Notification,双对象拆分)的字节级布局。
Registrystream_executor::PlatformManager — 单例 Impl @ anon-ns,由 __cxa_guard 构建,0x48 字节(absl::Mutex + 两个 map)
注册入口(TPU)tensorflow::tpu::RegisterTpuPlatform @ 0xe99a3a0 → CHECK @ tpu_platform.cc:178
PlatformManager::RegisterPlatform0x1d0fe120 — CHECK platform != nullptr @ platform_manager.cc:93
TPU platform 对象tensorflow::tpu::TpuPlatformnew(0x98),全局 tpu_registered_platform
Host platform 对象stream_executor::host::HostPlatform(ctor 0xfe6d380
Executor cachestream_executor::ExecutorCache::GetOrCreate @ 0x1d0fd2e0FlatHashMap<int, unique_ptr<SE>> + absl::Mutex,log @ executor_cache.cc:44
TpuExecutornew(0x48),vtable off_21612AC8: StreamExecutorCommonSE_StreamExecutor* @ +0x38,ordinal @ +0x40
HostExecutorInit @ 0xfe6d780tsl::thread::ThreadPool("host-executor", NumSchedulableCPUs)
HostStreamctor 0xfe6ec80new(0x80),vtable off_217B0228: StreamCommon
Host EventCreateEvent @ 0xfe6d9e0new(0x18) wrapper(off_217B0138)+ new(0x28) event(off_215FC128Notification @ +24
证据等级重新实现级 / 已用 IDA 反编译按字节确认

范围 — 逐执行 enqueue(ExecuteAsyncOnStream / LoadProgramAndEnqueueToStream)由 ExecuteAsyncOnStreamLoadProgramAndEnqueueToStream 负责。跨 stream wait/dependency 模型(WaitFor / RecordEvent、FIFO 排序契约、compute/transfer stream 拆分)由 Stream 语义与依赖 负责。SE → PJRT bridge(TpuClient / CommonPjRt)由 StreamExecutor → PJRT 适配器 负责。本页只覆盖平台注册 + SE 对象模型层;它链接到那些页面,而不是复述它们。


1. Platform Registry — PlatformManager

目的

stream_executor::PlatformManager 是进程全局 registry,把平台名平台 id 映射到 Platform*。XLA 的设备枚举代码按名称/id 向它请求“TPU platform”或“host platform”,并取得能够创建 executors 的单例 Platform 对象。每个进程恰好有一个 PlatformManager,并且是惰性构造的。

说明 — 较老的 XLA 把这个类称作 MultiPlatformManager。此二进制中不存在 MultiPlatformManager 符号;上游 XLA 已将 MultiPlatformManagerPlatformManager,去掉了 Multi 前缀,本构建只包含新名称。重新实现者应以 stream_executor::PlatformManager 为准。

对象布局

registry 状态是单个匿名命名空间单例 — stream_executor::(anonymous namespace)::Impl()::impl — 一个在 __cxa_guard 下构建的 0x48 字节对象。每个 public PlatformManager static method 都会先 materialise 它。从 RegisterPlatform0x1d0fe120)和 PlatformWithName0x1d0fe5c0)读到的布局完全一致:

c
// Impl(): the PlatformManager singleton                       (anon-ns, __cxa_guard-built)
struct Impl {                       // operator new(0x48)
    absl::Mutex  mu;                // +0x00  guard (init {0, 1, 0})
    FlatHashMap  id_map;            // +0x10  PlatformId -> Platform*
    FlatHashMap  name_map;          // +0x28  name      -> Platform*   (XMM-zeroed at +0x28)
    // ...
};

// every accessor begins with:
function PlatformManager_AnyAccessor():
    if (!guard_for(impl)):                            // __cxa_guard_acquire
        impl = new Impl();                            // zero-init both maps under the guard
        guard_release();
    ...
```text

### 函数映射

| 函数 | 地址 | 角色 |
|---|---|---|
| `PlatformManager::RegisterPlatform` | `0x1d0fe120` | 安装一个 `unique_ptr<Platform>`;CHECK `platform != nullptr` @ `platform_manager.cc:93`;通过 `platform->vtable+24` 查询 id |
| `PlatformManager::PlatformWithName(string_view)` | `0x1d0fe5c0` | 名称查找 |
| `PlatformManager::PlatformWithName(string_view, bool)` | `0x1d0fe820` | 带 init flag 的名称查找 |
| `PlatformManager::PlatformWithId(const PlatformIdInfo*)` | `0x1d0fe680` | Id 查找 |
| `PlatformManager::PlatformsWithFilter(...)` | `0x1d0ff140` / `0x1d0ff160` | 过滤枚举 |
| `(anon)::PlatformManagerImpl::PlatformWithName` | `0x1d0fe8e0` | static wrappers 后面的真实查找主体 |
| `(anon)::PlatformManagerImpl::InitializedPlatformNames...` | `0x1d100260` | 枚举已初始化的平台(一个 `std::function` filter) |

> **注意 —** `RegisterPlatform` 在插入前通过 `(*platform)->vtable[+24]` 读取平台 id — id 是每个 `Platform` 的*虚拟*属性,而不是构造函数参数。TPU id 来自 `tensorflow::tpu::GetTpuPlatformId() @ 0x20818ec0`。重新实现必须把 id 暴露为 virtual,使 registry 能在不知道具体类型的情况下以它为 key。

---

## 2. 注册 TPU Platform

### 目的

`tensorflow::tpu::RegisterTpuPlatform()` 是构造单例 `TpuPlatform` 并将其安装到 registry 的一次性入口。它在 libtpu 初始化期间运行(由 TPU-driver bring-up 路径驱动,而不是由 PJRT 调用驱动)。返回之后,`PlatformManager::PlatformWithName("TPU")` 可以解析,PjRtClient 就能创建 executors。

### 算法

```c
// tensorflow::tpu::RegisterTpuPlatform()                      sub_E99A3A0
function RegisterTpuPlatform():
    apifn = ExecutorApiFn()                                    // resolve TfTpu_ExecutorApiFn singleton
    if (IsStreamExecutorEnabled(apifn)                         // SE backend must be enabled
        && !tpu_platform_registered):                          // function-local once-flag
        p = (TpuPlatform*)operator new(0x98)                   // 152-byte platform object
        TpuPlatform::TpuPlatform(p)                            // ctor  sub_E999960
        tpu_registered_platform = p                            // stash in a process global
        st = PlatformManager::RegisterPlatform(p)              // 0x1d0fe120  (moves the unique_ptr)
        if (st != OkStatus):                                   // st != 1
            LOG(FATAL) "stream_executor::PlatformManager::RegisterPlatform( std::move(platform)) is OK"
                       @ tpu_platform.cc:178                   // brings the process down
        tpu_platform_registered = 1
    return 1                                                    // always returns true

三个对重新实现至关重要的事实。(1) 注册IsStreamExecutorEnabled 控制 — 如果 SE backend 被禁用,platform 永远不会安装,该函数会作为 no-op 返回 true。(2) once-flag tpu_platform_registered 是函数局部 static,因此重复调用是幂等的。(3) RegisterPlatform 的成功在 tpu_platform.cc:178 处是 hard CHECK — 注册失败是 fatal,而不是可恢复错误。构造出的对象大小为 0x98(152)字节;全局 tpu_registered_platform 会由 TpuPlatform::GetRegisteredPlatform() @ 0xe999aa0 读回,该函数是一行 return tpu_registered_platform

TpuPlatform::Initialize

注册不同,Initialize0xe999ac0)在 platform 下 bring up TPU driver。它是纯 C-shim 转发:

c
// tensorflow::tpu::TpuPlatform::Initialize()                  sub_E999AC0
function TpuPlatform_Initialize(this):
    status_obj = ExecutorApiFn()[+360](this)                  // device/status scratch object
    ExecutorApiFn()[+16](this->se_platform/*this+1*/, status_obj)   // TfTpu init the platform
    if (!ExecutorApiFn()[+408](status_obj)):                  // status not OK?
        code = ExecutorApiFn()[+400](status_obj)              // status code
        msg  = ExecutorApiFn()[+392](status_obj)              // status message
        rep  = absl::Status::MakeRep(code, msg, ..., status_helper.h:38)
    ExecutorApiFn()[+384](status_obj)                          // free the status scratch
    return rep_or_Ok
```text

`+360 / +408 / +400 / +392 / +384` slot pattern — *取得 status scratch object,执行 op,查询 ok/code/message,释放 scratch* — 是通用的 “TfTpu C-shim → `absl::Status`” 习惯用法;每个 TPU SE method(executor `Init`、`Allocate`、stream `WaitFor`)都使用它,并且总是锚定在 `status_helper.h:38`。

### 函数映射

| 函数 | 地址 | 角色 |
|---|---|---|
| `tpu::RegisterTpuPlatform` | `0xe99a3a0` | 一次性构造 + 注册;once-flag;fatal CHECK @ `:178` |
| `tpu::GetTpuPlatformId` | `0x20818ec0` | registry 用作 key 的 TPU `PlatformId` |
| `TpuPlatform::TpuPlatform` (C2) | `0xe999960` | `0x98` 字节 platform 的 ctor |
| `TpuPlatform::Initialize` | `0xe999ac0` | 通过 `ExecutorApiFn()+16` 进行 driver bring-up;status @ `status_helper.h:38` |
| `TpuPlatform::GetRegisteredPlatform` | `0xe999aa0` | `return tpu_registered_platform` |
| `TpuPlatform::FindExisting(int)` | `0xe99a4a0` | 逐 ordinal 缓存 executor 查找(不构建) |
| `TpuPlatform::VisibleDeviceCount` | `0xe999d20` | 通过 shim(`ExecutorApiFn()+48`)取得设备数 |
| `TpuPlatform::Insert/Lookup/EraseEvent` | `0xe999fa0`/`0xe99a100`/`0xe99a160` | `Event*` ↔ `SE_Event*` registry(由 `WaitFor`/`RecordEvent` 使用) |
| `TpuPlatform::TpuMemoryLimit` | `0xe99a2c0` | HBM 大小查询 |
| `TpuPlatform::GetTopologyPtr` | `0xe999f40` | 设备 topology 句柄 |

> **注意 —** `TpuPlatform::Insert/Lookup/EraseEvent` 维护 `Event* → SE_Event*` map,TPU stream 的 `WaitFor(Event*)`/`RecordEvent` 会解引用该 map。该 map 的*使用方式*记录在 [Stream 语义](../runtime/stream-semantics.md);platform *拥有*这个 map,因此这些 method 位于 `TpuPlatform` 上。

---

## 3. 创建 Executors — `ExecutorForDevice` → `ExecutorCache`

### 目的

`Platform` 不会为每次请求创建新的 `StreamExecutor`;它按设备 ordinal 为每个设备缓存一个。`Platform::ExecutorForDevice(int ordinal)` 返回已缓存的 executor,或在首次使用时构建它;`GetUncachedExecutor` 是 cache 在 miss 时调用的 factory。这是 `StreamExecutor` 得以存在的唯一路径。

### 入口点

```text
TpuPlatform::ExecutorForDevice(int)            0xe999d40   ── public entry
  └─ ExecutorCache::GetOrCreate(cache@+40, ordinal, factory)   0x1d0fd2e0
       ├─ ExecutorCache::Get(ordinal)          0x1d0fd580   ── fast path: hit
       └─ factory()  ==  $_0 lambda  ─────────► TpuPlatform::GetUncachedExecutor(int)   0xe999da0
                                                  └─ ExecutorApiFn()+32  ── mint SE_StreamExecutor*
                                                  └─ new(0x48) TpuExecutor : StreamExecutorCommon

算法

ExecutorForDevice0xe999d40)把 GetUncachedExecutor 调用包装在 std::function factory 中,并把 cache(位于 platform+40)传给 GetOrCreate

c
// TpuPlatform::ExecutorForDevice(int ordinal)                 sub_E999D40
function TpuPlatform_ExecutorForDevice(this, ordinal):
    factory = bind(TpuPlatform::GetUncachedExecutor, this, ordinal)   // $_0 lambda
    ExecutorCache::GetOrCreate(this->cache /*this+40*/, ordinal, factory)   // 0x1d0fd2e0
    // returns StatusOr<StreamExecutor*>
```text

`ExecutorCache::GetOrCreate`(`0x1d0fd2e0`)是 cache 核心:

```c
// ExecutorCache::GetOrCreate(int ordinal, factory)            sub_1D0FD2E0
function ExecutorCache_GetOrCreate(cache, ordinal, factory):
    if (ExecutorCache::Get(ordinal) is OK):                    // 0x1d0fd580 — lock-free-ish hit
        return cached                                          // fast path
    VLOG(2) "building executor"  @ executor_cache.cc:44
    se = factory()                                             // run GetUncachedExecutor
    if (!se.ok()): return se.status()
    lock(cache->mu /*ordinal-keyed table mutex*/)
    table = FlatHashMap<int, unique_ptr<StreamExecutor>>(cache+8)
    slot  = table.find_or_prepare_insert(ordinal)              // double-check under lock
    if (slot existed):                                         // raced — keep the existing one
        unlock; drop our `se`; return existing
    table[ordinal] = move(se)                                  // install
    unlock
    return se

cache 是由 absl::Mutex 保护的 FlatHashMap<int, unique_ptr<StreamExecutor>>,并采用 double-checked insert:先运行 Get fast path,factory 在锁外构建,然后在锁下 find_or_prepare_insert,保留竞态中获胜的对象。executor_cache.cc:44 处的 VLOG "building executor" 在常见情况下每个 ordinal 只触发一次。

GetUncachedExecutor — factory

c
// TpuPlatform::GetUncachedExecutor(int ordinal)               sub_E999DA0
function TpuPlatform_GetUncachedExecutor(this, ordinal):
    status_obj = ExecutorApiFn()[+360](this)                  // status scratch
    se_handle  = ExecutorApiFn()[+32](this->se_platform, ordinal, status_obj)  // mint SE_StreamExecutor*
    if (ExecutorApiFn()[+408](status_obj)):                   // ok?
        exec = (TpuExecutor*)operator new(0x48)               // 72-byte executor
        StreamExecutorCommon::StreamExecutorCommon(exec, this/*Platform*/)
        exec->vtable      = off_21612AC8
        exec->platform    = this        // +0x30 (this+6)
        exec->se_handle   = se_handle   // +0x38 (this+7)  opaque SE_StreamExecutor*
        exec->ordinal     = ordinal     // +0x40 (this+16, 32-bit)
        return StatusOr::ok(exec)
    else:                                                      // mint failed
        return harvest_status(status_obj) @ status_helper.h:38
    // (status scratch freed via ExecutorApiFn()+384)
```text

`TpuExecutor` 是 `0x48`(72)字节对象:`StreamExecutorCommon` base(持有 `const Platform*`)、vtable `off_21612AC8`、位于 `+0x30` 的*第二份* `Platform*` 副本、位于 `+0x38` 的不透明 driver 句柄 `SE_StreamExecutor*`,以及位于 `+0x40` 的设备 ordinal。它自身不拥有设备状态 — 每个 method 都通过 `ExecutorApiFn()` 针对 `+0x38` 句柄转发。

### 函数映射

| 函数 | 地址 | 角色 |
|---|---|---|
| `TpuPlatform::ExecutorForDevice` | `0xe999d40` | public mint-or-cache 入口;cache @ `platform+40` |
| `TpuPlatform::GetUncachedExecutor` | `0xe999da0` | Factory:`ExecutorApiFn()+32` mint + `new(0x48)` `TpuExecutor` |
| `ExecutorCache::GetOrCreate` | `0x1d0fd2e0` | double-checked 逐 ordinal cache;log @ `executor_cache.cc:44` |
| `ExecutorCache::Get` | `0x1d0fd580` | fast-path cached lookup |
| `TpuExecutor::Init` | `0xe996980` | 通过 shim 的逐 executor init |
| `TpuExecutor::CreateStream` | `0xe996ca0` | 创建 `TpuStream` |
| `TpuExecutor::CreateEvent` | `0xe996e60` | 创建 `TpuEvent` |
| `TpuExecutor::Allocate` | `0xe996fa0` | 通过 shim 分配设备内存 |

---

## 4. 类层次

### 三级层次

```text
stream_executor::StreamExecutor                 (abstract base, vtable off_21FDAD98)
  └ stream_executor::StreamExecutorCommon        (concrete intermediate; holds const Platform*)
      ├ stream_executor::host::HostExecutor       (CPU thread pool; in-binary)
      └ stream_executor::tpu::TpuExecutor          (C-shim over TfTpu_ExecutorApiFn)

stream_executor::Stream                          (abstract base)
  └ stream_executor::StreamCommon                 (holds parent StreamExecutor* @ +0x48)
      ├ stream_executor::host::HostStream          (synchronous, in-binary)
      └ tensorflow::tpu::TpuStream                 (: TpuStreamInterface, C-shim)

StreamExecutorCommon 是与设备无关的中间层:它持有 const Platform*,并实现 GetPlatformGetDeviceDescriptionActivateGetMemoryLimitBytes — 所有不需要触碰设备的内容。两个具体 executor 的区别只在于其 primitive operations 落到哪里HostExecutor 落到 tsl::thread::ThreadPoolTpuExecutor 落到 TfTpu_ExecutorApiFn 表。Stream 层次精确镜像这一点,StreamCommon 持有 parent back-pointer(记录于 Stream 语义 §1)。

Base / common 层

角色关键符号
StreamExecutor抽象设备 executor;vtable off_21FDAD98ctor 0x208193e0,D2 0x20819440
StreamExecutorCommon具体中间层;持有 const Platform*ctor 0x1d0f03e0GetDeviceDescription 0x1d0f04a0
Platform抽象 platform;FindExisting(int)FindExisting 0xe718b60
ExecutorCache逐 ordinal executor cacheGetOrCreate 0x1d0fd2e0Get 0x1d0fd580
DeviceDescription可复制的 device-info bagcopy-ctor 0xe6b5ee0
RuntimeAbiVersionManager单例 ABI guardGetInstance 0xe6b8040

特性 — libtpu 中没有 stream_executor::interpreter::InterpreterExecutor。XLA 独立 interpreter device(xla/backends/interpreter/)没有被链接。唯一的 “interpreter” 符号是 ANTLR parser runtime 和 mlir::interpreter::*,二者都与设备执行无关。期望有一个 interpreter Platform 可回退的重新实现者不会找到它 — host-interpreter 角色被拆分到编译期 xla::HloEvaluator(constant folding)和运行时 CPU thunk backend,它们都不是 SE Platform。当 compiler 无法 fold 某个内容时,该节点只是留在图中交给真实 backend。


5. Host SE 对象模型

目的

host 后端(stream_executor::host::*)是唯一一个有真实二进制内实现、而不是 C shim 的 SE 后端。它用于 host-memory staging、简单 host ops,以及 CPU-only PjRtClient 会驱动的 executor。它是同步变体:没有 worker thread,没有 task queue — 每个操作都在调用线程上 inline 运行,因此 stream 显然总是 drained。

HostPlatformHostExecutor::Init

HostPlatform(ctor 0xfe6d380)镜像 TpuPlatformExecutorForDevice / GetUncachedExecutor 形状(0xfe6d580 / 0xfe6d5e0),但构建的是 HostExecutor,而不是 shim wrapper。HostExecutor::Init0xfe6d780)会创建 host 唯一的真实资源 — thread pool:

c
// stream_executor::host::HostExecutor::Init()                 sub_FE6D780
function HostExecutor_Init(this):
    env = tsl::Env::Default()
    n   = tsl::port::NumSchedulableCPUs()                      // one thread per schedulable CPU
    pool = operator new(0x40)                                  // tsl::thread::ThreadPool
    pool->vtable = off_217B00E8
    construct_at<ThreadPool>(pool+3, env, "host-executor", n)  // named pool
    this->thread_pool      = pool + 3   // +0x38 (this+7)
    old = this->pool_owner              // +0x40 (this+8)
    this->pool_owner       = pool       // refcounted; release the old one
    if (old && --old.refcount == 0): destroy(old)
    return OkStatus
```text

`Allocate`/`Deallocate` 是普通的 `operator new` / `free`(host memory 只是进程内存,路由到 tcmalloc);`HostMemoryAllocate`(`0xfe6e0c0`)用 `AnyInvocable` deleter 包装 `new`,使返回的 `MemoryAllocation` 能释放自身。

### `HostStream` — 同步 stream 对象

`HostStream::HostStream`(`0xfe6ec80`)是两行 ctor:chain 到 `StreamCommon`,然后设置 vtable。

```c
// stream_executor::host::HostStream::HostStream(StreamExecutor*)   sub_FE6EC80
function HostStream_ctor(this, parent):
    StreamCommon::StreamCommon(this, parent)    // 0x1d100280 — sets parent @ +0x48, base layout
    this->vtable = off_217B0228                 // 0x80-byte object

它是 0x80(128)字节对象(由 HostExecutor::CreateStream 分配 new(0x80))。StreamCommon base 提供 parent pointer 和 FIFO/sub-stream slots;HostStream 增加 host-specific notification slot。CreateStream0xfe6de20)通过可插拔 factory 路由,并且只有在没有注册 factory 时才 new 一个默认 HostStream

c
// stream_executor::host::HostExecutor::CreateStream(...)      sub_FE6DE20
function HostExecutor_CreateStream(this):
    f = HostStreamFactory::GetFactory()                        // 0xfe6eb40
    if (f):  s = f->CreateStream(this)                         // pluggable override
    else:    s = new(0x80) HostStream(this)                    // default path
    return StatusOr::ok(s)
```text

stream 的运行时行为由其同步性质决定:`BlockHostUntilDone`(`0xfe6f000`)字面上就是 `return 1`(所有事情已经运行完),而 `DoHostCallbackWithStatus`(`0xfe6efe0`)会在调用线程上 inline 调用 callback。`WaitFor`/`RecordEvent` surface(no-op `WaitFor(Stream*)`、基于 `Notification` 的 `WaitFor(Event*)`/`RecordEvent`)记录在 [Stream 语义 §3](../runtime/stream-semantics.md) — wait 模型由该页面负责。

### Host `Event` — 双对象拆分

`HostExecutor::CreateEvent`(`0xfe6d9e0`)把 host event 构建成**两个**堆对象:

```c
// stream_executor::host::HostExecutor::CreateEvent()          sub_FE6D9E0
function HostExecutor_CreateEvent(this):
    wrapper = operator new(0x18)                  // outer Event handle
    wrapper->vtable = off_217B0138
    inner   = operator new(0x28)                  // the real event state
    inner->vtable          = off_215FC128
    inner->[+24]           = absl::Notification{}  // single-shot notification
    inner->[+32]           = 0                     // notified flag (byte)
    wrapper[+1]            = &inner[+24]            // ptr to the Notification
    wrapper[+2]            = inner                  // ptr to the inner object (for refcount)
    return StatusOr::ok(wrapper)

这种拆分是有意为之:外层 0x18 字节 wrapper 是 SE API 发放的 Event(vtable off_217B0138);内层 0x28 字节对象(vtable off_215FC128)在 +24 处持有 absl::Notification,在 +32 处持有 notified-flag byte,并带有 shared_weak_count,这样 inner object 可以在 WaitFor/RecordEvent 之间被准确释放一次。host event 是单次触发的 — recording 一个已经 notified 的 event 会在 host_stream.cc:92 CHECK-fail(见 Stream 语义 §3)。

函数映射

函数地址角色
HostPlatform::HostPlatform0xfe6d380Host platform ctor
HostPlatform::ExecutorForDevice0xfe6d580Mint-or-cache(镜像 TPU)
HostPlatform::GetUncachedExecutor0xfe6d5e0构建 HostExecutor
HostExecutor::Init0xfe6d780ThreadPool("host-executor", NumSchedulableCPUs)
HostExecutor::CreateStream0xfe6de20Factory 或默认 new(0x80) HostStream
HostExecutor::CreateEvent0xfe6d9e0基于双对象 Notification 的 event
HostExecutor::HostMemoryAllocate0xfe6e0c0new + AnyInvocable deleter
HostStream::HostStream0xfe6ec80StreamCommon + vtable off_217B02280x80 B
HostStream::BlockHostUntilDone0xfe6f000return 1(同步)
HostStream::DoHostCallbackWithStatus0xfe6efe0在 caller thread 上 inline 调用
HostStreamFactory::GetFactory0xfe6eb40可插拔 stream-factory accessor
HostStreamFactory::Register0xfe6ea20安装 custom stream factory

注意 — HostStreamFactory::Register0xfe6ea20)提供了一个 hook,可覆盖默认 HostStream,但在任何已追踪 call site 中都没有发现非默认 factory registration — GetFactory 路径返回 null,CreateStream 落入默认 new HostStream。该 override 在此构建中可达的置信度:LOW。重新实现者可以忽略这个 factory hook,除非需要 custom host stream。


6. 给重新实现者的注意事项

  • 注册是一次性、失败即 fatal 的握手。 镜像 RegisterTpuPlatform:受 backend-enabled flag 控制,用 once-flag 保护,并把 RegisterPlatform 失败视作 fatal(tpu_platform.cc:178 的 CHECK)。静默失败的注册会让设备枚举断掉且没有错误。
  • PlatformManager 为准,而不是 MultiPlatformManager 旧类名已从此构建中消失(§1 说明)。registry 以 virtual platform-id 为 key。
  • Executors 是带 double-checked cache 的逐 ordinal 单例。 不要每次调用都构建新 executor。复制 ExecutorCache:fast-path Get,在锁构建 factory 结果,然后在锁下 find_or_prepare_insert,并丢弃竞态中的失败者。
  • TPU executor 不拥有设备状态。 它是一个 0x48 字节的句柄持有者(SE_StreamExecutor* @ +0x38,ordinal @ +0x40)。每个 method 都通过 ExecutorApiFn() 转发;status 习惯用法始终是 scratch / op / ok?-code-msg / free @ status_helper.h:38
  • host 后端按设计是同步的。 BlockHostUntilDone 返回 true,callbacks inline 运行,host events 是 single-shot(host_stream.cc:92 CHECK)。如果添加 async host worker queue(上游 XLA 有一个 — 此处未链接),就必须同时改变这三点。不存在 async HostStream 符号是基于推断(未找到符号),因此对此链接构建纯同步的置信度:HIGH。
  • 不存在 interpreter fallback Platform 不要期望编译失败会路由到 interpreter device。编译期求值是 xla::HloEvaluator(constant folding);运行时 host 执行是 CPU thunks;二者都在 SE Platform 模型之外(§4 特性)。

相关组件

名称关系
stream_executor::PlatformManager本页记录的进程全局 registry(由 MultiPlatformManager 改名而来)
tensorflow::tpu::TpuPlatformTPU PlatformRegisterTpuPlatform 安装它,ExecutorForDevice 创建 TpuExecutors
stream_executor::host::HostPlatform / HostExecutor / HostStream同步的二进制内 host SE 对象模型
stream_executor::ExecutorCacheExecutorForDevice 背后的逐 ordinal FlatHashMap<int, unique_ptr<SE>> cache
TfTpu_ExecutorApiFn tableTPU 后端转发到的 C-ABI(ExecutorApiFn() slots +16/+32/+360/+384/+392/+400/+408
xla::HloEvaluator / CPU thunks不是 SE Platform 的 host-interpreter 角色(编译期 fold + 运行时 CPU backend)

交叉引用