RouteCacheDeduplicator — 缓存键与三个代号集合
本页所有地址均适用于
libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64wheel 中的libtpu.so(构建libtpu_lts_20260413_b_RC00,build-id89edbbe81c5b328a958fe628a9f2207d)。该二进制带有完整 C++ 符号(.textVMA == 文件偏移,基址0xe63c000);下文所有地址都是 VMA。解修饰名称、结构体偏移和字符串字面量均已与 IDA 反编译交叉核对。其他版本会有所不同。
摘要
RouteCacheDeduplicator 是把切片形状转换为预烘焙 route-cache blob 的查找表,也是让同一形状的每种轴旋转共享一个烘焙 blob 的机制。它是 InitRouteSolution 在预计算(快速)路径上首先触碰的对象:在从磁盘读取任何 blob 之前,会用拓扑的 {dim_sizes, is_twisted, orientation} 三元组对已填充的 FlatHashMap 做哈希查找。命中会返回该 blob 烘焙时使用的规范标识符,以及把该规范 chip-id 空间重映射到当前拓扑的 vector<proto::Orientation> 旋转。这个旋转向量正是 route-cache-decompress.md 中的 CacheRead 用来给内存映射重新键控的输入。
去重器存在的原因是烘焙 blob 的库存很小:kRouteCacheSet 中只有八个 twisted 形状,但一个物理切片可能是沿 X、Y 或 Z 轴 twisted 的 12x12x24 torus。生产者不会为每个(形状 × 轴)烘焙一个 blob,而是为每个形状烘焙一个规范 blob,并把每个形状的全部四种 orientation(X/Y/Z + 无 orientation 形式)注册为映射键,全部指回同一个规范条目。映射的值是从规范 orientation 恢复被查询 orientation 的 orientation 旋转,也就是 CacheRead 中旋转辅助器应用的几何收益。
本页负责说明去重键(RouteCacheIdentifier)、Find/Insert/UpdateDeduplicator 查找与填充路径,以及按代号集合的选择:无参 singleton 只由 kRouteCacheSet 喂入;按 codename 加键的 GetCacheDeduplicator(int) singleton 则在基础集合之上叠加 k6acc60406RouteCacheSet 或 kViperfishRouteCacheSet。缓存容器(四个 FlatHashMap 和运行时读侧分派)见 toroidal-route-cache.md;Decompress + proto 到 map 的展开见 route-cache-decompress.md;逐轴旋转数学见 route-cache-codec.md。
对重新实现而言,契约是:
- 键 —
RouteCacheIdentifier{ vector<int> dim_sizes; bool is_twisted; optional<proto::Orientation> orientation },通过 Abseilcombine<vector<int>, bool, optional<Orientation>>哈希,其中is_twisted以取反形式(^ 1)参与哈希。 Find— 一次FlatHashMap查找;命中时复制匹配槽位中的规范RouteCacheIdentifier以及值vector<Orientation>旋转;未命中时返回 not-found 标志。UpdateDeduplicator— 对集合中的每个拓扑字符串,先ParseTopologyString,再Insert四个变体(X/Y/Z + NONE),从而让全部旋转注册到同一个规范条目。- 三个集合 —
kRouteCacheSet(基础集合,八个 twisted 形状)、k6acc60406RouteCacheSet、kViperfishRouteCacheSet。无参 singleton 只加载基础集合;6acc60406(TPU7x)和 viperfish singleton 各自加载基础集合外加其代号专属集合。
| 查找 | RouteCacheDeduplicator::Find(const RouteCacheIdentifier&) @ 0x20b59000 |
| 插入(规范化) | RouteCacheDeduplicator::Insert(const RouteCacheIdentifier&) @ 0x20b58340 |
| 填充集合 | (anon)::UpdateDeduplicator(RouteCacheDeduplicator&, SetView<string_view>) @ 0x1fbe3780 |
| 键哈希 | absl::…::combine<vector<int>, bool, optional<proto::Orientation>> @ 0x20b5aa40 |
| 解析形状字符串 | slice_builder::ParseTopologyString(string_view) @ 0x20b480c0 |
| 无参 singleton | (anon)::GetCacheDeduplicator()::deduplicator(guard @ 0x2258fc28) |
| 代号 singleton | (anon)::GetCacheDeduplicator(int)::{pf,vf,gf}_deduplicator |
| 基础集合 | kRouteCacheSet @ 0x22011f88(8 个 {const char*, size_t} 对,.data.rel.ro) |
| 代号集合 | k6acc60406RouteCacheSet, kViperfishRouteCacheSet(ResilientToroidalTopology:: 成员) |
| 来源 | platforms/accel_ssw/deepsea/slice_builder/internal/{toroidal_route_cache,topology}.cc |
对于键字段遍历、Find 槽位复制偏移、Insert 规范化链、UpdateDeduplicator 四变体扇出,以及三个集合的代号分层,置信度为 Confirmed (byte-anchored),均已与 IDA 反编译交叉核对(combine<…> 模板解修饰、FlatHashMapPolicy<RouteCacheIdentifier, pair<…, vector<Orientation>>> 的 find/find_or_prepare_insert 实例化,以及命名每个集合的 MakeCheckFailString 字面量)。两个 LOW / HIGH 项在正文中就地标出(int → ToroidalRouteCacheType 表,以及 code 来源)。
1. 去重键 — RouteCacheIdentifier
该键是一个三字段结构体,用切片形状而不是芯片对来标识唯一 route-cache 条目。具有相同形状、twist 和轴 orientation 的两个切片共享一个条目;逐 (src,dst) 的数据位于该条目指向的 blob 内部。
struct RouteCacheIdentifier { // sizeof ~0x28
std::vector<int> dim_sizes; // +0x0 base / +0x8 end / +0x10 cap
bool is_twisted; // +0x18 (hashed inverted: ^ 1)
std::optional<proto::Orientation> orientation; // +0x1c value (int) / +0x20 has_value (bool)
};
```text
字节偏移已由 `0x20b5aa40` 处的 hash-combine 字段遍历确认。其解修饰模板签名为 `combine<std::vector<int>, bool, std::optional<proto::Orientation>>`,也就是按声明顺序的三个字段:
```c
// 0x20b5aa40 — HashStateBase<MixingHashState>::combine<vector<int>, bool, optional<Orientation>>
// step 1: CRC32 over the dim_sizes int vector (4 bytes / 8 bytes per stride; tail-folded)
v11 = _mm_crc32_u32(v11, *(uint32_t*)v7); // each int element
…
// step 2: is_twisted, hashed INVERTED — (*a3 ^ 1)
v15 = _mm_crc32_u64(HIDWORD(v14), -(int64_t)(uint8_t)*a3);
v16 = _mm_crc32_u64((unsigned)v14, 3LL * (*a3 ^ 1u) - 3); // line 0x20b5aae4
// step 3: the optional<Orientation> { value @ +0, has_value @ +4 }怪癖 —
is_twisted以取反形式(^ 1)哈希。 该布尔值在进入 CRC 混合前会与 1 做 XOR(3 * (is_twisted ^ 1) - 3)。这不影响正确性,因为哈希在内部是一致的;但如果重新实现者对原始布尔值做哈希,会得到不同的哈希表布局,并且如果某天去重表被序列化,将无法与烘焙的去重表互操作。它只是结构体字节如何被送入absl::Hash的产物;只有在需要逐字节哈希兼容时才需要原样复现。
proto::Orientation enum(既用作可选键字段,也用作值向量的元素类型)是一个稠密的 [0,6] protobuf enum:
| 值 | 名称 | 值 | 名称 | |
|---|---|---|---|---|
| 0 | UNKNOWN | 4 | A | |
| 1 | X | 5 | B | |
| 2 | Y | 6 | C | |
| 3 | Z |
UpdateDeduplicator(§3)只会产生 X/Y/Z(1/2/3)和未设置(has_value == false)形式;A/B/C 属于 enum 取值范围,但在此构建中未作为去重 orientation 使用。
2. 去重器映射与 Find @0x20b59000
去重器是单个 Abseil flat hash map,其 policy 已由 Find 内部的 find 实例化从字节层面确认:
absl::container_internal::raw_hash_set<
absl::container_internal::FlatHashMapPolicy<
RouteCacheIdentifier, // KEY
std::pair<RouteCacheIdentifier, std::vector<proto::Orientation>> // VALUE
>>
```text
因此每个槽位存储 `(key, value)`,其中 `value` 本身是 `pair<canonical RouteCacheIdentifier, vector<Orientation>>`。**值中的** `RouteCacheIdentifier` 是烘焙 blob 写入时使用的*规范*(未旋转)形状;`vector<Orientation>` 是把该规范形状映射到被查询形状的旋转。一个形状的四种 orientation 这些多个键都指向同一个规范条目,这就是去重。
`Find` 是一个命中后复制的例程。命中时,它通过复制匹配槽位的值来物化一个小型的 `StatusOr<RouteCacheIdentifier>` 形状结果:
```c
// 0x20b59000 — Find(out <- a1, const RouteCacheIdentifier* key <- a2, hash <- a3)
__int64 Find(out, key, hash) {
if (map.find<RouteCacheIdentifier>(key, hash)) { // FlatHashMapPolicy find → slot ptr v5
// --- copy the slot's CANONICAL RouteCacheIdentifier into out[0x0..0x20] ---
out[0..0x10] = {}; // empty dim_sizes vector header
n = slot->dim_sizes.size(); // [slot+0x30]
if (n) {
out.dim_sizes = new int[n]; // operator new(4*n)
memcpy(out.dim_sizes, slot->dim_sizes, 4*n); // [slot+0x28] → out+0x0
}
out.is_twisted = *(uint8_t*)(slot+0x48); // canonical is_twisted
out.orientation = *(uint64_t*)(slot+0x40); // canonical optional<Orientation>
// --- copy the VALUE vector<Orientation> (the rotation) into out[0x28..0x38] ---
m = slot->orient_vec.size(); // [slot+0x58]
if (m) {
out.orient_vec = new Orientation[m]; // operator new(4*m)
memcpy(out.orient_vec, slot->orient_vec, 4*m); // [slot+0x50] → out+0x28
}
out.ok = 1; // [out+0x40] = found
} else {
out.dim_sizes_hdr = 0;
out.ok = 0; // [out+0x40] = not found
}
return out;
}槽位偏移已从字节确认:规范 dim_sizes 向量位于 slot+0x28/0x30,规范 is_twisted/orientation 位于 slot+0x48/slot+0x40,值中的 vector<Orientation> 旋转位于 slot+0x50/0x58。结果的成功字节是 [out+0x40]。未命中时,结果携带空的 dim_sizes 头和 ok == 0。
说明 — 调用者如何使用两半结果。 返回的规范
RouteCacheIdentifier会送入GetRouteCacheDataPath来计算embed://…/<shape>.binarypb.compressed路径(文件名使用规范形状);返回的vector<Orientation>会送入CacheRead的TopologyRotationHelper(该旋转把已加载的 map 重新键控到被查询拓扑)。重新实现者必须贯通结果的两半,而不只是命中/未命中标志。见route-cache-decompress.md。
3. 填充集合 — UpdateDeduplicator @0x1fbe3780 与 Insert @0x20b58340
UpdateDeduplicator 接受一个 gtl::SetView<string_view>(三个 k*RouteCacheSet 表之一)并注册其中每个形状。对每个拓扑字符串,它先解析形状,然后插入四个 orientation 变体:
// 0x1fbe3780 — UpdateDeduplicator(RouteCacheDeduplicator& dedup, SetView<string_view> set)
for (string_view topo : set) {
auto [dims, is_twisted] = ParseTopologyString(topo); // 0x20b480c0
// four Insert variants — orientation value (v30) X(1) / Y(2) / Z(3), then the NONE form
Insert(dedup, {dims, is_twisted, /*orient=*/X, /*has=*/true}); // v30=1 @0x1fbe385c-ish
Insert(dedup, {dims, is_twisted, /*orient=*/Y, /*has=*/true}); // v30=2
Insert(dedup, {dims, is_twisted, /*orient=*/Z, /*has=*/true}); // v30=3
Insert(dedup, {dims, is_twisted, /*orient=*/NONE, /*has=*/false});// the un-oriented form
}
```text
反编译确认了该扇出:一串 `Insert` 调用中 orientation 字段(`v30`)被设为 `1`、`2`、`1`、`2`,随后是 `3`,外加无 orientation(NONE/`has_value == false`)路径;也就是每个形状注册四个键,让一个规范 blob 服务每种轴旋转。
`ParseTopologyString` @ `0x20b480c0` 会按 `'x'` 拆分字符串(Abseil `ByChar('x')`),用 `safe_strto32_base` 把每段解析进 `dim_sizes`,并通过 `"_twisted"` 后缀测试设置 `is_twisted`(`setne @0x20b481a1`)。因此 `"12x12x24_twisted"` → `dims=[12,12,24], is_twisted=true`,而 2 轴 `"4x8_twisted"` → `dims=[4,8], is_twisted=true`。
`Insert` @ `0x20b58340` 不会原样存储被查询的标识符;它会先**规范化**:
```c
// 0x20b58340 — Insert(RouteCacheDeduplicator& dedup, const RouteCacheIdentifier& id)
auto faulty = ToFaultyDimensions(id); // 0x20b57b40 — vector<FaultyDimension> (stride 12)
sort(faulty); // introsort 0x20b59140
auto canonical = ToRouteCacheIdentifier(faulty); // 0x20b57d80 — the un-rotated canonical shape
auto rotation = ToRotationMapper(canonical, id); // 0x20b58160 — the Orientation rotation list
dedup.find_or_prepare_insert(canonical) = {canonical, rotation}; // 0x20b5b680ToRouteCacheIdentifier 会验证最多只有一个维度是 “faulty”(即最多只有一个轴被旋转);两个会触发 abort 字符串 "Only one faulty orientation is allowed but found two:"(.rodata @ 0xa244c87,带 " v.s." 分隔符 @ 0xa291630)。存储的值对是 {canonical key, rotation vector},也正是 Find 复制出来的内容。
陷阱 — 你查询的键不是被存储的键。
Insert把规范标识符注册为 map 键,但会针对每个 orientation 变体调用一次。四个变体都会规范化到(近似)同一个形状,但携带不同旋转,因此 map 最终会为每个不同规范形状保留一个条目,其值记录如何到达被查询的 orientation。若重新实现者直接存储被查询的RouteCacheIdentifier(跳过ToFaultyDimensions → ToRouteCacheIdentifier),就会破坏旋转收益:CacheRead随后会收到 identity rotation,并错误地给旋转切片的 map 键控。
4. 三个代号集合与逐 singleton 选择
这里有三个拓扑字符串集合,以及消费它们的两类不同 singleton。这是本页的结构核心,而三个集合之间的区别很容易混淆。
4.1 基础集合 — kRouteCacheSet @0x22011f88
TwistedTorusTopology::kRouteCacheSet @ 0x22011f88 是 .data.rel.ro 中的八个 {const char*, size_t} 对,RELA 解析到八个 twisted 形状字符串。(ResilientToroidalTopology::kRouteCacheSet @ 0x21f57380 是一个不同的数据符号,库存更大且不同,包含十二个 {const char*, size_t} 对,覆盖普通和 twisted 形状:4x4x4, 4x4x8, 4x4x8_twisted, 4x4x12, 4x4x16, 4x8x8, 4x8x8_twisted, 8x8x8, 8x8x16_twisted, 8x16x16_twisted, 12x12x12, 12x12x24_twisted(通过重定位 0x21f57380 处的 RELA 指针按字节验证)。§4.3 代号 singleton 叠加的 resilient base set 是这个 12 形状的 ResilientToroidalTopology::kRouteCacheSet,不是下表列出的 8 形状 twisted set。)
| 索引 | str VMA | 长度 | 字符串 | 维度 |
|---|---|---|---|---|
| 0 | 0x87022ba | 16 | 12x12x24_twisted | [12,12,24] |
| 1 | 0x87022a9 | 16 | 12x24x24_twisted | [12,24,24] |
| 2 | 0x87022cb | 16 | 16x16x32_twisted | [16,16,32] |
| 3 | 0x870227c | 13 | 4x4x8_twisted | [4,4,8] |
| 4 | 0x870227e | 11 | 4x8_twisted | [4,8] |
| 5 | 0x870226e | 13 | 4x8x8_twisted | [4,8,8] |
| 6 | 0x8702299 | 15 | 8x16x16_twisted | [8,16,16] |
| 7 | 0x870228a | 14 | 8x8x16_twisted | [8,8,16] |
这八个形状 × 四个 {X,Y,Z,NONE} orientation 变体会注册 32 个去重键(规范化后收敛为每个不同规范形状一个条目)。索引 4 是唯一的 2 轴形状(4x8_twisted)。
4.2 无参 singleton(slice-builder twisted torus)
TwistedTorusTopology::InitRouteSolution @ 0x20b3f7c0 使用无参 GetCacheDeduplicator()(匿名命名空间 singleton,guard @ 0x2258fc28)。其惰性 Create() lambda 遍历 kRouteCacheSet(0x20b3f990 处的循环把 &kRouteCacheSet[v15] / [v15+1] 当作 {ptr,len} 对索引),并调用一次 UpdateDeduplicator。这条路径上的未命中是致命错误:"Cannot find route cache: "(.rodata,见该函数的错误构造器)。这是唯一的默认去重器;它永远不会看到代号集合。
4.3 按代号加键的 singleton(resilient 路径)
ResilientToroidalTopology::InitRouteSolution @ 0x1fbdf8a0 使用 int 重载 GetCacheDeduplicator(int code)。反编译后的 switch (code) 会选择三个匿名命名空间 singleton 之一,并且每个都会惰性填充自己:在基础 kRouteCacheSet 之上叠加其代号集合。集合名称由 CHECK/MakeCheckFailString 字面量按字节确认:
code | singleton | UpdateDeduplicator 调用(按顺序) | ToroidalRouteCacheType |
|---|---|---|---|
1 | pf_deduplicator (pufferfish) | kRouteCacheSet | CACHE_ICI_RESILIENCY_PUFFERFISH (2) |
2 | vf_deduplicator (viperfish) | kRouteCacheSet,然后 kViperfishRouteCacheSet | CACHE_ICI_RESILIENCY_VIPERFISH (3) |
4 | gf_deduplicator (6acc60406 / TPU7x) | kRouteCacheSet,然后 k6acc60406RouteCacheSet | CACHE_ICI_RESILIENCY_6acc60406 (4) |
精确的反编译 CHECK 字符串如下,逐字保留:
"UpdateDeduplicator( **pf_deduplicator, ResilientToroidalTopology::kRouteCacheSet) is OK"
"UpdateDeduplicator( **vf_deduplicator, ResilientToroidalTopology::kRouteCacheSet) is OK"
"UpdateDeduplicator( **vf_deduplicator, ResilientToroidalTopology::kViperfishRouteCacheSet) is OK"
"UpdateDeduplicator( **gf_deduplicator, ResilientToroidalTopology::kRouteCacheSet) is OK"
"UpdateDeduplicator( **gf_deduplicator, ResilientToroidalTopology::k6acc60406RouteCacheSet) is OK"
```text
> **说明 — 代号集合是*加法式*的,不是替换。** Pufferfish 只加载基础 `kRouteCacheSet`。Viperfish 和 `6acc60406` 各自先加载基础集合,**然后**加载其代号专属集合;viperfish 通过 `RouteCacheDeduplicator::CreateResilientViperfish()` 的 `Create` lambda(其 `__policy_func` 在第二次 `UpdateDeduplicator` 前接线),`6acc60406` 通过 `k6acc60406RouteCacheSet`。因此 viperfish 切片既可以命中基础 twisted 形状,*也可以*命中 viperfish 专属形状;它们并不互斥,`6acc60406` 也没有折叠进基础集合。不同符号名、每个 singleton 两次调用的结构,以及逐字 `CHECK` 字面量共同确认了三个集合,其中基础集合为所有集合共享。置信度:HIGH。
`code` 值来自 fault topology(追踪到 `TopologyFaults::GetSymmetryProperty` @ `0x20bfbd60` / `GetMinFaultLocation` @ `0x20bfbc00`)。在 `Find` 之后,同一函数中的*第二个* `switch`(post-Find 分支处的 case `1 → 4 → 2`)会重新推导传给 `GetRouteCacheData` 的 `ToroidalRouteCacheType`(逐代号烘焙 blob)。结构绑定(`pf → PUFFERFISH`、`vf → VIPERFISH`、`gf → 6acc60406`)通过去重 singleton 按字节确认;第二个 switch 的精确 `code → enum` 整数算术是结构性读取,并未穷尽制表。置信度:LOW。
> **说明 — `6acc60406` 是已脱敏的硅代号,不是十六进制地址。** 它在 enum 名称(`CACHE_ICI_RESILIENCY_6acc60406`)、集合符号(`k6acc60406RouteCacheSet`)和资源路径前缀中都作为字符串 token 原样出现。它表示 TPU7x 硅代;应把它当作不透明标识符。
---
## 5. 端到端位置(一张图)
```text
[slice-builder, non-resilient] TwistedTorusTopology::InitRouteSolution # 0x20b3f7c0
dedup = GetCacheDeduplicator() # no-arg singleton; UpdateDeduplicator(kRouteCacheSet)
res = dedup.Find({this.dims, twisted, orient}) # 0x20b59000
if !res.ok: FATAL "Cannot find route cache: "
blob = ToroidalRouteCache::Create(res.canonical, CACHE_TWISTED_TORUS, res.orient_vec)
# → route-cache-decompress.md
[slice-builder, resilient] ResilientToroidalTopology::InitRouteSolution # 0x1fbdf8a0
code = f(TopologyFaults.GetSymmetryProperty / GetMinFaultLocation)
dedup = GetCacheDeduplicator(code) # switch: 1→pf / 2→vf / 4→gf singleton
# pf: UpdateDeduplicator(kRouteCacheSet)
# vf: UpdateDeduplicator(kRouteCacheSet); UpdateDeduplicator(kViperfishRouteCacheSet)
# gf: UpdateDeduplicator(kRouteCacheSet); UpdateDeduplicator(k6acc60406RouteCacheSet)
res = dedup.Find(...) # 0x20b59000
type = {pf→PUFFERFISH, vf→VIPERFISH, gf→6acc60406} # CACHE_ICI_RESILIENCY_*
blob = ToroidalRouteCache::Create(res.canonical, type, res.orient_vec)Find 的两个输出,即规范 RouteCacheIdentifier(命名 blob 文件)和 vector<Orientation>(旋转已加载的 map),就是本页暴露给其余加载路径的完整接口。