diff --git a/CHANGELOG.md b/CHANGELOG.md index 79a234ec9..1495a57d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: Re-use priority queue in StaticRTree. [#6952](https://github.com/Project-OSRM/osrm-backend/pull/6952) - CHANGED: Optimise encodePolyline function. [#6940](https://github.com/Project-OSRM/osrm-backend/pull/6940) - CHANGED: Avoid reallocations in base64 encoding. [#6951](https://github.com/Project-OSRM/osrm-backend/pull/6951) - CHANGED: Get rid of unused Boost dependencies. [#6960](https://github.com/Project-OSRM/osrm-backend/pull/6960) diff --git a/include/util/binary_heap.hpp b/include/util/binary_heap.hpp new file mode 100644 index 000000000..3189addc7 --- /dev/null +++ b/include/util/binary_heap.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include +#include + +namespace osrm::util +{ + +// in its essence it is std::priority_queue, but with `clear` method +template class BinaryHeap +{ + public: + bool empty() const { return heap_.empty(); } + + const T &top() const + { + BOOST_ASSERT(!heap_.empty()); + return heap_.front(); + } + + void pop() + { + BOOST_ASSERT(!heap_.empty()); + std::pop_heap(heap_.begin(), heap_.end()); + heap_.pop_back(); + } + + template void emplace(Args &&...args) + { + heap_.emplace_back(std::forward(args)...); + std::push_heap(heap_.begin(), heap_.end()); + } + + void clear() { heap_.clear(); } + + private: + std::vector heap_; +}; + +} // namespace osrm::util \ No newline at end of file diff --git a/include/util/static_rtree.hpp b/include/util/static_rtree.hpp index f9d72c144..bbb12efa5 100644 --- a/include/util/static_rtree.hpp +++ b/include/util/static_rtree.hpp @@ -2,7 +2,9 @@ #define STATIC_RTREE_HPP #include "storage/tar_fwd.hpp" +#include "osrm/coordinate.hpp" #include "util/bearing.hpp" +#include "util/binary_heap.hpp" #include "util/coordinate_calculation.hpp" #include "util/deallocating_vector.hpp" #include "util/exception.hpp" @@ -15,8 +17,6 @@ #include "util/vector_view.hpp" #include "util/web_mercator.hpp" -#include "osrm/coordinate.hpp" - #include "storage/shared_memory_ownership.hpp" #include @@ -554,9 +554,12 @@ class StaticRTree auto projected_coordinate = web_mercator::fromWGS84(input_coordinate); Coordinate fixed_projected_coordinate{projected_coordinate}; + // we re-use queue for each query to avoid re-allocating memory + static thread_local util::BinaryHeap traversal_queue; + + traversal_queue.clear(); // initialize queue with root element - std::priority_queue traversal_queue; - traversal_queue.push(QueryCandidate{0, TreeIndex{}}); + traversal_queue.emplace(QueryCandidate{0, TreeIndex{}}); while (!traversal_queue.empty()) { @@ -710,10 +713,11 @@ class StaticRTree // distance must be non-negative BOOST_ASSERT(0. <= squared_distance); BOOST_ASSERT(i < std::numeric_limits::max()); - traversal_queue.push(QueryCandidate{squared_distance, - leaf_id, - static_cast(i), - Coordinate{projected_nearest}}); + + traversal_queue.emplace(QueryCandidate{squared_distance, + leaf_id, + static_cast(i), + Coordinate{projected_nearest}}); } } @@ -742,7 +746,7 @@ class StaticRTree child.minimum_bounding_rectangle.GetMinSquaredDist( fixed_projected_input_coordinate); - traversal_queue.push(QueryCandidate{ + traversal_queue.emplace(QueryCandidate{ squared_lower_bound_to_element, TreeIndex(parent.level + 1, child_index - m_tree_level_starts[parent.level + 1])}); } diff --git a/unit_tests/util/binary_heap.cpp b/unit_tests/util/binary_heap.cpp new file mode 100644 index 000000000..354fe7464 --- /dev/null +++ b/unit_tests/util/binary_heap.cpp @@ -0,0 +1,132 @@ +#include "util/binary_heap.hpp" + +#include +#include + +BOOST_AUTO_TEST_SUITE(binary_heap_test) + +BOOST_AUTO_TEST_CASE(empty_heap) +{ + osrm::util::BinaryHeap heap; + BOOST_CHECK(heap.empty()); +} + +BOOST_AUTO_TEST_CASE(push_and_top) +{ + osrm::util::BinaryHeap heap; + heap.emplace(5); + BOOST_CHECK_EQUAL(heap.top(), 5); + BOOST_CHECK(!heap.empty()); +} + +BOOST_AUTO_TEST_CASE(push_multiple_and_order) +{ + osrm::util::BinaryHeap heap; + heap.emplace(5); + heap.emplace(10); + heap.emplace(3); + heap.emplace(8); + BOOST_CHECK_EQUAL(heap.top(), 10); +} + +BOOST_AUTO_TEST_CASE(pop_and_order) +{ + osrm::util::BinaryHeap heap; + heap.emplace(5); + heap.emplace(10); + heap.emplace(3); + heap.emplace(8); + + BOOST_CHECK_EQUAL(heap.top(), 10); + heap.pop(); + BOOST_CHECK_EQUAL(heap.top(), 8); + heap.pop(); + BOOST_CHECK_EQUAL(heap.top(), 5); + heap.pop(); + BOOST_CHECK_EQUAL(heap.top(), 3); + heap.pop(); + BOOST_CHECK(heap.empty()); +} + +BOOST_AUTO_TEST_CASE(clear_heap) +{ + osrm::util::BinaryHeap heap; + heap.emplace(5); + heap.emplace(10); + heap.clear(); + BOOST_CHECK(heap.empty()); +} + +BOOST_AUTO_TEST_CASE(emplace_with_custom_type) +{ + struct CustomType + { + int value; + CustomType(int v) : value(v) {} + bool operator<(const CustomType &other) const { return value < other.value; } + }; + + osrm::util::BinaryHeap heap; + heap.emplace(5); + heap.emplace(10); + heap.emplace(3); + BOOST_CHECK_EQUAL(heap.top().value, 10); +} + +BOOST_AUTO_TEST_CASE(large_number_of_elements) +{ + osrm::util::BinaryHeap heap; + for (int i = 0; i < 1000; ++i) + { + heap.emplace(i); + } + BOOST_CHECK_EQUAL(heap.top(), 999); + + for (int i = 999; i >= 0; --i) + { + BOOST_CHECK_EQUAL(heap.top(), i); + heap.pop(); + } + BOOST_CHECK(heap.empty()); +} + +BOOST_AUTO_TEST_CASE(duplicate_values) +{ + osrm::util::BinaryHeap heap; + heap.emplace(5); + heap.emplace(5); + heap.emplace(5); + + BOOST_CHECK_EQUAL(heap.top(), 5); + heap.pop(); + BOOST_CHECK_EQUAL(heap.top(), 5); + heap.pop(); + BOOST_CHECK_EQUAL(heap.top(), 5); + heap.pop(); + BOOST_CHECK(heap.empty()); +} + +BOOST_AUTO_TEST_CASE(string_type) +{ + osrm::util::BinaryHeap heap; + heap.emplace("apple"); + heap.emplace("banana"); + heap.emplace("cherry"); + + BOOST_CHECK_EQUAL(heap.top(), "cherry"); + heap.pop(); + BOOST_CHECK_EQUAL(heap.top(), "banana"); + heap.pop(); + BOOST_CHECK_EQUAL(heap.top(), "apple"); +} + +BOOST_AUTO_TEST_CASE(emplace_after_clear) +{ + osrm::util::BinaryHeap heap; + heap.emplace(5); + heap.clear(); + heap.emplace(10); + BOOST_CHECK_EQUAL(heap.top(), 10); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file