Skip to content

Repository files navigation

Containa

A high-performance C++ container library featuring btree_map, small_vectra, and devectra - optimized alternatives to standard containers.

Containers

btree_map / btree_set

A high-performance B-tree based ordered map and set, optimized to outperform absl::btree_map.

small_vectra

A small buffer optimized vector that stores elements inline (no heap allocation) for small sizes.

devectra

A double-ended vector with O(1) amortized push_front and push_back operations.

static_vectra

A fixed-capacity vector with embedded storage - never performs dynamic memory allocation.


btree_map Design

Why B-tree, Not B+ Tree?

We initially implemented both btree_map (B-tree) and bplus_tree_map (B+ tree). After extensive benchmarking, we chose B-tree:

MetricB+ TreeB-tree
Find (int)+8% fasterbaseline
Insert-50% to -70% slowerbaseline
Memory2x (duplicated keys)1x

The Problem with B+ Tree: Our B+ tree used a separate keys[] array for SIMD-accelerated search, plus slots[] for key-value storage. Every insert/erase had to maintain both arrays, causing significant overhead.

Conclusion: 8% find improvement does not justify 50-70% insert penalty.

Key Optimizations

1. SIMD-Accelerated Node Search

Linear SIMD scan outperforms binary search for small node sizes (<64 keys):

PlatformInstruction SetElements/Iteration
x86_64AVX-51216 x int32, 8 x int64
x86_64AVX28 x int32
x86_64SSE24 x int32
ARMNEON4 x int32
ARMSVEHardware-dependent

Why manual loads instead of AVX2 gather?vpgatherdd has 12-20 cycle latency. For stride access (interleaved key-value storage), manual loads with _mm256_set_epi32 are faster.

2. O(1) end() with Cached Rightmost Leaf

// Before: O(log n) tree traversal for each end() call
iterator end() { returntraverse_to_rightmost(); }
// After: O(1) cached pointer
leaf_node* _rightmost_leaf;
iterator end() { returniterator(_rightmost_leaf, _rightmost_leaf->count); }

3. Optimized Node Split with memcpy

// Before: Two loops - copy all, then shift to remove medianfor (i = 0; i < right_count; ++i)
right->slots[i] = std::move(left->slots[mid + i]);
for (i = 0; i < right->count - 1; ++i)
right->slots[i] = std::move(right->slots[i + 1]);
// After: Single memcpy - extract median first, copy remaining directly
Key median_key = std::move(left->slots[mid].first);
std::memcpy(&right->slots[0], &left->slots[mid + 1], right_count * sizeof(storage_type));

4. Prefetch for Tree Traversal

// Prefetch next node before traversing (hides memory latency)__builtin_prefetch(internal->children[pos], 0, 3);
node = internal->children[pos];

5. Three-Way Comparison for Strings

String comparison is expensive. Using three-way comparison (<=>) avoids redundant comparisons:

// Before: Two comparisons
pos = lower_bound(key); // uses <if (pos < count && !(key < slots[pos].key)) // uses < again// After: Single three-way comparisonauto [pos, exact_match] = lower_bound_with_match(key); // uses <=>if (exact_match) return iterator(node, pos);

6. Sorted Insert Fast Path

Sequential insertions (common in bulk loading) skip tree traversal:

if (_comp(_rightmost_leaf->key(_rightmost_leaf->count - 1), key)) {
// Key > max key, append directly to rightmost leafinsert_at_rightmost(key, value);
}

Performance vs Abseil btree_map

Test Environment: Intel i7-10700 @ 2.90GHz, Clang 21.1, -O3 -march=native

Clang + libstdc++

Operation10K int100K int10K string100K string
Sorted Insert3.4x faster3.1x faster2.1x faster1.9x faster
Random Insert1.11x faster1.12x faster1.02x faster1.07x faster
Find1.15x faster1.01x faster1.01x faster1.07x faster
Erase1.22x faster1.09x faster--
Iterate0.89x0.93x2.1x faster2.4x faster

Clang + libc++

Operation10K int100K int10K string100K string
Sorted Insert2.8x faster3.3x faster2.6x faster2.4x faster
Random Insert1.19x faster1.09x faster1.10x faster1.06x faster
Find1.20x faster1.06x faster~1.0x1.02x faster
Erase1.17x faster1.11x faster--
Iterate0.80x0.87x1.4x faster1.5x faster

See benchmark_result.md for detailed results.


Building

Requirements

  • CMake 3.20+
  • C++20 compiler (GCC 10+, Clang 10+, MSVC 2019+)

Basic Build

cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build

Build with Abseil Benchmarks

cmake -B build -DENABLE_ABSL_BENCH=ON
cmake --build build --target btree_bench
./build/bench/btree_bench

Testing

cmake --build build --target containa_test
./build/tests/containa_test

With Sanitizers

# AddressSanitizer
cmake -B build -DENABLE_ASAN=ON -DCMAKE_BUILD_TYPE=Debug
cmake --build build --target containa_test
./build/tests/containa_test
# UndefinedBehaviorSanitizer
cmake -B build -DENABLE_UBSAN=ON -DCMAKE_BUILD_TYPE=Debug

Project Structure

container/
btree_map.hpp # B-tree map implementation
btree_set.hpp # B-tree set implementation
skiplist_map.hpp # Skip list map implementation
small_vectra.hpp # Small buffer optimized vector
devectra.hpp # Double-ended vector
static_vectra.hpp # Fixed-capacity vector
ring_buffer.hpp # Circular buffer
container_base.hpp # Common utilities
...
tests/ # Test suite (doctest)
bench/ # Benchmarks (nanobench)
benchmark_result.md # Detailed benchmark results
tradeoff.md # Design decisions and tradeoffs

License

Apache License 2.0 - See LICENSE file for details.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages