├── .gitignore ├── LICENSE_1_0.txt ├── README.md ├── perf_benchmark.cpp ├── self_test.cpp └── prio_queue.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | #*# 2 | a.out 3 | *~ 4 | *.o 5 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## *prio_queue* 2 | 3 | This is the implementation of a cache friendly priority queue as a [B-heap](https://en.wikipedia.org/wiki/B-heap). 4 | 5 | The workings and some performance observarions are available in a post on the blog [playfulprogramming](http://playfulprogramming.blogspot.se/2015/08/cache-optimizing-priority-queue.html). 6 | 7 | Usage differs slightly from [std::priority_queue](http://en.cppreference.com/w/cpp/container/priority_queue) 8 | 9 | The (simplified) signature is: 10 | 11 | ```Cpp 12 | // in namespace rollbear 13 | template > 14 | class prio_queue 15 | { 16 | public: 17 | prio_queue(Compare const& comp = Compare()); 18 | 19 | using value type = Prio; 20 | using payload_type = Value; 21 | 22 | void push(Prio p, Value v); 23 | std::pair top() const noexcept; 24 | void pop(); 25 | void reschedule_top(Prio); 26 | bool empty() const noexcept; 27 | std::size_t size() const noexcept(); 28 | }; 29 | ``` 30 | 31 | The reason for separate priority field and value data is that it offers much better performance than to have a priority queue over a struct/pair/tuple holding both and compare priority on it. 32 | 33 | It is legal to set `Value` as `void`, in which case `push()` will accept only one parameter, and `top()` will return `Prio const&`, but generally performance will suffer when doing so. 34 | 35 | `miniheap_size` is what you can tweak for optimum performance. How large to set 36 | it depends on the size of the `Prio` data type and the size of the cache lines. 37 | See the above mentioned 38 | [blog post](http://playfulprogramming.blogspot.se/2015/08/cache-optimizing-priority-queue.html) for guidance. 39 | 40 | The real signatures for `push()` uses perfect forwarding. 41 | 42 | `reschedule_top()` is synonymous to `auto v = q.top(); q.pop(); q.push(v);`, but 43 | is usually faster. 44 | 45 | There is an additional allocator parameter. 46 | 47 | If the Prio and Value types have `noexcept` move constructors and assignment, the strong exception guarantee holds, otherwise the weak exception guarantee. 48 | 49 | Self test 50 | --------- 51 | The included self test program relies on two external frame works. 52 | 53 | * [Catch!](https://github.com/philsquared/Catch) 54 | - a header only unit test frame work 55 | * [trompeloeil](https://github.com/rollbear/trompeloeil) 56 | - a header only mocking frame work 57 | 58 | Performance benchmark 59 | --------------------- 60 | The included performance benchmark relies on one external frame work. 61 | 62 | * [tachymeter](https://github.com/rollbear/tachymeter) 63 | - a header only C++14 micro benchmark frame work 64 | -------------------------------------------------------------------------------- /perf_benchmark.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * B-heap priority queue 3 | * 4 | * Copyright Björn Fahller 2015 5 | * 6 | * Use, modification and distribution is subject to the 7 | * Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at 9 | * http://www.boost.org/LICENSE_1_0.txt) 10 | * 11 | * Project home: https://github.com/rollbear/prio_queue 12 | */ 13 | 14 | #include "prio_queue.hpp" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | using namespace std::literals::chrono_literals; 27 | using namespace tachymeter; 28 | using Clock = std::chrono::high_resolution_clock; 29 | using rollbear::prio_queue; 30 | 31 | struct null_obj_t 32 | { 33 | constexpr operator int() const { return 0; } 34 | template 35 | constexpr operator std::unique_ptr() const { return nullptr; } 36 | }; 37 | 38 | static const constexpr null_obj_t null_obj{ }; 39 | static int n[600000]; 40 | auto const test_sizes = powers(seq(1, 2, 5), 1, 100000, 10); 41 | auto const min_test_duration = 1000ms; 42 | 43 | template 44 | struct is_pair 45 | { 46 | struct no {}; 47 | static no func(...); 48 | struct yes {}; 49 | template 50 | static yes func(std::pair const*); 51 | static constexpr bool value = std::is_same()))>::value; 52 | }; 53 | 54 | template 55 | struct has_push 56 | { 57 | struct no {}; 58 | static no func(...); 59 | template 60 | static auto func(U* u) -> decltype(u->push(std::declval()...)); 61 | static constexpr bool value = !std::is_same()))>::value; 62 | }; 63 | 64 | template 65 | inline 66 | std::enable_if_t::value> 67 | add(Q& q, int n) 68 | { 69 | q.push(typename Q::value_type(n, null_obj)); 70 | } 71 | 72 | template 73 | inline 74 | std::enable_if_t::value && has_push::value> 75 | add(Q& q, int n) 76 | { 77 | q.push(n); 78 | } 79 | 80 | 81 | template 82 | inline 83 | std::enable_if_t::value && !has_push::value> 84 | add(Q& q, int n) 85 | { 86 | q.push(n, null_obj); 87 | } 88 | 89 | template 90 | class populate 91 | { 92 | public: 93 | populate(uint64_t) { } 94 | void operator()(uint64_t size) 95 | { 96 | for (uint64_t i = 0; i != size; ++i) 97 | { 98 | add(q, n[i]); 99 | } 100 | } 101 | private: 102 | Q q; 103 | }; 104 | 105 | template 106 | class pop_all 107 | { 108 | public: 109 | pop_all(std::size_t size) 110 | { 111 | for (uint64_t i = 0; i != size; ++i) 112 | { 113 | add(q, n[i]); 114 | } 115 | } 116 | void operator()(uint64_t size) 117 | { 118 | while (size--) 119 | { 120 | q.pop(); 121 | } 122 | } 123 | private: 124 | Q q; 125 | }; 126 | 127 | template 128 | class operate 129 | { 130 | public: 131 | operate(std::size_t size) 132 | { 133 | for (uint64_t i = 0; i != size; ++i) 134 | { 135 | add(q, n[i]); 136 | } 137 | } 138 | void operator()(uint64_t size) 139 | { 140 | auto p = n + size; 141 | auto remaining_cycles = num_cycles; 142 | while (remaining_cycles--) 143 | { 144 | auto elements_to_push = delta_size; 145 | while (elements_to_push--) 146 | { 147 | add(q, *p++); 148 | } 149 | auto elements_to_pop = delta_size; 150 | while (elements_to_pop--) 151 | { 152 | q.pop(); 153 | } 154 | } 155 | } 156 | private: 157 | Q q; 158 | }; 159 | 160 | 161 | template 162 | class reschedule 163 | { 164 | public: 165 | reschedule(std::size_t size) 166 | { 167 | for (uint64_t i = 0; i != size; ++i) 168 | { 169 | add(q, n[i]); 170 | } 171 | } 172 | void operator()(uint64_t size) 173 | { 174 | auto p = n + size; 175 | auto remaining_cycles = num_cycles; 176 | while (remaining_cycles--) 177 | { 178 | q.reschedule_top(*p++); 179 | } 180 | } 181 | private: 182 | Q q; 183 | }; 184 | 185 | template 186 | class pop_push 187 | { 188 | public: 189 | pop_push(std::size_t size) 190 | { 191 | for (uint64_t i = 0; i != size; ++i) 192 | { 193 | add(q, n[i]); 194 | } 195 | } 196 | void operator()(uint64_t size) 197 | { 198 | auto p = n + size; 199 | auto remaining_cycles = num_cycles; 200 | while (remaining_cycles--) 201 | { 202 | q.pop(); 203 | add(q, *p++); 204 | } 205 | } 206 | private: 207 | Q q; 208 | }; 209 | 210 | inline 211 | bool operator<(const std::pair> &lh, 212 | const std::pair> &rh) 213 | { 214 | return lh.first < rh.first; 215 | } 216 | 217 | template 218 | void measure_prio_queue(int argc, char *argv[]) 219 | { 220 | std::ostringstream os; 221 | os << "/tmp/q/" << size; 222 | std::string path = os.str(); 223 | 224 | std::cout << path << '\n'; 225 | 226 | CSV_reporter reporter(path.c_str(), &std::cout); 227 | benchmark benchmark(reporter); 228 | 229 | using qint = prio_queue<8, int, void>; 230 | using qintintp = prio_queue, void>; 231 | using qintptrp = prio_queue>, void>; 232 | using qintp = prio_queue>; 233 | using qintint = prio_queue; 234 | 235 | using std::to_string; 236 | 237 | benchmark.measure>(test_sizes, 238 | "populate prio_queue", 239 | min_test_duration); 240 | benchmark.measure>(test_sizes, 241 | "pop all prio_queue", 242 | min_test_duration); 243 | benchmark.measure>(test_sizes, 244 | "operate prio_queue", 245 | min_test_duration); 246 | 247 | benchmark.measure>(test_sizes, 248 | "populate prio_queue<, void>", 249 | min_test_duration); 250 | benchmark.measure>(test_sizes, 251 | "pop all prio_queue<, void>", 252 | min_test_duration); 253 | benchmark.measure>(test_sizes, 254 | "operate prio_queue<, void>", 255 | min_test_duration); 256 | 257 | benchmark.measure>(test_sizes, 258 | "populate prio_queue<, void>", 259 | min_test_duration); 260 | benchmark.measure>(test_sizes, 261 | "pop all prio_queue<, void>", 262 | min_test_duration); 263 | benchmark.measure>(test_sizes, 264 | "operate prio_queue<, void>", 265 | min_test_duration); 266 | 267 | 268 | benchmark.measure>(test_sizes, 269 | "populate prio_queue", 270 | min_test_duration); 271 | benchmark.measure>(test_sizes, 272 | "pop all prio_queue", 273 | min_test_duration); 274 | benchmark.measure>(test_sizes, 275 | "operate prio_queue", 276 | min_test_duration); 277 | 278 | benchmark.measure>(test_sizes, 279 | "reschedule prio_queue", 280 | min_test_duration); 281 | benchmark.measure>(test_sizes, 282 | "reschedule with pop/push prio_queue", 283 | min_test_duration); 284 | 285 | benchmark.measure>(test_sizes, 286 | "populate prio_queue", 287 | min_test_duration); 288 | benchmark.measure>(test_sizes, 289 | "pop all prio_queue", 290 | min_test_duration); 291 | benchmark.measure>(test_sizes, 292 | "operate prio_queue", 293 | min_test_duration); 294 | benchmark.run(argc, argv); 295 | } 296 | 297 | int main(int argc, char *argv[]) 298 | { 299 | std::random_device rd; 300 | std::mt19937 gen(rd()); 301 | std::uniform_int_distribution<> dist(1, 10000000); 302 | for (auto& i : n) i = dist(gen); 303 | 304 | std::cout << sizeof(int) << ' ' 305 | << sizeof(std::pair>) << '\n'; 306 | 307 | measure_prio_queue<8>(argc, argv); 308 | measure_prio_queue<16>(argc, argv); 309 | measure_prio_queue<32>(argc, argv); 310 | measure_prio_queue<64>(argc, argv); 311 | 312 | 313 | using qint = std::priority_queue; 314 | using qintintp = std::priority_queue>; 315 | using qintptrp = std::priority_queue>>; 316 | 317 | 318 | CSV_reporter reporter("/tmp/q/std", &std::cout); 319 | benchmark benchmark(reporter); 320 | 321 | benchmark.measure>(test_sizes, 322 | "std pop/push", 323 | min_test_duration); 324 | 325 | benchmark.measure>(test_sizes, 326 | "populate priority_queue", 327 | min_test_duration); 328 | benchmark.measure>(test_sizes, 329 | "pop all priority_queue", 330 | min_test_duration); 331 | benchmark.measure>(test_sizes, 332 | "operate priority_queue", 333 | min_test_duration); 334 | 335 | 336 | benchmark.measure>(test_sizes, 337 | "populate priority_queue<>", 338 | min_test_duration); 339 | benchmark.measure>(test_sizes, 340 | "pop all priority_queue<>", 341 | min_test_duration); 342 | benchmark.measure>(test_sizes, 343 | "operate priority_queue<>", 344 | min_test_duration); 345 | 346 | benchmark.measure>(test_sizes, 347 | "populate priority_queue<>", 348 | min_test_duration); 349 | benchmark.measure>(test_sizes, 350 | "pop all priority_queue<>", 351 | min_test_duration); 352 | benchmark.measure>(test_sizes, 353 | "operate priority_queue<>", 354 | min_test_duration); 355 | 356 | benchmark.run(argc, argv); 357 | } 358 | -------------------------------------------------------------------------------- /self_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * B-heap priority queue 3 | * 4 | * Copyright Björn Fahller 2015 5 | * 6 | * Use, modification and distribution is subject to the 7 | * Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at 9 | * http://www.boost.org/LICENSE_1_0.txt) 10 | * 11 | * Project home: https://github.com/rollbear/prio_queue 12 | */ 13 | 14 | 15 | #include "prio_queue.hpp" 16 | #include 17 | 18 | #define CATCH_CONFIG_MAIN 19 | #include 20 | 21 | using A = rollbear::prio_q_internal::heap_heap_addressing<8>; 22 | using V = rollbear::prio_q_internal::skip_vector; 23 | using rollbear::prio_queue; 24 | 25 | TEST_CASE("a default constructed vector is empty", "[vector]") 26 | { 27 | V v; 28 | REQUIRE(v.size() == 0); 29 | REQUIRE(v.empty()); 30 | } 31 | 32 | TEST_CASE("a has size 2 after one push_key", "[vector]") 33 | { 34 | V v; 35 | auto i = v.push_back(1); 36 | REQUIRE(!v.empty()); 37 | REQUIRE(v.size() == 2); 38 | REQUIRE(i == 1); 39 | } 40 | 41 | TEST_CASE("a vector of size 2 becomes empty on pop", "[vector]") 42 | { 43 | V v; 44 | v.push_back(1); 45 | v.pop_back(); 46 | REQUIRE(v.empty()); 47 | REQUIRE(v.size() == 0); 48 | } 49 | 50 | TEST_CASE("push_key indexes skip multiples of 4", "[vector]") 51 | { 52 | V v; 53 | REQUIRE(v.push_back(1) == 1); 54 | REQUIRE(v.push_back(1) == 2); 55 | REQUIRE(v.push_back(1) == 3); 56 | REQUIRE(v.push_back(1) == 5); 57 | REQUIRE(v.push_back(1) == 6); 58 | REQUIRE(v.push_back(1) == 7); 59 | REQUIRE(v.push_back(1) == 9); 60 | } 61 | 62 | TEST_CASE("back refers to last element through push_key and pop", "[vector]") 63 | { 64 | V v; 65 | v.push_back(21); 66 | REQUIRE(v.back() == 21); 67 | v.push_back(20); 68 | REQUIRE(v.back() == 20); 69 | v.push_back(19); 70 | REQUIRE(v.back() == 19); 71 | v.push_back(18); 72 | REQUIRE(v.back() == 18); 73 | v.push_back(17); 74 | REQUIRE(v.back() == 17); 75 | v.pop_back(); 76 | REQUIRE(v.back() == 18); 77 | v.pop_back(); 78 | REQUIRE(v.back() == 19); 79 | v.pop_back(); 80 | REQUIRE(v.back() == 20); 81 | v.pop_back(); 82 | REQUIRE(v.back() == 21); 83 | v.pop_back(); 84 | REQUIRE(v.empty()); 85 | 86 | } 87 | TEST_CASE("block root", "[addressing]") 88 | { 89 | REQUIRE(A::is_block_root(1)); 90 | REQUIRE(A::is_block_root(9)); 91 | REQUIRE(A::is_block_root(17)); 92 | REQUIRE(A::is_block_root(73)); 93 | REQUIRE(!A::is_block_root(2)); 94 | REQUIRE(!A::is_block_root(3)); 95 | REQUIRE(!A::is_block_root(4)); 96 | REQUIRE(!A::is_block_root(7)); 97 | REQUIRE(!A::is_block_root(31)); 98 | } 99 | 100 | TEST_CASE("block leaf", "[addressing]") 101 | { 102 | REQUIRE(!A::is_block_leaf(1)); 103 | REQUIRE(!A::is_block_leaf(2)); 104 | REQUIRE(!A::is_block_leaf(3)); 105 | REQUIRE(A::is_block_leaf(4)); 106 | REQUIRE(A::is_block_leaf(5)); 107 | REQUIRE(A::is_block_leaf(6)); 108 | REQUIRE(A::is_block_leaf(7)); 109 | REQUIRE(A::is_block_leaf(28)); 110 | REQUIRE(A::is_block_leaf(29)); 111 | REQUIRE(A::is_block_leaf(30)); 112 | REQUIRE(!A::is_block_leaf(257)); 113 | REQUIRE(A::is_block_leaf(255)); 114 | } 115 | TEST_CASE("Obtaining child", "[addressing]") 116 | { 117 | REQUIRE(A::child_of(1) == 2); 118 | REQUIRE(A::child_of(2) == 4); 119 | REQUIRE(A::child_of(3) == 6); 120 | REQUIRE(A::child_of(4) == 9); 121 | REQUIRE(A::child_of(31) == 249); 122 | } 123 | 124 | TEST_CASE("Obtaining parent", "[addressing]") 125 | { 126 | REQUIRE(A::parent_of(2) == 1); 127 | REQUIRE(A::parent_of(3) == 1); 128 | REQUIRE(A::parent_of(6) == 3); 129 | REQUIRE(A::parent_of(7) == 3); 130 | REQUIRE(A::parent_of(9) == 4); 131 | REQUIRE(A::parent_of(17) == 4); 132 | REQUIRE(A::parent_of(33) == 5); 133 | REQUIRE(A::parent_of(29) == 26); 134 | REQUIRE(A::parent_of(1097) == 140); 135 | } 136 | 137 | TEST_CASE("a default constructed queue is empty", "[empty]") 138 | { 139 | prio_queue<16, int, void> q; 140 | REQUIRE(q.empty()); 141 | REQUIRE(q.size() == 0); 142 | } 143 | 144 | TEST_CASE("an empty queue is not empty when one element is inserted", "[empty]") 145 | { 146 | prio_queue<16, int, void> q; 147 | q.push(1); 148 | REQUIRE(!q.empty()); 149 | REQUIRE(q.size() == 1); 150 | } 151 | 152 | TEST_CASE("a queue with one element has it on top", "[single element]") 153 | { 154 | prio_queue<16, int, void> q; 155 | q.push(8); 156 | REQUIRE(q.top() == 8); 157 | } 158 | 159 | TEST_CASE("a queue with one element becomes empty when popped", 160 | "[single element],[empty]") 161 | { 162 | prio_queue<16, int, void> q; 163 | q.push(9); 164 | q.pop(); 165 | REQUIRE(q.empty()); 166 | REQUIRE(q.size() == 0); 167 | } 168 | 169 | TEST_CASE("insert sorted stays sorted", "[dead]") 170 | { 171 | prio_queue<16, int, void> q; 172 | q.push(1); 173 | q.push(2); 174 | q.push(3); 175 | q.push(4); 176 | q.push(5); 177 | q.push(6); 178 | q.push(7); 179 | q.push(8); 180 | REQUIRE(q.top() == 1); 181 | q.pop(); 182 | REQUIRE(q.top() == 2); 183 | q.pop(); 184 | REQUIRE(q.top() == 3); 185 | q.pop(); 186 | REQUIRE(q.top() == 4); 187 | q.pop(); 188 | REQUIRE(q.top() == 5); 189 | q.pop(); 190 | REQUIRE(q.top() == 6); 191 | q.pop(); 192 | REQUIRE(q.top() == 7); 193 | q.pop(); 194 | REQUIRE(q.top() == 8); 195 | q.pop(); 196 | REQUIRE(q.empty()); 197 | } 198 | 199 | TEST_CASE("key value pairs go in tandem", "[nontrivial]") 200 | { 201 | struct P 202 | { 203 | int a; 204 | int b; 205 | bool operator==(const std::pair& rh) const 206 | { 207 | return a == rh.first && b == rh.second; 208 | } 209 | }; 210 | prio_queue<16, int, int> q; 211 | q.push(3, -3); 212 | q.push(4, -4); 213 | q.push(8, -8); 214 | q.push(1, -1); 215 | q.push(22, -22); 216 | q.push(23, -23); 217 | q.push(16, -16); 218 | q.push(9, -9); 219 | q.push(25, -25); 220 | q.push(20, -20); 221 | q.push(10, -10); 222 | q.push(5, -5); 223 | q.push(11, -11); 224 | q.push(12, -12); 225 | q.push(19, -19); 226 | q.push(2, -2); 227 | 228 | REQUIRE((P{1, -1}) == q.top()); 229 | q.pop(); 230 | REQUIRE((P{2, -2}) == q.top()); 231 | q.pop(); 232 | REQUIRE((P{3, -3}) == q.top()); 233 | q.pop(); 234 | REQUIRE((P{4, -4}) == q.top()); 235 | q.pop(); 236 | REQUIRE((P{5, -5}) == q.top()); 237 | q.pop(); 238 | REQUIRE((P{8, -8}) == q.top()); 239 | q.pop(); 240 | REQUIRE((P{9, -9}) == q.top()); 241 | q.pop(); 242 | REQUIRE((P{10, -10}) == q.top()); 243 | q.pop(); 244 | REQUIRE((P{11, -11}) == q.top()); 245 | q.pop(); 246 | REQUIRE((P{12, -12}) == q.top()); 247 | q.pop(); 248 | REQUIRE((P{16, -16}) == q.top()); 249 | q.pop(); 250 | REQUIRE((P{19, -19}) == q.top()); 251 | q.pop(); 252 | REQUIRE((P{20, -20}) == q.top()); 253 | q.pop(); 254 | REQUIRE((P{22, -22}) == q.top()); 255 | q.pop(); 256 | REQUIRE((P{23, -23}) == q.top()); 257 | q.pop(); 258 | REQUIRE((P{25, -25}) == q.top()); 259 | q.pop(); 260 | REQUIRE(q.empty()); 261 | 262 | } 263 | 264 | TEST_CASE("key value pairs can have complex value type", "[nontrivial]") 265 | { 266 | prio_queue<16, int, std::unique_ptr> q; 267 | q.push(2, nullptr); 268 | q.push(1, nullptr); 269 | REQUIRE(q.top().first == 1); 270 | q.pop(); 271 | REQUIRE(q.top().first == 2); 272 | q.pop(); 273 | REQUIRE(q.empty()); 274 | } 275 | TEST_CASE("randomly inserted elements are popped sorted", "heap") 276 | { 277 | prio_queue<16, int, void> q; 278 | std::random_device rd; 279 | std::mt19937 gen(rd()); 280 | std::uniform_int_distribution<> dist(1,100000); 281 | int n[36000]; 282 | for (auto& i : n) 283 | { 284 | i = dist(gen); 285 | q.push(i); 286 | } 287 | 288 | REQUIRE(!q.empty()); 289 | REQUIRE(q.size() == 36000); 290 | std::sort(std::begin(n), std::end(n)); 291 | for (auto i : n) 292 | { 293 | REQUIRE(q.top() == i); 294 | q.pop(); 295 | } 296 | REQUIRE(q.empty()); 297 | } 298 | 299 | TEST_CASE("reschedule top with highest prio leaves order unchanged", "heap") 300 | { 301 | prio_queue<4, int, int*> q; 302 | // 0 1 2 3 4 5 6 7 8 303 | int nums[] = { 32, 1, 88, 16, 9, 11, 3, 22, 23 }; 304 | for (auto& i : nums) q.push(i, &i); 305 | REQUIRE(q.top().first == 1); 306 | REQUIRE(q.top().second == &nums[1]); 307 | REQUIRE(*q.top().second == 1); 308 | 309 | q.reschedule_top(2); 310 | 311 | REQUIRE(q.top().first == 2); 312 | REQUIRE(q.top().second == &nums[1]); 313 | q.pop(); 314 | REQUIRE(q.top().first == 3); 315 | REQUIRE(q.top().second == &nums[6]); 316 | q.pop(); 317 | REQUIRE(q.top().first == 9); 318 | REQUIRE(q.top().second == &nums[4]); 319 | q.pop(); 320 | REQUIRE(q.top().first == 11); 321 | REQUIRE(q.top().second == &nums[5]); 322 | q.pop(); 323 | REQUIRE(q.top().first == 16); 324 | REQUIRE(q.top().second == &nums[3]); 325 | q.pop(); 326 | REQUIRE(q.top().first == 22); 327 | REQUIRE(q.top().second == &nums[7]); 328 | q.pop(); 329 | REQUIRE(q.top().first == 23); 330 | REQUIRE(q.top().second == &nums[8]); 331 | q.pop(); 332 | REQUIRE(q.top().first == 32); 333 | REQUIRE(q.top().second == &nums[0]); 334 | q.pop(); 335 | REQUIRE(q.top().first == 88); 336 | REQUIRE(q.top().second == &nums[2]); 337 | q.pop(); 338 | REQUIRE(q.empty()); 339 | } 340 | 341 | TEST_CASE("reschedule to mid range moves element to correct place", "heap") 342 | { 343 | prio_queue<4, int, int*> q; 344 | // 0 1 2 3 4 5 6 7 8 345 | int nums[] = { 32, 1, 88, 16, 9, 11, 3, 22, 23 }; 346 | for (auto& i : nums) q.push(i, &i); 347 | REQUIRE(q.top().first == 1); 348 | REQUIRE(q.top().second == &nums[1]); 349 | REQUIRE(*q.top().second == 1); 350 | 351 | q.reschedule_top(12); 352 | 353 | REQUIRE(q.top().first == 3); 354 | REQUIRE(q.top().second == &nums[6]); 355 | q.pop(); 356 | REQUIRE(q.top().first == 9); 357 | REQUIRE(q.top().second == &nums[4]); 358 | q.pop(); 359 | REQUIRE(q.top().first == 11); 360 | REQUIRE(q.top().second == &nums[5]); 361 | q.pop(); 362 | REQUIRE(q.top().first == 12); 363 | REQUIRE(q.top().second == &nums[1]); 364 | q.pop(); 365 | REQUIRE(q.top().first == 16); 366 | REQUIRE(q.top().second == &nums[3]); 367 | q.pop(); 368 | REQUIRE(q.top().first == 22); 369 | REQUIRE(q.top().second == &nums[7]); 370 | q.pop(); 371 | REQUIRE(q.top().first == 23); 372 | REQUIRE(q.top().second == &nums[8]); 373 | q.pop(); 374 | REQUIRE(q.top().first == 32); 375 | REQUIRE(q.top().second == &nums[0]); 376 | q.pop(); 377 | REQUIRE(q.top().first == 88); 378 | REQUIRE(q.top().second == &nums[2]); 379 | q.pop(); 380 | REQUIRE(q.empty()); 381 | } 382 | 383 | TEST_CASE("reschedule to last moves element to correct place", "heap") 384 | { 385 | prio_queue<4, int, int*> q; 386 | // 0 1 2 3 4 5 6 7 8 387 | int nums[] = { 32, 1, 88, 16, 9, 11, 3, 22, 23 }; 388 | for (auto& i : nums) q.push(i, &i); 389 | REQUIRE(q.top().first == 1); 390 | REQUIRE(q.top().second == &nums[1]); 391 | REQUIRE(*q.top().second == 1); 392 | 393 | q.reschedule_top(89); 394 | 395 | REQUIRE(q.top().first == 3); 396 | REQUIRE(q.top().second == &nums[6]); 397 | q.pop(); 398 | REQUIRE(q.top().first == 9); 399 | REQUIRE(q.top().second == &nums[4]); 400 | q.pop(); 401 | REQUIRE(q.top().first == 11); 402 | REQUIRE(q.top().second == &nums[5]); 403 | q.pop(); 404 | REQUIRE(q.top().first == 16); 405 | REQUIRE(q.top().second == &nums[3]); 406 | q.pop(); 407 | REQUIRE(q.top().first == 22); 408 | REQUIRE(q.top().second == &nums[7]); 409 | q.pop(); 410 | REQUIRE(q.top().first == 23); 411 | REQUIRE(q.top().second == &nums[8]); 412 | q.pop(); 413 | REQUIRE(q.top().first == 32); 414 | REQUIRE(q.top().second == &nums[0]); 415 | q.pop(); 416 | REQUIRE(q.top().first == 88); 417 | REQUIRE(q.top().second == &nums[2]); 418 | q.pop(); 419 | REQUIRE(q.top().first == 89); 420 | REQUIRE(q.top().second == &nums[1]); 421 | q.pop(); 422 | REQUIRE(q.empty()); 423 | } 424 | 425 | TEST_CASE("reschedule top of 2 elements to last", "[heap]") 426 | { 427 | prio_queue<8, int, void> q; 428 | q.push(1); 429 | q.push(2); 430 | REQUIRE(q.top() == 1); 431 | q.reschedule_top(3); 432 | REQUIRE(q.top() == 2); 433 | } 434 | 435 | TEST_CASE("reschedule top of 3 elements left to 2nd", "[heap]") 436 | { 437 | prio_queue<8, int, void> q; 438 | q.push(1); 439 | q.push(2); 440 | q.push(4); 441 | REQUIRE(q.top() == 1); 442 | q.reschedule_top(3); 443 | REQUIRE(q.top() == 2); 444 | } 445 | 446 | TEST_CASE("reschedule top of 3 elements right to 2nd", "[heap]") 447 | { 448 | prio_queue<8, int, void> q; 449 | q.push(1); 450 | q.push(4); 451 | q.push(2); 452 | REQUIRE(q.top() == 1); 453 | q.reschedule_top(3); 454 | REQUIRE(q.top() == 2); 455 | } 456 | 457 | 458 | TEST_CASE("reschedule top random gives same resultas pop/push", "[heap]") 459 | { 460 | std::random_device rd; 461 | std::mt19937 gen(rd()); 462 | std::uniform_int_distribution dist(1,100000); 463 | 464 | prio_queue<8, unsigned, void> pq; 465 | std::priority_queue, std::greater<>> stdq; 466 | 467 | for (size_t outer = 0; outer < 100U; ++outer) 468 | { 469 | unsigned num = gen(); 470 | pq.push(num); 471 | stdq.push(num); 472 | for (size_t inner = 0; inner < 100; ++inner) 473 | { 474 | unsigned newval = gen(); 475 | pq.reschedule_top(newval); 476 | stdq.pop(); 477 | stdq.push(newval); 478 | auto n = pq.top(); 479 | auto sn = stdq.top(); 480 | REQUIRE(sn == n); 481 | } 482 | } 483 | } 484 | 485 | struct ptr_cmp 486 | { 487 | template 488 | bool operator()(T const& lh, T const& rh) const { return *lh < *rh;} 489 | }; 490 | 491 | TEST_CASE("unique ptrs are sorted with custom compare", "[nontrivial]") 492 | { 493 | prio_queue<8, std::unique_ptr, void, ptr_cmp> q; 494 | for (int i = 255; i >= 0; --i) 495 | { 496 | q.push(std::make_unique(i)); 497 | } 498 | 499 | for (int i = 0; i < 256; ++i) 500 | { 501 | REQUIRE(*q.top() == i); 502 | q.pop(); 503 | } 504 | REQUIRE(q.empty()); 505 | } 506 | 507 | unsigned obj_count; 508 | unsigned copy_count; 509 | unsigned move_throw_count; 510 | unsigned copy_throw_count; 511 | struct traced_throwing_move 512 | { 513 | traced_throwing_move(int n_) : n{n_} { ++obj_count; } 514 | traced_throwing_move(const traced_throwing_move& rh) :n{rh.n} { if (--copy_throw_count == 0) throw 2; ++obj_count; ++copy_count; } 515 | traced_throwing_move(traced_throwing_move&& rh) : n{rh.n} { if (--move_throw_count == 0) throw 3; ++obj_count; rh.n = -rh.n;} 516 | ~traced_throwing_move() { --obj_count;} 517 | traced_throwing_move& operator=(const traced_throwing_move& rh) { n = rh.n;return *this; } 518 | int n; 519 | bool operator<(const traced_throwing_move& rh) const { return n < rh.n;} 520 | }; 521 | 522 | TEST_CASE("throwable move ctor causes copies", "[nontrivial]") 523 | { 524 | obj_count = 0; 525 | move_throw_count = 0; 526 | copy_throw_count = 0; 527 | copy_count = 0; 528 | { 529 | prio_queue<16, traced_throwing_move, void> q; 530 | for (int i = 0; i < 15*16; ++i) 531 | { 532 | q.push(500 - i); 533 | } 534 | REQUIRE(obj_count == 15*16); 535 | REQUIRE(copy_count == 0); 536 | q.push(100); 537 | REQUIRE(obj_count == 15*16 + 1); 538 | REQUIRE(copy_count == 15*16); 539 | } 540 | 541 | REQUIRE(obj_count == 0); 542 | REQUIRE(copy_count == 15*16); 543 | } 544 | 545 | TEST_CASE("throwing move allows safe destruction", "[nontrivial") 546 | { 547 | obj_count = 0; 548 | move_throw_count = 18; 549 | copy_throw_count = 0; 550 | copy_count = 0; 551 | bool too_soon = true; 552 | try { 553 | prio_queue<16, traced_throwing_move, void> q; 554 | for (int i = 0; i < 17; ++i) 555 | { 556 | q.push(i); 557 | } 558 | too_soon = false; 559 | q.push(18); 560 | FAIL("didn't throw"); 561 | } 562 | catch (int) 563 | { 564 | } 565 | REQUIRE_FALSE(too_soon); 566 | REQUIRE(obj_count == 0); 567 | } 568 | 569 | TEST_CASE("throwing copy allows safe destruction", "[nontrivial]") 570 | { 571 | obj_count = 0; 572 | move_throw_count = 0; 573 | copy_throw_count = 15*15; 574 | copy_count = 0; 575 | std::cout << "begin\n"; 576 | bool too_soon = true; 577 | try { 578 | prio_queue<16, traced_throwing_move, void> q; 579 | for (int i = 0; i < 15*16; ++i) 580 | { 581 | q.push(i); 582 | } 583 | too_soon = false; 584 | q.push(1000); 585 | FAIL("didn't throw"); 586 | } 587 | catch (int) 588 | { 589 | } 590 | REQUIRE_FALSE(too_soon); 591 | REQUIRE(obj_count == 0); 592 | } 593 | -------------------------------------------------------------------------------- /prio_queue.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * B-heap priority queue 3 | * 4 | * Copyright Björn Fahller 2015 5 | * 6 | * Use, modification and distribution is subject to the 7 | * Boost Software License, Version 1.0. (See accompanying 8 | * file LICENSE_1_0.txt or copy at 9 | * http://www.boost.org/LICENSE_1_0.txt) 10 | * 11 | * Project home: https://github.com/rollbear/prio_queue 12 | */ 13 | 14 | #ifndef ROLLBEAR_PRIO_QUEUE_HPP 15 | #define ROLLBEAR_PRIO_QUEUE_HPP 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | #ifdef __GNUC__ 24 | #define rollbear_prio_q_likely(x) __builtin_expect(!!(x), 1) 25 | #define rollbear_prio_q_unlikely(x) __builtin_expect(!!(x), 0) 26 | #else 27 | #define rollbear_prio_q_likely(x) x 28 | #define rollbear_prio_q_unlikely(x) x 29 | #endif 30 | 31 | namespace rollbear 32 | { 33 | 34 | namespace prio_q_internal 35 | { 36 | template > 38 | class skip_vector : private Allocator 39 | { 40 | using A = std::allocator_traits; 41 | static constexpr std::size_t block_mask = block_size - 1; 42 | static_assert((block_size & block_mask) == 0U, "block size must be 2^n"); 43 | public: 44 | skip_vector() noexcept; 45 | explicit skip_vector(Allocator const &alloc) 46 | noexcept(std::is_nothrow_copy_constructible::value); 47 | skip_vector(skip_vector &&v) noexcept; 48 | 49 | ~skip_vector() noexcept(std::is_nothrow_destructible::value); 50 | 51 | T &operator[](std::size_t idx) noexcept; 52 | T const &operator[](std::size_t idx) const noexcept; 53 | 54 | T &back() noexcept; 55 | T const &back() const noexcept; 56 | 57 | template 58 | std::size_t push_back(U &&u); 59 | 60 | void pop_back() noexcept(std::is_nothrow_destructible::value); 61 | 62 | bool empty() const noexcept; 63 | std::size_t size() const noexcept; 64 | private: 65 | template 66 | std::enable_if_t::value && std::is_trivial::value> 67 | destroy() noexcept { } 68 | 69 | template 70 | std::enable_if_t::value || !std::is_trivial::value> 71 | destroy() noexcept(std::is_nothrow_destructible::value); 72 | 73 | template 74 | std::size_t grow(U &&u); 75 | 76 | template 77 | std::enable_if_t::value && std::is_trivial::value> 78 | move_to(T const *b, std::size_t s, T *ptr) noexcept; 79 | 80 | template 81 | std::enable_if_t< 82 | !(std::is_standard_layout::value && std::is_trivial::value) 83 | && std::is_nothrow_move_constructible::value> 84 | move_to(T *b, std::size_t s, T *ptr) 85 | noexcept(std::is_nothrow_destructible::value); 86 | 87 | 88 | template 89 | std::enable_if_t::value> 90 | move_to(T const *b, std::size_t s, T *ptr) 91 | noexcept(std::is_nothrow_copy_constructible::value 92 | && std::is_nothrow_destructible::value); 93 | 94 | T *m_ptr = nullptr; 95 | std::size_t m_end = 0; 96 | std::size_t m_storage_size = 0; 97 | }; 98 | 99 | 100 | template 101 | skip_vector 102 | ::skip_vector() noexcept 103 | : skip_vector(Allocator()) 104 | { 105 | } 106 | 107 | template 108 | skip_vector 109 | ::skip_vector(Allocator const &alloc) noexcept(std::is_nothrow_copy_constructible< 110 | T>::value) 111 | : Allocator(alloc) 112 | { 113 | } 114 | 115 | template 116 | skip_vector 117 | ::skip_vector(skip_vector &&v) noexcept 118 | : m_ptr(v.m_ptr) 119 | , m_end(v.m_end) 120 | , m_storage_size(v.m_storage_size) 121 | { 122 | v.m_ptr = nullptr; 123 | } 124 | 125 | 126 | template 127 | skip_vector:: 128 | ~skip_vector() noexcept(std::is_nothrow_destructible::value) 129 | { 130 | if (m_ptr) 131 | { 132 | destroy(); 133 | A::deallocate(*this, m_ptr, m_storage_size); 134 | } 135 | } 136 | 137 | template 138 | T & 139 | skip_vector:: 140 | operator[](std::size_t idx) noexcept 141 | { 142 | assert(idx < m_end); 143 | assert((idx & block_mask) != 0); 144 | return m_ptr[idx]; 145 | } 146 | 147 | template 148 | T const & 149 | skip_vector:: 150 | operator[](std::size_t idx) const noexcept 151 | { 152 | assert(idx < m_end); 153 | assert((idx & block_mask) != 0); 154 | return m_ptr[idx]; 155 | } 156 | 157 | template 158 | T & 159 | skip_vector:: 160 | back() noexcept 161 | { 162 | assert(!empty()); 163 | return m_ptr[m_end - 1]; 164 | } 165 | template 166 | T const & 167 | skip_vector:: 168 | back() const noexcept 169 | { 170 | assert(!empty()); 171 | return m_ptr[m_end - 1]; 172 | } 173 | 174 | template 175 | template 176 | std::enable_if_t::value || !std::is_trivial::value> 177 | skip_vector:: 178 | destroy() noexcept(std::is_nothrow_destructible::value) 179 | { 180 | auto i = m_end; 181 | while (rollbear_prio_q_unlikely(i-- != 0)) 182 | { 183 | if (rollbear_prio_q_likely(i & block_mask)) 184 | { 185 | A::destroy(*this, m_ptr + i); 186 | } 187 | } 188 | } 189 | 190 | template 191 | template 192 | std::size_t 193 | skip_vector:: 194 | push_back(U &&u) 195 | { 196 | if (rollbear_prio_q_likely(m_end & block_mask)) 197 | { 198 | A::construct(*this, m_ptr + m_end, std::forward(u)); 199 | return m_end++; 200 | 201 | } 202 | if (rollbear_prio_q_unlikely(m_end == m_storage_size)) 203 | { 204 | return grow(std::forward(u)); 205 | } 206 | m_end++; 207 | A::construct(*this, m_ptr + m_end, std::forward(u)); 208 | return m_end++; 209 | } 210 | 211 | template 212 | void 213 | skip_vector:: 214 | pop_back() noexcept(std::is_nothrow_destructible::value) 215 | { 216 | assert(m_end); 217 | A::destroy(*this, m_ptr + --m_end); 218 | m_end -= (m_end & block_mask) == 1; 219 | } 220 | 221 | template 222 | template 223 | std::size_t 224 | skip_vector:: 225 | grow(U &&u) 226 | { 227 | auto desired_size = m_storage_size ? m_storage_size * 2 : block_size * 16; 228 | auto ptr = A::allocate(*this, desired_size, m_ptr); 229 | std::size_t idx = 0; 230 | try 231 | { 232 | A::construct(*this, ptr + m_end + 1, std::forward(u)); 233 | idx = m_end + 1; 234 | if (m_storage_size) 235 | { 236 | move_to(m_ptr, m_end, ptr); 237 | A::deallocate(*this, m_ptr, m_storage_size); 238 | } 239 | m_ptr = ptr; 240 | m_storage_size = desired_size; 241 | m_end = idx + 1; 242 | return idx; 243 | } 244 | catch (...) 245 | { 246 | if (idx != 0) A::destroy(*this, ptr + idx); 247 | A::deallocate(*this, ptr, m_storage_size); 248 | throw; 249 | } 250 | } 251 | 252 | template 253 | template 254 | std::enable_if_t::value && std::is_trivial::value> 255 | skip_vector:: 256 | move_to(T const *b, std::size_t s, T *ptr) noexcept 257 | { 258 | std::copy(b, b + s, ptr); 259 | } 260 | 261 | template 262 | template 263 | std::enable_if_t< 264 | !(std::is_standard_layout::value && std::is_trivial::value) 265 | && std::is_nothrow_move_constructible::value> 266 | skip_vector:: 267 | move_to(T *b, 268 | std::size_t s, 269 | T *ptr) noexcept(std::is_nothrow_destructible::value) 270 | { 271 | for (std::size_t i = 1; rollbear_prio_q_likely(i < s); ++i) 272 | { 273 | if (rollbear_prio_q_likely(i & block_mask)) 274 | { 275 | A::construct(*this, ptr + i, std::move(b[i])); 276 | A::destroy(*this, b + i); 277 | } 278 | } 279 | } 280 | 281 | template 282 | template 283 | std::enable_if_t::value> 284 | skip_vector:: 285 | move_to(T const *b, std::size_t s, T *ptr) noexcept( 286 | std::is_nothrow_copy_constructible::value 287 | && std::is_nothrow_destructible::value) 288 | { 289 | std::size_t i; 290 | try 291 | { 292 | for (i = 1; rollbear_prio_q_likely(i != s); ++i) 293 | { 294 | if (rollbear_prio_q_likely(i & block_mask)) 295 | { 296 | A::construct(*this, ptr + i, b[i]); 297 | } 298 | } 299 | while (rollbear_prio_q_likely(i--)) 300 | { 301 | if (rollbear_prio_q_likely(i & block_mask)) 302 | { 303 | A::destroy(*this, b + i); 304 | } 305 | } 306 | } 307 | catch (...) 308 | { 309 | while (rollbear_prio_q_likely(i--)) 310 | { 311 | if (rollbear_prio_q_likely(i & block_mask)) 312 | { 313 | A::destroy(*this, ptr + i); 314 | } 315 | } 316 | throw; 317 | } 318 | } 319 | 320 | template 321 | bool 322 | skip_vector::empty() const noexcept 323 | { 324 | return size() == 0; 325 | } 326 | 327 | template 328 | std::size_t 329 | skip_vector::size() const noexcept 330 | { 331 | return m_end; 332 | } 333 | 334 | template 335 | struct heap_heap_addressing 336 | { 337 | static const constexpr std::size_t block_size = blocking; 338 | static const constexpr std::size_t block_mask = block_size - 1; 339 | static_assert((block_size & block_mask) == 0U, 340 | "block size must be 2^n for some integer n"); 341 | 342 | static std::size_t child_of(std::size_t node_no) noexcept; 343 | static std::size_t parent_of(std::size_t node_no) noexcept; 344 | static bool is_block_root(std::size_t node_no) noexcept; 345 | static std::size_t block_offset(std::size_t node_no) noexcept; 346 | static std::size_t block_base(std::size_t node_no) noexcept; 347 | static bool is_block_leaf(std::size_t node_no) noexcept; 348 | static std::size_t child_no(std::size_t node_no) noexcept; 349 | }; 350 | 351 | template > 353 | class payload 354 | { 355 | public: 356 | payload(Allocator const &alloc = Allocator{ }) : m_storage(alloc) { } 357 | template 358 | void push_back(U &&u) { m_storage.push_back(std::forward(u)); } 359 | void pop_back() { m_storage.pop_back(); } 360 | V &top() { return m_storage[1]; } 361 | V &back() { return m_storage.back(); } 362 | void store(std::size_t idx, V &&v) { m_storage[idx] = std::move(v); } 363 | void move(std::size_t from, std::size_t to) 364 | { 365 | m_storage[to] = std::move(m_storage[from]); 366 | } 367 | private: 368 | skip_vector m_storage; 369 | }; 370 | 371 | template 372 | class payload 373 | { 374 | public: 375 | payload(Allocator const & = Allocator{ }) { } 376 | constexpr bool back() const { return true; } 377 | constexpr void store(std::size_t, bool) const { } 378 | constexpr void move(std::size_t, std::size_t) const { } 379 | constexpr void pop_back() const { }; 380 | }; 381 | 382 | } // namespace prio_q_internal 383 | 384 | template , 386 | typename Allocator = std::allocator> 387 | class prio_queue : private Compare, private prio_q_internal::payload 388 | { 389 | using address = prio_q_internal::heap_heap_addressing; 390 | using P = prio_q_internal::payload; 391 | public: 392 | prio_queue(Compare const &compare = Compare()) : Compare(compare) { } 393 | explicit prio_queue(Compare const &compare, Allocator const &a) 394 | : Compare(compare) 395 | , m_storage(a) { } 396 | 397 | using value_type = T; 398 | using payload_type = V; 399 | 400 | template 401 | std::enable_if_t::value> 402 | push(U &&u); 403 | 404 | template 405 | std::enable_if_t::value> 406 | push(U &&key, X &&value); 407 | 408 | template 409 | std::enable_if_t::value, value_type const &> 410 | top() const noexcept; 411 | 412 | template 413 | std::enable_if_t::value, std::pair> 414 | top() noexcept; 415 | 416 | void pop() noexcept(std::is_nothrow_destructible::value); 417 | 418 | template 419 | std::enable_if_t::value> 420 | reschedule_top(T t); 421 | 422 | template 423 | std::enable_if_t::value> 424 | reschedule_top(T t); 425 | 426 | bool empty() const noexcept; 427 | 428 | std::size_t size() const noexcept; 429 | private: 430 | template 431 | void push_key(U &&key); 432 | 433 | bool sorts_before(value_type const &lv, value_type const &rv) const noexcept; 434 | 435 | prio_q_internal::skip_vector m_storage; 436 | size_t do_reschedule_top(T t) noexcept(noexcept(std::declval() = std::declval())); 437 | }; 438 | 439 | 440 | template 441 | template 442 | inline 443 | std::enable_if_t::value> 444 | prio_queue:: 445 | push(U &&u) 446 | { 447 | push_key(std::forward(u)); 448 | } 449 | 450 | template 452 | template 453 | inline 454 | std::enable_if_t::value> 455 | prio_queue:: 456 | push(U &&key, X &&value) 457 | { 458 | P::push_back(std::forward(value)); 459 | push_key(std::forward(key)); 460 | } 461 | 462 | 463 | template 465 | template 466 | inline 467 | void 468 | prio_queue:: 469 | push_key(U &&key) 470 | { 471 | auto hole_idx = m_storage.push_back(std::forward(key)); 472 | auto tmp = std::move(m_storage.back()); 473 | auto val = std::move(P::back()); 474 | 475 | while (rollbear_prio_q_likely(hole_idx != 1U)) 476 | { 477 | auto parent = address::parent_of(hole_idx); 478 | auto &p = m_storage[parent]; 479 | if (rollbear_prio_q_likely(!sorts_before(tmp, p))) break; 480 | m_storage[hole_idx] = std::move(p); 481 | P::move(parent, hole_idx); 482 | hole_idx = parent; 483 | } 484 | m_storage[hole_idx] = std::move(tmp); 485 | P::store(hole_idx, std::move(val)); 486 | } 487 | 488 | template 490 | inline 491 | void 492 | prio_queue:: 493 | pop() 494 | noexcept(std::is_nothrow_destructible::value) 495 | { 496 | assert(!empty()); 497 | std::size_t idx = 1; 498 | auto const last_idx = m_storage.size() - 1; 499 | for (; ;) 500 | { 501 | auto lc = address::child_of(idx); 502 | if (rollbear_prio_q_unlikely(lc > last_idx)) break; 503 | auto const sibling_offset = rollbear_prio_q_unlikely(address::is_block_leaf(idx)) 504 | ? address::block_size : 1; 505 | auto rc = lc + sibling_offset; 506 | auto i = 507 | rc < last_idx && !sorts_before(m_storage[lc], m_storage[rc]); 508 | auto next = i ? rc : lc; 509 | m_storage[idx] = std::move(m_storage[next]); 510 | P::move(next, idx); 511 | idx = next; 512 | } 513 | if (rollbear_prio_q_likely(idx != last_idx)) 514 | { 515 | auto last = std::move(m_storage.back()); 516 | auto last_val = std::move(P::back()); 517 | while (rollbear_prio_q_likely(idx != 1)) 518 | { 519 | auto parent = address::parent_of(idx); 520 | if (rollbear_prio_q_likely(!sorts_before(last, m_storage[parent]))) break; 521 | m_storage[idx] = std::move(m_storage[parent]); 522 | P::move(parent, idx); 523 | idx = parent; 524 | } 525 | m_storage[idx] = std::move(last); 526 | P::store(idx, std::move(last_val)); 527 | } 528 | m_storage.pop_back(); 529 | P::pop_back(); 530 | } 531 | 532 | 533 | template 535 | template 536 | inline 537 | std::enable_if_t::value, T const &> 538 | prio_queue:: 539 | top() 540 | const 541 | noexcept 542 | { 543 | assert(!empty()); 544 | return m_storage[1]; 545 | } 546 | 547 | template 549 | template 550 | inline 551 | std::enable_if_t::value, std::pair> 552 | prio_queue:: 553 | top() 554 | noexcept 555 | { 556 | assert(!empty()); 557 | return { m_storage[1], P::top() }; 558 | } 559 | 560 | template 561 | template 562 | inline 563 | std::enable_if_t::value> 564 | prio_queue:: 565 | reschedule_top(T t) 566 | { 567 | assert(!empty()); 568 | auto val = std::move(P::top()); 569 | size_t idx = do_reschedule_top(t); 570 | P::store(idx, std::move(val)); 571 | } 572 | 573 | template 574 | template 575 | inline 576 | std::enable_if_t::value> 577 | prio_queue:: 578 | reschedule_top(T t) 579 | { 580 | assert(!empty()); 581 | do_reschedule_top(t); 582 | } 583 | 584 | template 585 | size_t 586 | prio_queue:: 587 | do_reschedule_top(T t) 588 | noexcept(noexcept(std::declval() = std::declval())) 589 | { 590 | std::size_t idx = 1; 591 | auto const last_idx = m_storage.size() - 1; 592 | for (;;) 593 | { 594 | auto lc = address::child_of(idx); 595 | if (rollbear_prio_q_unlikely(lc > last_idx)) break; 596 | auto const sibling_offset = rollbear_prio_q_unlikely(address::is_block_leaf(idx)) ? address::block_size : 1; 597 | auto rc = lc + sibling_offset; 598 | auto i = rc <= last_idx && !sorts_before(m_storage[lc], m_storage[rc]); 599 | auto next = i ? rc : lc; 600 | if (sorts_before(t, m_storage[next])) break; 601 | m_storage[idx] = std::move(m_storage[next]); 602 | P::move(next, idx); 603 | idx = next; 604 | } 605 | m_storage[idx] = std::move(t); 606 | return idx; 607 | } 608 | 609 | template 611 | inline 612 | bool 613 | prio_queue:: 614 | empty() 615 | const 616 | noexcept 617 | { 618 | return m_storage.empty(); 619 | } 620 | 621 | template 623 | inline 624 | std::size_t 625 | prio_queue:: 626 | size() 627 | const 628 | noexcept 629 | { 630 | return m_storage.size() 631 | - (m_storage.size() + address::block_size - 1) / address::block_size; 632 | } 633 | 634 | template 636 | inline 637 | bool 638 | prio_queue:: 639 | sorts_before(value_type const &lv, value_type const &rv) 640 | const 641 | noexcept 642 | { 643 | Compare const &c = *this; 644 | return c(lv, rv); 645 | } 646 | 647 | namespace prio_q_internal 648 | { 649 | 650 | template 651 | inline 652 | std::size_t 653 | heap_heap_addressing:: 654 | child_of(std::size_t node_no) 655 | noexcept 656 | { 657 | if (rollbear_prio_q_likely(!is_block_leaf(node_no))) 658 | { 659 | return node_no + block_offset(node_no); 660 | } 661 | auto base = block_base(node_no) + 1; 662 | return base * block_size + child_no(node_no) * block_size * 2 + 1; 663 | } 664 | 665 | template 666 | inline 667 | std::size_t 668 | heap_heap_addressing:: 669 | parent_of(std::size_t node_no) 670 | noexcept 671 | { 672 | auto const node_root = block_base(node_no); // 16 673 | if (rollbear_prio_q_likely(!is_block_root(node_no))) 674 | { 675 | return node_root + block_offset(node_no) / 2; 676 | } 677 | auto const parent_base = block_base(node_root / block_size - 1); // 0 678 | auto const child = 679 | ((node_no - block_size) / block_size - parent_base) / 2; 680 | return parent_base + block_size / 2 + child; // 30 681 | } 682 | 683 | template 684 | inline 685 | bool 686 | heap_heap_addressing:: 687 | is_block_root(std::size_t node_no) 688 | noexcept 689 | { 690 | return block_offset(node_no) == 1U; 691 | } 692 | 693 | template 694 | inline 695 | std::size_t 696 | heap_heap_addressing:: 697 | block_offset(std::size_t node_no) 698 | noexcept 699 | { 700 | return node_no & block_mask; 701 | } 702 | 703 | template 704 | inline 705 | std::size_t 706 | heap_heap_addressing:: 707 | block_base(std::size_t node_no) 708 | noexcept 709 | { 710 | return node_no & ~block_mask; 711 | } 712 | 713 | template 714 | inline 715 | bool 716 | heap_heap_addressing:: 717 | is_block_leaf(std::size_t node_no) 718 | noexcept 719 | { 720 | return (node_no & (block_size >> 1)) != 0U; 721 | } 722 | 723 | template 724 | inline 725 | std::size_t 726 | heap_heap_addressing:: 727 | child_no(std::size_t node_no) 728 | noexcept 729 | { 730 | assert(is_block_leaf(node_no)); 731 | return node_no & (block_mask >> 1); 732 | } 733 | } // namespace prio_q_internal 734 | 735 | 736 | } // namespace rollbear 737 | 738 | #undef rollbear_prio_q_likely 739 | #undef rollbear_prio_q_unlikely 740 | 741 | #endif //ROLLBEAR_PRIO_QUEUE_HPP 742 | --------------------------------------------------------------------------------