由于项目中需要优化原有std::map红黑树实现的目录树,调研了trie树及其变种Ternary Search Trie的原理,并做了相应的性能测试。
1 支持前缀查找(自动补齐,比如fuse中ls -al) 2 支持高效范围查找 3 支持内存量的减少(类似设计模式中的享元模式)
测试对象:
Std::map 红黑树 来自标准模板库实现
Patricia Trie 压缩trie 来自标准模板库ext实现
Ternary search Tree 三叉树 来自wiki的一个STL-compliant的开源实现
测试结果:
测试1
向stl::map、tst、stl::PAT分别插入1到100W数据,并查找这些数据。
tst
$./otst
start to insert
insert use ms:5530
start to search
search use ms:3692
size = 1000000
内存:
VmSize: 110168 kB
map:
$./omap
start to insert
insert use ms:2712
start to search
search use ms:1625
size = 1000000
内存:
VmSize: 121172 kB
PAT:
$./opat
start to insert
insert use ms:4163
start to search
search use ms:985
size = 1000000
内存:
VmSize: 311796 kB
一, map的插入性能最优,pat的查找性能最优,tst插入查找性能都较差。
二, pat最耗内存,在数据量较大时,tst消耗内存接近map, tst在fuse这种情景下内存理应是最小的。
三, tst和pat自身提供prefix_range这些范围查找函数。
测试2:
向map和tst插入前缀相同的一些字符串,检查内存消耗。
这些字符串大小为100M+2,其中前100M的数据都相同,仅最后一个字符不同。共插入10个这样的数据,分别记录插入第一个和所有都插入后的内存。
tst:
仅插入第一个字符串:
VmSize: 4411012 kB
全插入后:
VmSize: 4308608 kB(猜测是三叉树的rebalance的原因)
map:
仅插入第一个字符串:
VmSize: 216684 kB
全插入后:
VmSize: 933512 kB
总结:当字符串的前缀相同时,tst的公用字符机制可以很有效的节省内存。
总结:
Map的性能适中,但自身不支持范围查找和前缀查找,需要自身代码实现。相对效率较低
PatTrie的查找性能很强,根据测试,并没有相应的共享前缀节点,导致占用内存不理想。
Ternary tree其实是一颗变种的trie树,insert find效率都不高,由于每个字符占用一个节点,而red-black的一个key占用一个节点,相对树的层次会比red-black高(在节点相似度不高的情况下)。但内存和前缀查找均很好的支持。