Skip to content

RTTI 命名空间普查

本页上的所有计数、地址和符号名称适用于 libtpu-0.0.40-cp314 轮中的 libtpu.so:一个 781,691,048 字节的 ELF64 共享对象,build-id 89edbbe81c5b328a958fe628a9f2207d(轮/METADATA/__init__ 版本是 0.0.40;引脚为 build-id)。其他轮子在每个地址都会有所不同。

摘要

libtpu.so 出厂时未剥离完整的 Itanium-ABI RTTI:每个多态类都留下一个 type_info 结构 (_ZTI)、一个类型名称字符串 (_ZTS),以及(如果具体的话)一个 vtable 组(_ZTV)。 RTTI / Vtable普查 建立标题计数(160,351 记录:_ZTI 60,457 · _ZTV 39,244 · _ZTS 60,650,总和正好为 160,351),并按宽度和深度对主要的“层次结构”进行排名。本附录对相同的 160,351 条记录提出了不同的问题:哪个 C++ 命名空间拥有类型系统? 它通过其领先的命名空间标记对 60,457 个不同的 type_info 结构进行存储,并根据每个库贡献的多态类数量对库进行排名。

答案是两个帝国的分裂。 MLIR(mlir::,13,091 个类型信息)和 TPU 驱动程序堆栈(asic_sw::,11,379 个类型信息)共同拥有二进制文件中每个多态类的 40% — MLIR 是因为每个注册的操作、模式、传递和方言接口都是不同的 C++ 类型,而 asic_sw:: 是因为每个代号 /每通道集群硬件驱动程序为每一代芯片×功能块实例化一个单独的类。在它们后面是框架核心(tensorflow:: 3,108、xla:: 3,036、llvm:: 2,940)和一长串供应商支持库(dnnl:: 1,888、std:: 1,787、 grpc_core:: 1,502)。读者可能期望在顶部看到的 TPU 代号 命名空间 — jellyfishpufferfishviperfishghostlitesparse_core — 根本不是**顶级命名空间;它们是嵌套在 xla::mlir::platforms_deepsea:: 内的子命名空间,并且它们的类计入这些父级下。

人口普查可以通过两种方式计算,但这两种方式不一致——这是本页上最重要的警告。这里使用的度量是通过领先的 typeinfo 命名空间(_ZTIN<len><name> 前缀 - “这个类位于命名空间 X”)进行计数:它回答“X 定义了多少个多态类”。按分解名称的顶部标记计数会超出仅标头模板库的计数 - absl::StatusOr<xla::Foo>Eigen::Matrix<...> 显示为数千个类型信息的返回类型模板参数,这些类型信息的所属类位于其他命名空间中。如果两者相差一个数量级(abslEigenxla),下面的 > **NOTE —** 会记录这两个数字并解释差距。

为了复制——从二进制文件重建此人口普查——合约是:

  • 分桶规则: type_info 结构体的所属命名空间是其 _ZTI 损坏符号的前导 N<len><name> 令牌;没有前导 N_ZTI 是全局范围或复合(指针/函数/模板替换)类型。
  • 分母: 60,457 个 _ZTI 结构,不是完整的 160,351 个(将每个类三重计算为 _ZTI+_ZTV+_ZTS)。 60,457 个中的 46,078 个拥有领先的命名空间; 14,379 是全局范围或复合类型。
  • **模板包装陷阱:**永远不会通过分解的顶部令牌进行存储,或者 absl/Eigen/std 模板包装会膨胀几乎不拥有自己的多态类的库。
分母60,457 _ZTI(类型信息)结构
命名空间 _ZTI46,078(领先的N代币)
全局/复合 _ZTI14,379(_ZTIP…_ZTIF…_ZTI1X、模板替换)
顶级命名空间mlir:: — 13,091 个类型信息(约占所有 _ZTI 的 21.6%)
两个帝国共享mlir:: + asic_sw:: = 24,470 = _ZTI 的 40.5%
桶键前导 _ZTIN<len><name> 损坏的前缀

人口普查表

