Skip to content

符号命名空间索引

此页面上的所有计数适用于 libtpu-0.0.40-cp314 轮子中的 libtpu.so(781,691,048 字节 ELF64 共享对象,构建 ID 89edbbe81c5b328a958fe628a9f2207d;轮子/METADATA/__init__ 版本为0.0.40 — 引脚到 build-id)。字节占用空间是已解析函数的 size 总和,而不是节大小;其他轮子和其他提取通道会有所不同。

摘要

libtpu.so 是一个 745 MB 静态链接 PJRT 插件,并且(对于已发货的产品二进制文件来说很不寻常)它带有完整的本地符号表 (.symtab),而不仅仅是其 741 条目 .dynsym 中的 226 个定义的动态导出。 IDA 名称 sidecar 解析 1,893,205 个命名地址884,832 个函数,其中 881,784 个带有名称,3,048 个回落到合成的 sub_ 标签。由于名称是真正的 Itanium 损坏的 C++ 符号,因此二进制文件可以按其 顶级命名空间 进行分区 - 每个损坏的符号中紧跟在 _ZN 后面的命名空间标记。该分区就是本页的内容:按符号计数和代码字节占用对谁拥有多少函数群体进行普查。

参考框架是读者已经了解的 XLA/TPU 堆栈的其余部分。 libtpu.so 是将整个编译器加运行时折叠成一个共享对象:MLIR/LLVM 编译器核心、XLA HLO 中端、TensorFlow 运算桥、Eigen 内核库、Abseil/protobuf/gRPC 支持层,以及与芯片对话的封闭式 asic_sw 驱动程序堆栈(独一无二)。下面的排名使比例更加具体:仅 MLIR 就占所有命名函数的 31%,四个编译器命名空间(mlir、xla、llvm、tensorflow)加上驱动程序 (asic_sw) 超过 70%,所有 Google 内部但通用的内容(absl、tsl、protobuf、gRPC、tcmalloc)都是个位数的百分比尾部。

此页面是 RTTI命名空间普查符号/函数对应,它对 类型记录(typeinfo/vtable 条目)进行计数。两者在目的上存在很大分歧:具有数千个多态类 (asic_sw) 的命名空间在 RTTI 普查中排名靠前,而主要由自由函数和单态模板 (mlir、llvm) 组成的命名空间在此排名靠前。下面的方法部分明确了计数规则,因此数字是可重复的。

对于重新实现者来说,该页面满足的契约是:

  • 计数规则 — “拥有符号”的含义(_ZN<quals><len><ns> 顶级锚),以及为什么前缀任意位置和原始子字符串计数给出不同的、更大的答案。
  • 排名 — 每个命名空间的函数计数、字节占用量以及最大顶级所有者的角色,(其他) 包罗万象,因此列出的行加上剩余的函数总数达到 884,832 个。
  • 表面 - 为什么库族子串计数(absl、Eigen、re2)超出所有者计数,以及每个数字描述哪个表面。
二进制libtpu.so(车轮 0.0.40),build-id 89edbbe81c5b328a958fe628a9f2207d
符号表.symtab 存在(完整的本地符号) - 未剥离
命名地址1,893,205(881,784个功能·880,052个数据·108,629个其他·22,740个代码标签)
功能总计 884,832 — 881,784 已命名,3,048 匿名 (sub_)
解析的功能字节299,035,160(函数size的总和)
主要 .text314,422,404 字节,位于 0xe63c000;所有 CODE 段 342,157,540 字节
动态导出/导入226 / 515(.dynsym,总共 741 个条目,包括空)

符号如何归属

整个索引取决于一个决定:损坏的符号属于哪个命名空间。存在三个站得住脚的答案,它们给出了三种不同的计数,而将它们混为一谈是任何符号普查中最大的单一错误来源。

三个计数面

text
mangled symbol:  _ZN4mlir3xla9SomethingINS_4abslE...E   (illustrative)
                  └─┬─┘                  └──┬──┘
            top-level owner            absl appears here as
            = mlir                     a template argument

