RTTI 命名空间普查
本页上的所有计数、地址和符号名称适用于
libtpu-0.0.40-cp314轮中的libtpu.so:一个 781,691,048 字节的 ELF64 共享对象,build-id89edbbe81c5b328a958fe628a9f2207d(轮/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 代号 命名空间 — jellyfish、pufferfish、viperfish、ghostlite、sparse_core — 根本不是**顶级命名空间;它们是嵌套在 xla::、mlir:: 和 platforms_deepsea:: 内的子命名空间,并且它们的类计入这些父级下。
人口普查可以通过两种方式计算,但这两种方式不一致——这是本页上最重要的警告。这里使用的度量是通过领先的 typeinfo 命名空间(_ZTIN<len><name> 前缀 - “这个类位于命名空间 X”)进行计数:它回答“X 定义了多少个多态类”。按分解名称的顶部标记计数会超出仅标头模板库的计数 - absl::StatusOr<xla::Foo> 和 Eigen::Matrix<...> 显示为数千个类型信息的返回类型或模板参数,这些类型信息的所属类位于其他命名空间中。如果两者相差一个数量级(absl、Eigen、xla),下面的 > **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(类型信息)结构 |
命名空间 _ZTI | 46,078(领先的N代币) |
全局/复合 _ZTI | 14,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) |
|---|---|---|---|
mlir | 13,091 | ~13,000 | mlir::Pattern(6,142 / 9,0x21cea698); OperationName::InterfaceConcept (6,052 / 2, 0x217b1000) |
asic_sw | 11,379 | ~11,400 | …::profiler::EventControlInterface (821 / 1, 0x2175c798) |
tensorflow | 3,108 | ~3,100 | tensorflow::OpKernel (1,122 / 4, 0x218114c8) |
xla | 3,036 | ~3,000 | xla::HloInstruction (68 / 4, 0x21d2ce88); HloPassInterface (361 / 4) |
llvm | 2,940 | ~2,900 | llvm::Pass (628 / 5, 0x21ced3b8) |
(anonymous) | 2,352 | ~2,350 | 每 TU 本地类 (_GLOBAL__N_…) — 没有单个树 |
dnnl | 1,888 | ~1,900 | dnnl::impl::c_compatible (2,069 / 6, 0x21b69258) |
std | 1,787 | ~1,000 | std::__u 容器/iostream 管道 — 许多模板安装 |
grpc_core | 1,502 | ~1,500 | grpc_core::PolymorphicRefCount (442 / 6, 0x21ca0128) |
platforms_deepsea | 576 | ~580 | …::jellyfish::isa::Encoder (19 / 3, 0x21cb6a20) |
operations_research | 483 | ~610 | …::math_opt::SolverInterface 树(根 0x217fa708) |
grpc | 430 | ~430 | grpc::Service (44 / 10, 0x216162d8) — 二进制中最深的链 |
tpu | 315 | ~410 | tpu::TpuCodec (5 / 1, 0x21d35858) |
proto2 | 152 | ~8,000 | proto2::MessageLite (8,013 / 3, 0x22034138) — 参见问题 |
riegeli | 136 | ~140 | riegeli::Object (114 / 6, 0x220291a8) |
tsl | 128 | ~130 | tsl::core::RefCounted (140 / 4, 0x215f9b18) |
stream_executor | 58 | ~58 | stream_executor::… 设备/流接口 (0x215fb6f0) |
absl | 33 | ~33 | absl::Duration 和状态内部结构 (0x215fd610) — 请参阅下面的注释 |
Xbyak | 4 | ~551 | Xbyak::CodeArray (551 / 5, 0x21b6d738) — 参见问题 |
Eigen | 4 | ~4 | Eigen::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::HloProto、tensorflow::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(_ZTI0x21cea698,6,142 个后代,深度 9) — 重写/转换/降低模式森林。Pattern → RewritePattern → ConversionPattern → ConvertToLLVMPattern → ConvertOpToLLVMPattern → …,以TPU SparseCore下降链(SCConvertOpToLLVMPattern → StreamDmaOpLoweringBase → LinearStreamStartOpLowering)为最深分支。mlir::OperationName::InterfaceConcept(_ZTI0x217b1000,6,052 个后代,深度 2) — 类型擦除的操作接口调度:每个注册操作的InterfaceConcept → RegisteredOperationName → Model<Op>。这是调度表分类的23号RegisteredOperationName::Model<…>指纹。
QUIRK —
mlir::Operation不在本次普查中,并不是因为它被遗漏了。mlir::Operation、mlir::Value和mlir::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 作为第一个代币出现在任何地方”——一个上限,而不是阶级所有权。
供应商支持尾部
框架核心下方是静态链接的第三方库。他们贡献了大量的类型信息人口,但扎根很少的中央层次结构。
| 命名空间 | 类型信息 | 角色 | 代表根 |
|---|---|---|---|
dnnl | 1,888 | oneDNN 基元描述符/JIT 基元 | dnnl::impl::c_compatible 0x21b69258 (2,069 / 6) |
std | 1,787 | libc++ std::__u 容器、iostream、异常 | std::exception,分配器/迭代器模板实例 |
grpc_core | 1,502 | gRPC 引用计数核心(通道、LB、凭证) | grpc_core::PolymorphicRefCount 0x21ca0128 (442 / 6) |
platforms_deepsea | 576 | TPU ISA 捆绑编码器(每系列/每核) | …::jellyfish::isa::Encoder 0x21cb6a20 (19 / 3) |
operations_research | 483 | OR-Tools CP-SAT / math-opt 求解器 | …::math_opt::SolverInterface 0x217fa708 |
grpc | 430 | gRPC 生成的服务 | grpc::Service 0x216162d8(44 / 10 — 最深) |
tpu | 315 | 公共TPU编解码器/API接口 | tpu::TpuCodec 0x21d35858 (5 / 1) |
proto2 | 152 | protobuf消息运行时基础 | proto2::MessageLite 0x22034138 (8,013 / 3) |
riegeli | 136 | 记录IO对象库(读取器/写入器/编解码器) | riegeli::Object 0x220291a8 (114 / 6) |
tsl | 128 | TSL/TF 引用计数基数(设备、回调) | tsl::core::RefCounted 0x215f9b18 (140 / 4) |
stream_executor | 58 | 设备/流抽象层 | stream_executor::RuntimeAbiVersionManager 0x215fb6f0 |
absl | 33 | Abseil — 几乎没有多态类 | absl::Duration 0x215fd610 |
Eigen | 4 | Eigen — 仅标题模板 | Eigen::ThreadPoolInterface 0x2163bd98 |
grpc::Service 值得一提:在深度 10 处,它是二进制文件中最深的单个继承链,在生成的 tpu_debugger 服务中触底。 operations_research 行是最脆弱的计数 — 其前导前缀 _ZTIN19operations_research grep 为 483,但包括本地类 (_ZTIZN19operations_research…$_0) 和 math_opt 子命名空间 lambda 将“任何位置”计数推至约 610。
注意 —
absl和Eigen绝大多数是仅标头模板库:absl::StatusOr<T>、absl::flat_hash_map<K,V>、Eigen::Matrix<…>实例化为数千个类型信息的外部类型,但拥有的多态类是几乎总是在其他地方。按领先的 typeinfo 命名空间进行计数,得出absl33 和Eigen4 — 每个定义的真正多态类(例如Eigen::ThreadPoolInterface)。 Demangled-top-token 计数分别给出 509 和 406;这些衡量的是模板包装器的流行度,这是一个不同的合法指标,但不是类所有权。
TPU 代号子命名空间
寻找 jellyfish、pufferfish、viperfish、ghostlite 或 sparse_core 作为顶级命名空间的读者不会在普查表中找到它们 — 而这种缺失本身就是一个发现。这些是 TPU 生成/子系统代号,它们仅作为框架和驱动程序帝国内的嵌套子命名空间出现。计算每个损坏名称包含标记的 _ZTI(无论嵌套深度如何):
| 代号 | _ZTI 出现次数 | 嵌套父母(它居住的地方) |
|---|---|---|
jellyfish | 2,996 | xla::jellyfish (667)、platforms_deepsea::jellyfish (576)、asic_sw::…::jfc |
sparse_core | 3,155 | mlir::sparse_core (1,689)、xla::tpu::sparse_core (553)、platforms_performance_deepsea::sparse_core |
pufferfish | 29 | xla::pufferfish,asic_sw::…::pfc::Pufferfish* |
viperfish | 19 | xla::viperfish,xla::tpu::sparse_core::isa_emitter::viperfish |
ghostlite | 17 | xla::ghostlite,xla::tpu::sparse_core::isa_emitter::ghostlite |
注意 — 上面的代号计数是 子字符串(符号中的任何位置)匹配,而不是前导命名空间存储桶,因此它们 重叠 人口普查表的
xla/mlir/platforms_deepsea行,而不是添加到其中。xla::内的jellyfish667已计入xla3,036;mlir::内的sparse_core1,689 已在mlir13,091 内。 不要将代号行汇总到命名空间总计中 - 它们是一个横切视图,其呈现方式是为了重新实现者可以找到特定于代号的代码,而不是分区。这种不对称性(sparse_core3,155 与pufferfish29)反映了 SparseCore 具有完整的 MLIR 方言 + 降低管道 + ISA 发射器,而较旧的芯片代号仅作为少数驱动器/发射器叶类存在。QUIRK — 代号出现在两个角色中。 小写字母(
jellyfish、viperfish)是一个命名空间标记;大写的(PufferfishDeviceScanner、ViperfishTensorCoreEmitter、GhostliteTensorCoreEmitter)是与生成无关的命名空间(xla::、asic_sw::…::pfc)中的类名称标记。两种形式都编码相同的目标代;将代号映射到芯片的重新实现者必须匹配命名空间和类名拼写。
复制品
普查是字节精确的,并且可以使用十几个 grep 从 RTTI sidecar 重建。分母是 60,457 个 _ZTI 结构(每个 _ZTI 损坏记录的 string_addr 是 typeinfo 结构 VA — 根据父人口普查进行验证:xla::HloInstruction _ZTI 解析为 0x21d2ce88,与层次结构匹配表)。
# 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) — 分类摘要背后详尽的每类调度表列表。