历史快照
本专题页写于包拆分与 FAST-LIVO2 / CustomMsg / 图像输入落地之前,反映当时的单包结构与
五前端状态。已知过时点:目录布局(apps/launch 现属 asuka_ros 包)、共享的
config_sensor.json(外参已改为各前端 odometry 配置的 sensor 块)、ROS 层 acc_scale
等描述、前端数量(现六种,新增 fastlivo)、CustomMsg 与图像输入链路。2026-09 下旬以来:lightning / superodom 前端与 optimization_miao 后端已移除
(现五前端:fastlio / batchlio / superlio / fastlivo / srlivo;smallpointlio 已于 2026-10-23 移除,odometry/10-11 为其历史快照);
freedom 扩展已整体迁出 asuka(移入 nagisa 包);KeyFrame::stamp 统一为扫描
起始时刻;执行器新增连续失败熔断与 wait_for() 排空检测、扩展与执行器有
ok() 健康查询。
现行事实以主文档 (index / architecture / parameters / odometry 等)与源码为准。
title: "ankerl 高性能哈希容器" description: >- unordered_dense.h(88KB)提供 dense hash map/set,被体素地图与近邻结构等高频路径使用;stl.h 为配套工具头。
ankerl 高性能哈希容器
模块定位与职责
src/asuka/thirdparty/ankerl 是 vendored 进仓库的第三方单头库:martinus/unordered_dense v4.8.1(MIT 许可,L1-27),自述为 "A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion"(L3),即基于 robin hood 哈希 + 后移删除的稠密哈希 map/set。目录仅两个文件:
unordered_dense.h(88KB):全部容器实现;stl.h(4KB):配套工具头,本身不含容器逻辑。
在本仓库中,它的消费者集中在高频数据关联路径:SuperLIO 子模块的八叉体素地图 octvoxmap.hpp、哈希近邻查表 hknn_tables.hpp、体素级最近点搜索 voxel_grid_closest.hpp。这些路径每帧点云配准都要做大量体素键查找/插入,因此选型上强调缓存友好的稠密存储,而非 std::unordered_map 的链地址法。
两个文件的分工
stl.h 的职责是环境探测 + 标准头聚合:一次性包含 <array> <cstdint> <functional> <memory> <vector> 等十余个标准头(stl.h L32-47),并集中处理三处平台差异——MINGW64 + GCC≥13 + win32 线程模型下禁用 <memory_resource>(L49-66)、PMR 可用性探测(ANKERL_UNORDERED_DENSE_PMR 指向 std::pmr 或 std::experimental::pmr,L68-76)、MSVC x64 的 _umul128 内建(L78-81)。unordered_dense.h 在非 STD_MODULE 模式下 #include "stl.h"(L93-95)。把标准库包含收敛到独立小头,使平台差异只改一处,也便于编译缓存复用。
主头文件的自顶向下组织
- 版本与 ABI 隔离(L33-45, L116-117):三个版本宏拼出 inline namespace
v4_8_1,不同版本符号天然隔离。 - 硬性门槛(L84-85):C++17 以下直接
#error。 - 平台与优化宏:
PACK(GCC attribute / MSVC pragma pack,L53-59)、异常探测(L62-66)、LIKELY/UNLIKELY(C++20 attribute 或__builtin_expect,L97-114)、UBSAN 无符号溢出检查抑制(L73-82)。 - 错误处理边界(L121-147):三个
noinline终止函数on_error_key_not_found/on_error_bucket_overflow/on_error_too_many_elements。有异常时分别抛out_of_range(at()未命中)、overflow_error(桶数达上限)、out_of_range;无异常构建退化为abort()。注释明确:慢路径不内联,避免拖累其它内联决策。 - 哈希体系(L151 起):
detail::wyhash是 wyhash 精简版——硬编码 seed/secret、去掉大端支持。对外是hash<T>主模板及特化:整型族经ANKERL_UNORDERED_DENSE_HASH_STATICCAST直接static_cast<uint64_t>(bool/char/int/long 等,L388-419);tuple/pair逐元素to64(整型直转、复合类型递归 hash)后mix64混合(L341-385)。is_avalanching嵌套类型配合detect_avalanching(L470-471)标记哈希值是否已充分扩散;is_transparent检测(L473-489)启用异构查找。 - 桶编码(L425-445):
bucket_type::standard是两个uint32_t——m_dist_and_fingerprint(高 3 字节为到原始桶的探测距离,低 1 字节为哈希指纹)与m_value_idx(指向m_values的下标),共 8 字节;bucket_type::bigpack 成 12 字节,m_value_idx升为size_t,突破 4G 元素上限。 - 基础设施(L447-507):
base_table_type_map/base_table_type_set区分 map(有 mapped_type)与 set;has_reserve、is_detected等 enable_if 助手。
segmented_vector:稠密值的物理载体
segmented_vector(L509 起)自述"Very much like std::deque, but faster for indexing":等长块挂进 m_blocks 指针数组,块内元素数为 2 的幂(由 MaxSegmentSizeBytes=4096 与 sizeof(T) 推出,L537-548),寻址仅用移位+掩码 m_data[m_idx >> num_bits][m_idx & mask](L633-639)。与 std::vector 不同,扩容不整体翻倍搬移,只追加新块(L511-513)——这意味着桶表里存的 value_idx 在扩容后依然有效,正是"robin hood 桶表 + 稠密值数组”能正交组合的关键;代价是每次索引多一跳间接。迭代器 iter_t<IsConst> 单模板兼任 iterator/const_iterator,forward 类别(L550-670)。
查找/插入路径
flowchart LR
K["key"] --> H["hash<T> 特化<br/>(wyhash / static_cast / tuple mix64)"]
H --> F["低 8 位 → fingerprint<br/>其余位 → 起始桶下标"]
F --> B["m_buckets 逐桶探测<br/>先比 fingerprint 再比 key"]
B -->|"命中:取 value_idx"| V["m_values (segmented_vector)<br/>等长块 + 移位寻址"]
B -->|"空桶:backward shift<br/>插入并回移腾位"| D["更新 dist_and_fingerprint<br/>高 3 字节 = 探测距离"]
关键节点:所有键进入同一条 wyhash 管线,整型键(体素坐标编码是典型)零成本直转;桶内常驻 1 字节指纹,把绝大多数不相等挡在真实 key 比较之前;头注释点名的 backward shift deletion(L3)使删除不留墓碑、后继前移补位。值本体在 m_values 中稠密排布,遍历整表时按值数组线性扫——体素地图导出/遍历正受益于此。
本页证据覆盖该头前 820 行(宏、哈希、桶、存储层);基于上述组件组装的 table 模板与 map/set 公开别名位于其后,从 base_table_type_map/set 与桶注释中的 m_values 可反推其“桶表 + 稠密值数组"双结构组装方式。
边界条件与扩展点
- 边界:元素数逼近
standard桶uint32下标上限需换big桶类型,越界触发on_error_bucket_overflow;-fno-exceptions构建下at()未命中直接abort(),实时/嵌入式配置需留意;MINGW64 特定组合下 PMR 被静默禁用。 - 扩展:为
hash<T>添加特化即可接入自定义键(充分扩散的哈希加using is_avalanching = void;);Hash/KeyEqual 同时带is_transparent启用异构查找;Allocator 模板参数、MaxSegmentSizeBytes块大小与 PMR 别名可定制内存策略;升级 vendor 版本只需改三个版本宏,inline namespace 自动整体切换。
阅读建议:把本页当工具书,读 SuperLIO 地图层时只需记住三点——桶 8 字节双 32 位、值存 segmented_vector 且下标扩容稳定、无异常构建下错误路径是 abort();其余 80KB 模板细节按需检索。
Sources: src/asuka/thirdparty/ankerl/unordered_dense.h#L1-L230
Sources: src/asuka/thirdparty/ankerl/unordered_dense.h#L340-L560
Sources: src/asuka/thirdparty/ankerl/unordered_dense.h#L509-L820
Sources: src/asuka/thirdparty/ankerl/stl.h#L1-L83
关联源码
src/asuka/thirdparty/ankerl/unordered_dense.hsrc/asuka/thirdparty/ankerl/stl.h