surface 1  TOP-LEVEL OWNER     token right after _ZN[KVrOR]   -> mlir
surface 2  PREFIX-ANYWHERE     token "4mlir" / "4absl" anywhere in the symbol
surface 3  RAW SUBSTRING       literal "mlir" / "absl" anywhere, mangled or not
```text

Itanium ABI 将嵌套名称编码为 `_ZN`,这是 CV/ref 限定符的可选运行(`K`、`V`、`r`、`O`、`R`),然后长度前缀组件(`3xla`、`4mlir`、`15stream_executor`)。 **顶级所有者**是第一个组件。它是划分总体的唯一表面——每个命名函数都恰好落在一个桶中,并且这些桶的总和等于总数。这就是该页面排名的表面。

> **明白了 —** 任意位置的前缀和原始子字符串计数**不**分区。签名提到 `absl::Status`、`std::vector` 和 `Eigen::Tensor` 的单个 `xla` 函数会同时增加四个命名空间的任意位置计数。将这些列相加得到的数字比 884,832 大几倍。它们是“参与”指标——对于“这个词汇有多普遍”有用,对于“谁拥有二进制文件”毫无用处。

### 正则表达式及其运行位置

顶级所有者是从功能 sidecar 的 `name` 字段中提取的,锚定模式为 `^_ZN[KVrOR]?<len><ns>[A-Z0-9]`。尾随的 `[A-Z0-9]` 拒绝意外的前缀冲突(因此 `3tslSomething` 不会与较长的令牌混淆)。名称不是嵌套重整符号的函数(C 符号、operator/`extern "C"` 导出、libc++ `std::__u` 包装器和 3,048 个 `sub_` 匿名函数)落入专用存储桶或 `other`。存储桶的字节占用空间是其成员上的 `size` 字段的总和。

> **注意 —** libc++ 符号损坏为 `_ZNSt3__u…`(`St` 替换 `std`,然后是内联 ABI 命名空间 `__u`),因此它们永远不会与真正的顶级项目命名空间发生冲突并获得自己的行。代号驱动程序命名空间(`deepsea`、`jxc`、`pxc`、`vxc`、`gxc` 及其 `b0`/`lc`/`fc` 子变体)**嵌套在`asic_sw`**,非顶级;它们出现在 `asic_sw` 总计中,并在详细信息部分中细分,而不是作为单独的行。

---

## 命名空间普查

顶级所有者函数计数和字节占用空间,按符号计数排序。这些行排列的是**最大**的顶级所有者;它们并没有耗尽人口。整个二进制总数为 **884,832** 函数和 **299,035,160** 字节;列出的名称空间加上*(其他)* 包罗万象的帐户占这些总数,其中包罗万象的所有者吸收了排名低于削减的每个所有者。几个真正受损的 `_ZN` 所有者陷入了这种残差——特别是 `dnnl`(oneDNN,~19.5k 函数)、`proto2`(protobuf 运行时,~12k)、`grpc_core`(~8.4k)、`operations_research`(~6.8k)和`platforms_deepsea`(~6k,代号根设备层)——每个都比下面列出的几行大。它们在这里被折叠到 *(other)* 中,并被同级 [RTTI 命名空间普查](rtti-namespace-census.md) 分解。

| 命名空间 | 功能 | 功能字节 | 功能百分比 | 在 `libtpu.so` 中的角色 |
|---|---:|---:|---:|---|
| `mlir` | 270,983 | 54,560,105 | 30.6% | MLIR 编译器框架 — 方言、通道、IR/op 基础设施 |
| `asic_sw` | 194,445 | 26,811,387 | 22.0% | 封闭式 TPU 驱动程序堆栈 — 芯片生成寄存器/队列/DMA 层 |
| *(其他)* | 138,313 | 85,667,960 | 15.6% | 下切损坏的所有者(`dnnl`、`proto2`、`grpc_core`,...)、C 符号、非 `_ZN` 导出、vtable thunk |
| `llvm` | 91,060 | 30,705,459 | 10.3% | LLVM 核心 — IR、代码生成、JIT 使用的目标后端 |
| `xla` | 62,221 | 42,138,764 | 7.0% | XLA HLO 中端 — 通道、布局、运行时、`megascale` 集合 |
| `std` (libc++) | 57,643 | 18,571,901 | 6.5% | libc++ `std::__u` 容器/算法实例化到二进制文件中 |
| `absl` | 27,777 | 8,080,990 | 3.1% | Abseil — `Status`、容器、字符串、同步、时间 |
| `tensorflow` | 17,721 | 10,915,236 | 2.0% | TF 操作桥 — XLA 内核、设备编译器、SparseCore 操作 |
| `Eigen` | 10,419 | 15,400,879 | 1.2% | Eigen 张量/矩阵内核 — 注意每个函数的高字节 |
| `tpu` | 3,438 | 1,339,121 | 0.4% | TPU 驱动胶 — `Tpu*Driver`、芯片/核心手柄、HAL 桥 |
| `anon` (`sub_`) | 3,048 | 1,807,907 | 0.3% | 不带符号的函数 — 合成 `sub_<addr>` 标签 |
| `xprof` | 2,605 | 1,606,724 | 0.3% | TPU 分析器 — 跟踪转换到 XPlane、计数器控制 |
| `grpc` | 2,265 | 499,369 | 0.3% | gRPC — 用于分布式/多主机执行的 RPC 传输 |
| `tsl` | 1,855 | 678,318 | 0.2% | TSL(TensorFlow 支持库)— `AsyncValue`、监控、平台 |
| `stream_executor` | 542 | 99,857 | 0.1% | StreamExecutor设备抽象接口 |
| `re2` | 226 | 109,063 | <0.1% | RE2正则表达式引擎 |
| `google::protobuf` | 201 | 25,173 | <0.1% | protobuf `google::` 下的 C++ 运行时(运行时的大部分是单独的 `proto2` 所有者,在 *(other)* 中;在自己的命名空间下生成消息 mangle) |
| `tcmalloc` | 70 | 16,947 | <0.1% | tcmalloc 分配器核心 |

> **QUIRK —** `google::protobuf`、`tcmalloc` 和 `re2` 按“拥有的函数计数”排名垫底,但毫无疑问地存在且大量使用。所有者计数低估了它们,因为它们的工作出现在其他地方:protobuf 运行时的大部分是单独的 `proto2` 所有者(~12k 函数,折叠到上面的 *(other)* 中)加上生成的消息代码(在每个消息自己的命名空间下和 `protodesc_cold` / `.lrodata` 数据段中损坏),tcmalloc 的热路径位于`google_malloc` / `malloc_hook` 代码部分按*函数*而不是按命名空间,RE2 的价值在于从各处调用的一些引擎函数。所有者计数衡量的是创作表面,而不是运行时权重。

### 阅读排名

其形状是一个带有驱动程序的编译器。前 5 个自有函数行 — `mlir`、`asic_sw`、`llvm`、`xla` 以及 `other` 包罗万象 — 占所有函数的 85%。两个真正特定于 TPU 的命名空间(`asic_sw`、`tpu`)总共拥有约 198k 函数,仅次于 MLIR;其他一切都是标准开源 XLA/TF 基板,相同的代码可以在任何后端上运行。

字节列讲述了计数列隐藏的第二个故事。 `mlir` 拥有最多的函数,但 `other` 拥有最多的字节 (85.7 MB) — 包罗万象的是大型供应商库和模板扩展体。 `Eigen` 是最明显的异常值:10,419 个函数,但 15.4 MB,约 1,478 字节/函数,大约是二进制范围平均值约 338 字节/函数的五倍。 Eigen 的完全展开的向量化表达式模板编译为非常大的单个函数 - 内核库的重新实现者预算代码大小应该期望这种密度,而不是计数。

---

## 完整符号表面(所有命名地址)

以上普查计数功能。名称 sidecar 还解析数据符号、vtable、typeinfo 记录和代码标签 — 总共 1,893,205 个命名地址。按照相同的顶级所有者规则进行分区,*完整*符号表面会对命名空间进行重新排序,因为数据密集型和 RTTI 密集型命名空间会拾取纯函数视图错过的条目。

| 命名空间 | 命名地址(所有类型) | 与函数计数 |
|---|---:|---|
| `mlir` | 286,192 | +15,209 数据/类型信息/标签 |
| `asic_sw` | 210,325 | +15,880 — 驱动器 RTTI 和寄存器表 |
| `llvm` | 120,884 | +29,824 — 大型静态表 |
| `xla` | 65,339 | +3,118 |
| `std` (libc++) | 57,726 | ≈ 相同(主要是功能) |
| `absl` | 28,189 | +412 |
| `tensorflow` | 20,923 | +3,202 |
| `Eigen` | 10,419 | ≈ 相同 |
| `xprof` | 2,853 | +248 |
| `tpu` | 3,989 | +551 |
| `grpc` | 2,563 | +298 |
| `tsl` | 1,985 | +130 |
| `stream_executor` | 656 | +114 |
| `re2` | 499 | +273 |
| `google::protobuf` | 276 | +75 |
| `tcmalloc` | 84 | +14 |

完整表面的类型细分(881,784 个函数、880,052 个数据、108,629 `other`、22,740 个代码标签)解释了增量。 `llvm` 获得最多的绝对条目(~30k),因为它的目标表、指令信息数组和内在描述符是大型静态数据对象; `asic_sw` 从每个寄存器和每个队列数据表以及其密集的 RTTI 中获得第二大增量。

> **注意 —** 数据符号计数 (880,052) 几乎等于函数计数 (881,784)。这是使用完整本地符号构建的高度 RTTI 的 C++ 二进制文件的特征:每个函数大约有一个命名数据对象(vtable、typeinfo、static、字符串文字标签)。 [RTTI vtable普查](../forensics/rtti-vtable-census.md) 深入研究该数据的一半。

---

## 驱动程序代号子命名空间

`asic_sw` 是唯一一个完全特定于 TPU 的顶级命名空间,并且在内部它是按 **芯片生成代号** 而不是按组件组织的。这些是嵌套命名空间 (`asic_sw::driver::deepsea::<codename>::…`),因此它们不会显示为顶级行;此处列出了他们的参与计数(符号中任何位置出现的损坏令牌),以调整每代驱动程序表面的大小。参与过度计数 - 在 `gxc` 函数内作为模板参数引用的 `pxc` 符号会增加两者 - 因此将它们视为相对大小,而不是不相交的总数。

| 子命名空间令牌 | 参与次数 | 角色 |
|---|---:|---|
| `deepsea` | 271,973 | 适用于所有 TPU 代的伞驱动器系列 |
| `gxc` | 156,011 | A芯片一代核心(按符号参与度最大) |
| `glc` | 77,591 | `gxc` 子核心/车道控制变体 |
| `vxc` | 68,986 | A芯片一代核心 |
| `pxc` | 33,363 | A芯片一代核心 |
| `vfc` | 21,860 | `vxc` 子核变体 |
| `pfc` | 10,647 | `pxc` 子核心变体(带有 `b0` 步进子命名空间) |
| `jxc` | 5,183 | A芯片一代核心 |
| `vlc` | 3,008 | `vxc` 子核变体 |
| `plc` | 1,379 | `pxc` 子核变体 |

该模式是一个 `<x>xc` 内核,具有 `<x>lc` / `<x>fc` 通道/结构子内核,对于 `pfc`,还有一个显式硅步进命名空间 (`b0`)。驱动程序每代都会复制相同的工厂/队列/寄存器接口系列(`*Factory`、`*Interface`、`QueueAllocator`、`IndirectStateFactory`),这就是参与计数相对于 asic_sw 拥有函数总数如此之大的原因 - 大多数符号是共享接口的模板实例化,每个代号重新标记一次。 [RTTI 命名空间普查](rtti-namespace-census.md) 将这些代号解析为来自类型信息记录的具体代。

> **QUIRK —** `deepsea` 也作为顶级命名空间(822 个拥有的符号)存在,与其嵌套在 `asic_sw` 下的主导角色(208,549 个 `asic_sw…deepsea…` 参与)分开。不要重复计算:822 是一个小型独立表面; 208k 是 asic_sw 存储桶已包含的驱动程序系列实例。

---

## 库-三个表面上的家庭计数

该子字符串统计了为嵌入式库系列生成的临时 `nm | grep` 超出了所有者对该页面排名的计数,因为每个页面测量的表面不同。三个最大的供应库的三个表面:

| 命名空间 | 顶级业主 | 任意位置损坏的令牌 | 原始子字符串(损坏或未损坏) |
|---|---:|---:|---:|
| `absl` | 28,189 | 117,015 | 117,804 |
| `Eigen` | 10,419 | 27,577 | 28,818 |
| `re2` | 226(严格496) | 591 | 18,572 |

> **注意 —** 对于 `re2`,原始子字符串计数 (18,572) 捕获 RE2 的 API 表面*加上*仅提及正则表达式类型的每个符号。 RE2 实际拥有的函数表面要小两个数量级 — **226** 函数、496 个顶级所有者名称。 ~18.5k 是一个参与/子串指标,与每个命名空间所有者的排名不可比较;该页面按所有权对 re2 进行排名。
>
> **注意 —** `absl` 拥有 **27,777** 个函数/28,189 个名称,但参与~117k 个符号(子字符串),而 `Eigen` 拥有 **10,419** 个函数/10,419 个名称,但参与~28.6k。使用所有者计数来解决“谁拥有二进制文件”问题;原始子串计数会使两者膨胀大约 3-4 倍。

该课程概括为:在表面被命名之前,任何表示“二进制中有多少absl”的单个数字都是不明确的。 Abseil 的词汇(`absl::Status`、`absl::StatusOr`、平面哈希容器、`absl::Span`)存在于大部分函数的“签名”中,因此其参与计数(约 117k,约 13% 的名称)使其所属函数计数(约 28k,约 3%)相形见绌。两者都是正确的;他们回答不同的问题。

---

## 为什么这与 RTTI 普查不同

[RTTI 命名空间普查](rtti-namespace-census.md) 和此索引对相同的二进制进行计数,并故意对其进行不同的排名。 RTTI 普查统计**类型记录** — 每个多态类有一个条目,带有 `typeinfo`/vtable。该索引计算**所有函数** — 每个方法、自由函数、模板实例化和 lambda 主体。

命名空间的两个等级因其*风格*而有所不同:

- **`asic_sw`** 在 RTTI(深层接口/工厂层次结构 = 许多多态类)中排名非常高,在这里也很高,但它的功能与类型比率适中,因为大多数类都是瘦接口。
- **`mlir` / `llvm`** 在这里排名最高(巨大的自由函数和模板方法表面),但在 RTTI 中每种类型的排名较低,因为编译器的大部分是单态模板和没有 vtable 的自由函数。
- **`Eigen`** 在 RTTI 普查中几乎不可见(表达式模板是非多态的),但这里拥有 10k+ 函数和 15 MB。

重新实现者在调整组件大小时应参考以下两方面内容:本页了解*代码量和函数计数*,RTTI 普查了解*类/接口表面*。两者都不是整体。

---

## 交叉引用

- [RTTI 命名空间普查](rtti-namespace-census.md) — 类型记录对应项;计算每个命名空间的多态类,而不是函数
- [RTTI / Vtable普查](../forensics/rtti-vtable-census.md) — 符号表面的数据一半:vtables 和 typeinfo 记录
- [LLVM / MLIR 清单](../forensics/llvm-mlir-manifest.md) — 嵌入式 LLVM 和 MLIR 的版本证据(这里是两个最大的编译器命名空间)
- [嵌入式库图集](../forensics/embedded-library-atlas.md) — 每个库的标识;此页面解决了这些族跨越的absl/Eigen/re2所有者与子串表面
- [取证概述](../forensics/overview.md) — 二进制级事实:build-id、段、该索引所依赖的 `.symtab` 存在