From d380d2877ed45cf1e75a87d822b30e4e1e21e3d4 Mon Sep 17 00:00:00 2001 From: Martin Leitner-Ankerl Date: Sun, 26 Mar 2023 15:19:43 +0200 Subject: [PATCH 1/4] bench: Add benchmark for prevector usage in std::vector --- src/bench/prevector.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp index 59c4af086e..2524e215e4 100644 --- a/src/bench/prevector.cpp +++ b/src/bench/prevector.cpp @@ -80,6 +80,30 @@ static void PrevectorDeserialize(benchmark::Bench& bench) }); } +template +static void PrevectorFillVectorDirect(benchmark::Bench& bench) +{ + bench.run([&] { + std::vector> vec; + for (size_t i = 0; i < 260; ++i) { + vec.emplace_back(); + } + }); +} + + +template +static void PrevectorFillVectorIndirect(benchmark::Bench& bench) +{ + bench.run([&] { + std::vector> vec; + for (size_t i = 0; i < 260; ++i) { + // force allocation + vec.emplace_back(29, T{}); + } + }); +} + #define PREVECTOR_TEST(name) \ static void Prevector##name##Nontrivial(benchmark::Bench& bench) \ { \ @@ -96,3 +120,5 @@ PREVECTOR_TEST(Clear) PREVECTOR_TEST(Destructor) PREVECTOR_TEST(Resize) PREVECTOR_TEST(Deserialize) +PREVECTOR_TEST(FillVectorDirect) +PREVECTOR_TEST(FillVectorIndirect) From 81f67977f543faca2dcc35846f73e2917375ae79 Mon Sep 17 00:00:00 2001 From: Martin Leitner-Ankerl Date: Sun, 26 Mar 2023 15:39:20 +0200 Subject: [PATCH 2/4] util: prevector's move ctor and move assignment is `noexcept` Move operations already are `noexcept`, so add the keyword to the methods. This makes the `PrevectorFillVectorIndirect...` benchmarks about twice as fast on my machine, because otherwise `std::vector` has to use a copy when the vector resizes. --- src/prevector.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/prevector.h b/src/prevector.h index f36cfe4ff6..d3e4b8fd0d 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -264,7 +264,7 @@ public: fill(item_ptr(0), other.begin(), other.end()); } - prevector(prevector&& other) { + prevector(prevector&& other) noexcept { swap(other); } @@ -276,7 +276,7 @@ public: return *this; } - prevector& operator=(prevector&& other) { + prevector& operator=(prevector&& other) noexcept { swap(other); return *this; } From fffc86f49f4eeb811b8438bc1b7f8d9e05882c6f Mon Sep 17 00:00:00 2001 From: Martin Leitner-Ankerl Date: Sun, 26 Mar 2023 15:39:53 +0200 Subject: [PATCH 3/4] test: CScriptCheck is used a lot in std::vector, make sure that's efficient Adds a few static_asserts so CScriptCheck stays is_nothrow_move_assignable, is_nothrow_move_constructible, and is_nothrow_destructible --- src/validation.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/validation.h b/src/validation.h index aba863db09..b6d1995c5b 100644 --- a/src/validation.h +++ b/src/validation.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -331,6 +332,11 @@ public: ScriptError GetScriptError() const { return error; } }; +// CScriptCheck is used a lot in std::vector, make sure that's efficient +static_assert(std::is_nothrow_move_assignable_v); +static_assert(std::is_nothrow_move_constructible_v); +static_assert(std::is_nothrow_destructible_v); + /** Initializes the script-execution cache */ [[nodiscard]] bool InitScriptExecutionCache(size_t max_size_bytes); From bfb9291a8661fe5b26c14ed755cfa89d27c37110 Mon Sep 17 00:00:00 2001 From: Martin Leitner-Ankerl Date: Sun, 26 Mar 2023 14:40:32 +0200 Subject: [PATCH 4/4] util: implement prevector's move ctor & move assignment Using swap() was rather wasteful because it had to copy the whole direct memory data twice. Also, due to the swap() in move assignment the moved-from object might hold on to unused memory for longer than necessary. --- src/prevector.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/prevector.h b/src/prevector.h index d3e4b8fd0d..bcab1ff00c 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -264,8 +264,10 @@ public: fill(item_ptr(0), other.begin(), other.end()); } - prevector(prevector&& other) noexcept { - swap(other); + prevector(prevector&& other) noexcept + : _union(std::move(other._union)), _size(other._size) + { + other._size = 0; } prevector& operator=(const prevector& other) { @@ -277,7 +279,12 @@ public: } prevector& operator=(prevector&& other) noexcept { - swap(other); + if (!is_direct()) { + free(_union.indirect_contents.indirect); + } + _union = std::move(other._union); + _size = other._size; + other._size = 0; return *this; }