diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/chunked-vector.hh | 68 | ||||
-rw-r--r-- | src/libutil/types.hh | 59 |
2 files changed, 68 insertions, 59 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); + } +}; +} diff --git a/src/libutil/types.hh b/src/libutil/types.hh index bd1dd8bee..6bcbd7e1d 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -103,63 +103,4 @@ public: Ptr operator->() const { return Ptr(**this); } }; -/* 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); - } -}; - } |