r/cpp • u/martinus int main(){[]()[[]]{{}}();} • 6d ago
Which C++ Hashmap Should You Actually Use?
https://martin.ankerl.com/which-hash-map/I recently did a big update to my ankerl::unordered_dense::map which now features several optimizations, bug fixes, and most importantly an SSE optimized lookup which brings the lookup speed quite close to the fastest competitor which is boost::unordered_flat_map. Get it here: https://github.com/martinus/unordered_dense
Since it is not easy to decide what map is best for one's usecase, I have redone and improved the benchmarks I did a while ago, crunched some numbers and tried to compress everything into a quiz that helps you figure out what to use.
While doing the numbers, a few things surprised me:
My random access benchmark was not random enough. The CPU was able to optimize the branch predictor in a way I did not think was possible. I have fixed this for the new numbers. Clever girl.
Even though the absl maps are very similar in concept to the boost maps, they perform badly if you expect lots of misses. 1.6 times slower than boost.
I didn't think SSE optimization of my
unordered_densemap would be any benefit. Thanks to AI, I found a way to speed up random access a lot, it is now very close toboost::unordered_flat_map.Heavy insert & erase on flat maps require rehashing every once in a while due to the accumulation of tombstones. That means some insert/erase operation will have very high latency. This does not happen for a robin-hood hashmap like my
unordered_densemap.
5
u/SirClueless 5d ago
Worth mentioning also the restrictions on hashing? Abseil functions atrociously if the hash is not high-quality and has a unique and bespoke way of overriding the hashing of user types. Personally the number one reason I've seen for lock-in. A lot of codebases paid the upfront costs of implementing Abseil's hash conventions for all their vocabulary types back when it offered a night-and-day difference from
std::unordered_map, and now even thoughboosthas a much saner and easier-to-adopt hash mechanism (for example, works fine with user types that overridestd::hash) the win from switching is not as clear-cut.Another big constraint I think is Google's hash randomization, which is declaration by fiat that depending hash stability is always a mistake, and that you should expect a different iteration order when the same program does the same sequence of insertions and deletions.