60,457 个 type_info 结构按前导命名空间存储,按类型信息计数排名。 “Typeinfos”是 _ZTI 结构的计数,其重整符号以 _ZTIN<len><namespace> 开头。 “~Classes”与作为类总体读取的数字相同 - _ZTI 结构一个多态类标识,因此两者是相等的,除非一个逻辑类的模板实例化增加了计数(每行调用)。 “主导层次结构”是植根于该命名空间的最宽/最深的树,其根为 _ZTI 结构 VA。计数是 RTTI sidecar 上的字节精确 grep;层次结构的宽度/深度承载着父人口普查的置信度。

命名空间类型信息~课程主导层次结构(宽度/深度,根 _ZTI)
mlir13,091~13,000mlir::Pattern(6,142 / 9,0x21cea698); OperationName::InterfaceConcept (6,052 / 2, 0x217b1000)
asic_sw11,379~11,400…::profiler::EventControlInterface (821 / 1, 0x2175c798)
tensorflow3,108~3,100tensorflow::OpKernel (1,122 / 4, 0x218114c8)
xla3,036~3,000xla::HloInstruction (68 / 4, 0x21d2ce88); HloPassInterface (361 / 4)
llvm2,940~2,900llvm::Pass (628 / 5, 0x21ced3b8)
(anonymous)2,352~2,350每 TU 本地类 (_GLOBAL__N_…) — 没有单个树
dnnl1,888~1,900dnnl::impl::c_compatible (2,069 / 6, 0x21b69258)
std1,787~1,000std::__u 容器/iostream 管道 — 许多模板安装
grpc_core1,502~1,500grpc_core::PolymorphicRefCount (442 / 6, 0x21ca0128)
platforms_deepsea576~580…::jellyfish::isa::Encoder (19 / 3, 0x21cb6a20)
operations_research483~610…::math_opt::SolverInterface 树(根 0x217fa708)
grpc430~430grpc::Service (44 / 10, 0x216162d8) — 二进制中最深的链
tpu315~410tpu::TpuCodec (5 / 1, 0x21d35858)
proto2152~8,000proto2::MessageLite (8,013 / 3, 0x22034138) — 参见问题
riegeli136~140riegeli::Object (114 / 6, 0x220291a8)
tsl128~130tsl::core::RefCounted (140 / 4, 0x215f9b18)
stream_executor58~58stream_executor::… 设备/流接口 (0x215fb6f0)
absl33~33absl::Duration 和状态内部结构 (0x215fd610) — 请参阅下面的注释
Xbyak4~551Xbyak::CodeArray (551 / 5, 0x21b6d738) — 参见问题
Eigen4~4Eigen::ThreadPoolInterface (0x2163bd98) — 参见下面的注释

注意 — 表行总数约为 43,500;带有单位数名称空间(boringssl、re2、nsync、farmhash、snappy、zlibwrapper 等)的长尾,命名空间总数为 46,078,其余 14,379 _ZTI 是全局范围类和复合类型(_ZTIPF… 函数指针、_ZTIN… 模板替换,其替换解析如下领先的令牌)。总计:60,457。

明白了 — typeinfo 计数不是类树大小。 两行显示相反方向的陷阱。 proto2 仅拥有 152 typeinfo 结构,但 proto2::MessageLite 根植于 8,013 类树 - 因为 8,000 多个生成的消息类(xla::HloPrototensorflow::GraphDef 等)位于它们自己的命名空间中,并proto2::Message 继承;它们计入 xla/tensorflow,而不计入 proto2。相反,Xbyak 拥有 4 个领先命名空间类型信息,但 Xbyak::CodeArray 根源于 551 个后代——从它派生的 oneDNN JIT 发射器。 **按命名空间对定义的类进行计数;层次结构宽度计算其使用的位置。**对于其子类位于其他地方的基类,两者永远不会重合。


mlir — 最大的类型帝国 (13,091)

为什么MLIR占主导地位

MLIR 比任何其他命名空间贡献更多的多态类,因为 MLIR 的可扩展性模型 C++ 类型扩散。每个注册操作、每个重写模式、每个通道和每个方言接口都具体化为具有自己的 type_info 的独特具体类。整个二进制中两棵最宽的树都是 MLIR:

  • mlir::Pattern (_ZTI 0x21cea698,6,142 个后代,深度 9) — 重写/转换/降低模式森林。 Pattern → RewritePattern → ConversionPattern → ConvertToLLVMPattern → ConvertOpToLLVMPattern → …,以TPU SparseCore下降链(SCConvertOpToLLVMPattern → StreamDmaOpLoweringBase → LinearStreamStartOpLowering)为最深分支。
  • mlir::OperationName::InterfaceConcept(_ZTI 0x217b1000,6,052 个后代,深度 2) — 类型擦除的操作接口调度:每个注册操作的 InterfaceConcept → RegisteredOperationName → Model<Op>。这是调度表分类的23号RegisteredOperationName::Model<…>指纹。

