主要观点:在搜索引擎quickwit中,术语聚合是关键聚合之一,可用于分析日志。在处理低基数数据(如日志级别)时性能良好,但在处理高基数数据(如 id 字段)时性能不佳,因此研究了可提高性能和减少内存消耗的专用数据结构。在性能测试中发现了奇怪的性能影响,通过深入研究发现与 Rust 中hashbrown
的HashMap
实现有关,特别是在调整大小和内存映射方面。
关键信息:
- 术语聚合可用于统计特定时间段内每个日志级别的出现次数,等价于 SQL 中的
SELECT term, COUNT(*) GROUP BY term WHERE <some complicate where clause>
。 - 在性能测试中,添加未使用的数据集使函数吞吐量增加了 2 倍,但通过
perf
工具检查发现 L1 缓存访问计数器相同,突出的指标是MBr
增加了 25%,但不足以解释 2 倍的吞吐量差异。 hashbrown
的HashMap
在调整大小时使用copy_nonoverlapping
复制数据,导致在复制到新表时触发页错误,将数据加载到内存中。- 分配器(glibc)和操作系统(OS)在内存管理方面有不同的操作,
brk
用于调整数据段的末尾,mmap
用于分配新的内存区域。 - glibc 的分配器根据
M_MMAP_THRESHOLD
和M_TRIM_THRESHOLD
参数决定使用mmap
还是brk
,默认情况下快速版本的M_TRIM_THRESHOLD
设置为 8MB,导致分配器不释放内存回操作系统。 - 与
Vec
不同,Hashmap
需要复制数据,适当使用Hashmap::with_capacity
比Vec::with_capacity
对性能影响更大。
重要细节:
- 性能测试的函数为
test_fx_hash_map
,它简单地统计u32
id 在FxHashMap
中的出现次数。 - 在测试中使用
strace
工具记录系统调用,发现慢版本在每次调整大小时多次调用mmap
和brk
,而快速版本在第一次调整大小时调用mmap
,之后仅使用brk
。 - 可以通过设置环境变量
MALLOC_MMAP_THRESHOLD_
和MALLOC_TRIM_THRESHOLD_
来控制分配器的行为,以复制快速版本的性能。 - 测试代码的仓库为https://github.com/PSeitz/bench_riddle。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。