What Building a C++ Benchmarking Suite Taught Me About "Simple" Data Structures
We all know the Big-O complexity of basic data structures. Arrays are O(n) for search. Hash maps are O(1). Linked lists are... well, complicated. But when I set out to build hashbrowns — a C++17 benchmarking suite comparing arrays, linked lists, and hash maps — I discovered that theory and practice are very different beasts. Here's what I learned building this project from scratch, and why you…
In this article, a reporter shares their experience of building a C++17 benchmarking suite to compare arrays, linked lists, and hash maps. The project aimed to be clean and educational, implementing dynamic arrays, linked lists, and hash maps from scratch, and benchmarking insert, search, and remove operations.
The reporter learned several lessons during the project. First, they discovered that polymorphism, while elegant for benchmarking, comes at a cost due to the overhead of virtual function calls. To address this, they kept the clean interface for the benchmarking harness but used templates internally for performance-critical code.
Second, the reporter realized that benchmarking is more challenging than it appears. They addressed issues like warm-up runs, outlier detection using Z-scores, CPU frequency scaling, and the need for more than just the mean in their analysis. They implemented a comprehensive suite that reported median, percentiles, bootstrap confidence intervals, and provided multiple output formats.
Third, the reporter explored growth strategies for dynamic arrays. They implemented four different growth strategies and benchmarked them. The results showed that double the capacity (2.0x growth factor) was the fastest for pure insertion speed, while 1.5x growth used about 25% less memory on average. Other strategies, such as Fibonacci growth and additive growth, had their own trade-offs.
Lastly, the reporter discovered the hidden complexity in hash maps. They implemented two strategies: open addressing and separate chaining. Open addressing won when the load factor was kept low, keys were well-distributed, and mostly lookups were performed. Separate chaining won when there was clustering from bad hash distribution, deletion was common, or the load factor was high. The reporter also faced challenges like tombstones, which can degrade performance if too many exist.
Overall, the reporter emphasizes the importance of benchmarking before optimizing, as theory and practice can be quite different. They encourage readers to consider the trade-offs and hidden complexities when choosing data structures and growth strategies.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — it may contain errors, so check the original before relying on it.