QUIRK — mlir::Operation 不在本次普查中,并不是因为它被遗漏了。 mlir::Operationmlir::Valuemlir::Block非多态 — 它们没有 vtable,也没有 type_info,因此它们不发出_ZTI 对于 RTTI 步行是不可见的。 MLIR 操作行为通过上面的两棵树(接口 Model<Op> 和重写 Pattern)进行调度,而不是通过 Operation 上的虚拟方法。期望多态 Operation 基的重新实现者将找不到。

确实携带 typeinfo(总共 283 个)的具体 *Op C++ 类是其操作兼作 C++ 值类型的方言:mlir::hlo (102)、mlir::stablehlo (85)、mlir::TF (47)、 mlir::tfg (20)、quant/linalg(各 10 个)。通过和方言管道填充其余:mlir::Pass(606 / 7,0x21c2c450),mlir::Dialect(67种方言,0x21cea490),mlir::DialectInterface(100,0x21cea480)。


asic_sw — TPU 硬件驱动程序 (11,379)

asic_sw:: 是低级 TPU 设备驱动程序,由于结构原因它是第二大命名空间:它为每个芯片代×功能块×通道集群实例化一个单独的具体类。命名是笛卡尔积。最深的嵌套 - asic_sw::driver::deepsea::pxc::pfc::b0::TensorCoreCoreFactory - 编码芯片系列 (pxc/vxc/gxc/jxc),一种核心类型(pfc/plc/vfc/vlc/gfc/glc/dfc/jfc),以及硅版本(b0),并且有每个组合都是一个这样的类。

主树是 asic_sw::driver::deepsea::profiler::EventControlInterface(_ZTI 0x2175c798,821 个后代,深度 1 — 所有直接叶),每通道集群性能计数器事件控制层次结构,按通道集群精确分区:gxc/gfc 320, vxc/vfc 264、gxc/glc 130、pxc/pfc 63、vxc/vlc 24、pxc/plc 20。代表性的叶类型信息是 asic_sw::driver::DmaBuffer (_ZTI 0x215ff0f8)。

注意 — asic_sw:: 是设备上运行时,与为设备编译的 xla::/mlir:: 不同。 驱动程序实例化每芯片类;编译器发出目标通用 IR 并稍后选择代号。这两个命名空间几乎不共享基类 - 它们之间的边界是 TpuHal/TpuCodec 接口系列(tpu::,如下)。


tensorflow / xla / llvm — 框架核心

这三个命名空间是编译器和运行时本身,位于 ~3,000-typeinfo 带中。

tensorflow (3,108) 植根于 tensorflow::OpKernel(_ZTI 0x218114c8,1,122 个后代,深度 4)——TPU 嵌入和 XLA 桥接内核背后的 TF 操作内核基础。具有代表性的叶子 tensorflow::(anonymous)::TPUEmbeddingActivations (_ZTI 0x215f81f0) 显示了这里有多少 TF 表面是 TPU 特定的。

xla (3,036) 分为两棵著名的树:xla::HloInstruction(_ZTI 0x21d2ce88,68 个后代,深度 4 - 37 直接,9 个内部,59 个叶子)用于 IR 节点层次结构,以及xla::HloPassInterface(0x217f4428,361 后代,深度 4)用于编译器传递接口。 TPU codegen 发射器 xla::jellyfish::OpEmitter (0x219b0080, 66) 和 SparseCore 卸载工厂 xla::tpu::sparse_core::collective::OffloadFactory (0x218fffd8, 60 / 7) 作为嵌套代码名子命名空间位于 xla:: 下。

llvm (2,940) 是嵌入式 LLVM 后端 - 它是完整后端,而不是切片。 llvm::Pass(_ZTI 0x21ced3b8,628个后代,深度5)是最深的codegen结构;其下的 FunctionPass (506) → MachineFunctionPass (351) 承载 AMDGPU、PPC、ARM、AArch64、X86 TPU MachineFunctionPass。 Attributor的并行CRTP树(llvm::AbstractState 329 / 9、llvm::IRPosition 299 / 8、llvm::AADepGraphNode 299 / 8)是继grpc::Service之后最深的模板链。

