diff options
author | Théophane Hufschmitt <theophane.hufschmitt@tweag.io> | 2022-04-22 10:01:02 +0200 |
---|---|---|
committer | Théophane Hufschmitt <theophane.hufschmitt@tweag.io> | 2022-04-22 10:01:02 +0200 |
commit | 7ca6fbc8caf30d0f8b0dca9a294022d3e37c4082 (patch) | |
tree | 9524161787378d7b0746bc8c3f0bb625c83c4e8f /src/libutil/chunked-vector.hh | |
parent | 8adaa6acb5a513e010d262386271ef39c418ea7f (diff) |
Move ChunkedVector to its own header
Diffstat (limited to 'src/libutil/chunked-vector.hh')
-rw-r--r-- | src/libutil/chunked-vector.hh | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/src/libutil/chunked-vector.hh b/src/libutil/chunked-vector.hh new file mode 100644 index 000000000..f15af9cd7 --- /dev/null +++ b/src/libutil/chunked-vector.hh @@ -0,0 +1,68 @@ +#pragma once + +#include <bits/stdint-uintn.h> +#include <cstdlib> +#include <vector> +#include <limits> + +namespace nix { + +/* Provides an indexable container like vector<> with memory overhead + guarantees like list<> by allocating storage in chunks of ChunkSize + elements instead of using a contiguous memory allocation like vector<> + does. Not using a single vector that is resized reduces memory overhead + on large data sets by on average (growth factor)/2, mostly + eliminates copies within the vector during resizing, and provides stable + references to its elements. */ +template<typename T, size_t ChunkSize> +class ChunkedVector { +private: + uint32_t size_ = 0; + std::vector<std::vector<T>> chunks; + + /* keep this out of the ::add hot path */ + [[gnu::noinline]] + auto & addChunk() + { + if (size_ >= std::numeric_limits<uint32_t>::max() - ChunkSize) + abort(); + chunks.emplace_back(); + chunks.back().reserve(ChunkSize); + return chunks.back(); + } + +public: + ChunkedVector(uint32_t reserve) + { + chunks.reserve(reserve); + addChunk(); + } + + uint32_t size() const { return size_; } + + std::pair<T &, uint32_t> add(T value) + { + const auto idx = size_++; + auto & chunk = [&] () -> auto & { + if (auto & back = chunks.back(); back.size() < ChunkSize) + return back; + return addChunk(); + }(); + auto & result = chunk.emplace_back(std::move(value)); + return {result, idx}; + } + + const T & operator[](uint32_t idx) const + { + return chunks[idx / ChunkSize][idx % ChunkSize]; + } + + template<typename Fn> + void forEach(Fn fn) const + { + for (const auto & c : chunks) + for (const auto & e : c) + fn(e); + } +}; +} |