注意 — xla 有两个计数。按领先的 typeinfo 命名空间 (_ZTIN3xla…) 进行计数,得到 3,036 — 实际上定义的多态类 xla 的数量,以及此页面排名的数字。计算每个 demangled 名称的顶部标记给出 5,291,但这会将 xla 归功于每个 absl::StatusOr<xla::…>std::unique_ptr<xla::…> 包装器,其所属类位于 absl/std 中。 5,291 这个数字是“xla 作为第一个代币出现在任何地方”——一个上限,而不是阶级所有权。


供应商支持尾部

框架核心下方是静态链接的第三方库。他们贡献了大量的类型信息人口,但扎根很少的中央层次结构。

命名空间类型信息角色代表根
dnnl1,888oneDNN 基元描述符/JIT 基元dnnl::impl::c_compatible 0x21b69258 (2,069 / 6)
std1,787libc++ std::__u 容器、iostream、异常std::exception,分配器/迭代器模板实例
grpc_core1,502gRPC 引用计数核心(通道、LB、凭证)grpc_core::PolymorphicRefCount 0x21ca0128 (442 / 6)
platforms_deepsea576TPU ISA 捆绑编码器(每系列/每核)…::jellyfish::isa::Encoder 0x21cb6a20 (19 / 3)
operations_research483OR-Tools CP-SAT / math-opt 求解器…::math_opt::SolverInterface 0x217fa708
grpc430gRPC 生成的服务grpc::Service 0x216162d8(44 / 10 — 最深)
tpu315公共TPU编解码器/API接口tpu::TpuCodec 0x21d35858 (5 / 1)
proto2152protobuf消息运行时基础proto2::MessageLite 0x22034138 (8,013 / 3)
riegeli136记录IO对象库(读取器/写入器/编解码器)riegeli::Object 0x220291a8 (114 / 6)
tsl128TSL/TF 引用计数基数(设备、回调)tsl::core::RefCounted 0x215f9b18 (140 / 4)
stream_executor58设备/流抽象层stream_executor::RuntimeAbiVersionManager 0x215fb6f0
absl33Abseil — 几乎没有多态类absl::Duration 0x215fd610
Eigen4Eigen — 仅标题模板Eigen::ThreadPoolInterface 0x2163bd98

grpc::Service 值得一提:在深度 10 处,它是二进制文件中最深的单个继承链,在生成的 tpu_debugger 服务中触底。 operations_research 行是最脆弱的计数 — 其前导前缀 _ZTIN19operations_research grep 为 483,但包括本地类 (_ZTIZN19operations_research…$_0) 和 math_opt 子命名空间 lambda 将“任何位置”计数推至约 610。

注意 — abslEigen 绝大多数是仅标头模板库:absl::StatusOr<T>absl::flat_hash_map<K,V>Eigen::Matrix<…> 实例化为数千个类型信息的外部类型,但拥有的多态类是几乎总是在其他地方。按领先的 typeinfo 命名空间进行计数,得出 absl 33Eigen 4 — 每个定义的真正多态类(例如 Eigen::ThreadPoolInterface)。 Demangled-top-token 计数分别给出 509406;这些衡量的是模板包装器的流行度,这是一个不同的合法指标,但不是类所有权。


TPU 代号子命名空间

寻找 jellyfishpufferfishviperfishghostlitesparse_core 作为顶级命名空间的读者不会在普查表中找到它们 — 而这种缺失本身就是一个发现。这些是 TPU 生成/子系统代号,它们仅作为框架和驱动程序帝国内的嵌套子命名空间出现。计算每个损坏名称包含标记的 _ZTI(无论嵌套深度如何):

代号_ZTI 出现次数嵌套父母(它居住的地方)
jellyfish2,996xla::jellyfish (667)、platforms_deepsea::jellyfish (576)、asic_sw::…::jfc
sparse_core3,155mlir::sparse_core (1,689)、xla::tpu::sparse_core (553)、platforms_performance_deepsea::sparse_core
pufferfish29xla::pufferfishasic_sw::…::pfc::Pufferfish*
viperfish19xla::viperfishxla::tpu::sparse_core::isa_emitter::viperfish
ghostlite17xla::ghostlitexla::tpu::sparse_core::isa_emitter::ghostlite

注意 — 上面的代号计数是 子字符串(符号中的任何位置)匹配,而不是前导命名空间存储桶,因此它们 重叠 人口普查表的 xla/mlir/platforms_deepsea 行,而不是添加到其中。 xla::内的jellyfish 667已计入xla 3,036; mlir:: 内的 sparse_core 1,689 已在 mlir 13,091 内。 不要将代号行汇总到命名空间总计中 - 它们是一个横切视图,其呈现方式是为了重新实现者可以找到特定于代号的代码,而不是分区。这种不对称性(sparse_core 3,155 与 pufferfish 29)反映了 SparseCore 具有完整的 MLIR 方言 + 降低管道 + ISA 发射器,而较旧的芯片代号仅作为少数驱动器/发射器叶类存在。

QUIRK — 代号出现在两个角色中。 小写字母(jellyfishviperfish)是一个命名空间标记;大写的(PufferfishDeviceScannerViperfishTensorCoreEmitterGhostliteTensorCoreEmitter)是与生成无关的命名空间(xla::asic_sw::…::pfc)中的类名称标记。两种形式都编码相同的目标代;将代号映射到芯片的重新实现者必须匹配命名空间和类名拼写。


复制品

普查是字节精确的,并且可以使用十几个 grep 从 RTTI sidecar 重建。分母是 60,457 个 _ZTI 结构(每个 _ZTI 损坏记录的 string_addr typeinfo 结构 VA — 根据父人口普查进行验证:xla::HloInstruction _ZTI 解析为 0x21d2ce88,与层次结构匹配表)。

text
# total records (160,351) and flavor split (nm libtpu.so | rg -c '_ZTI' etc.):
count mangled ^_ZTI                 ->  60,457   (typeinfo structs = denominator)
count mangled ^_ZTV                 ->  39,244   (vtable groups)
count mangled ^_ZTS                 ->  60,650   (type-name strings)
# 60,457 + 39,244 + 60,650              = 160,351 (records)

# per-namespace bucket (leading prefix; <len> is the namespace name length):
count mangled ^_ZTIN3xla            ->   3,036
count mangled ^_ZTIN4mlir           ->  13,091
count mangled ^_ZTIN7asic_sw        ->  11,379
count mangled ^_ZTIN10tensorflow    ->   3,108
count mangled ^_ZTIN4llvm           ->   2,940
count mangled ^_ZTIN4dnnl           ->   1,888
count mangled ^_ZTIN9grpc_core      ->   1,502
count mangled ^_ZTIN6proto2         ->     152

# total namespaced vs global/compound:
count mangled ^_ZTIN                 ->  46,078   (leading-namespace)
60,457 - 46,078                      =  14,379   (global / pointer / function / substitution)
```text

> **GOTCHA — 正确获取 `<len>` 前缀。** Itanium mangling 将每个名称空间组件编码为其字节长度,后跟名称:`xla` 是 `3xla`,`mlir` 是 `4mlir`,`operations_research` 是`19operations_research`(19,而不是 20 — 计算下划线,但不计算前导数字)。 `_ZTIN20operations_research` 的 grep 返回**零**;正确的 length-19 前缀返回 483。长度标记中的减一会默默地将整个名称空间从普查中删除。

## 交叉引用

- [RTTI / Vtable普查](../forensics/rtti-vtable-census.md) — 策划的父级:记录分类、`type_info` 风格、按宽度/深度排列的前 30 个层次结构、边缘恢复方法。此附录是该数据的命名空间轴切片。
- [调度表分类](../forensics/dispatch-table-taxonomy.md) — 这里的每个主导层次结构都映射到那里的一个调度类(MLIR `Model<Op>`、llvm vtables、proto2、grpc),将命名空间所有权与 vtable-slot 指纹联系起来。
- [符号命名空间索引](symbol-namespace-index.md) — 更广泛的符号群图(所有符号,而不仅仅是 RTTI);互补轴,平行排列。
- [调度表分类(完整)](dispatch-table-taxonomy-full.md) — 分类摘要背后详尽的每类调度表列表。