-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use custom d-ary heap implementation #7017
Open
SiarheiFedartsou
wants to merge
1
commit into
master
Choose a base branch
from
sf-query-heap
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#pragma once | ||
|
||
#include <boost/assert.hpp> | ||
#include <functional> | ||
#include <limits> | ||
#include <utility> | ||
#include <vector> | ||
|
||
namespace osrm::util | ||
{ | ||
template <typename HeapData, int Arity, typename Comparator = std::less<HeapData>> class DAryHeap | ||
{ | ||
public: | ||
using HeapHandle = size_t; | ||
|
||
static constexpr HeapHandle INVALID_HANDLE = std::numeric_limits<size_t>::max(); | ||
|
||
public: | ||
const HeapData &top() const { return heap[0]; } | ||
|
||
size_t size() const { return heap.size(); } | ||
|
||
bool empty() const { return heap.empty(); } | ||
|
||
const HeapData &operator[](HeapHandle handle) const { return heap[handle]; } | ||
|
||
template <typename ReorderHandler> | ||
void emplace(HeapData &&data, ReorderHandler &&reorderHandler) | ||
{ | ||
heap.emplace_back(std::forward<HeapData>(data)); | ||
heapifyUp(heap.size() - 1, std::forward<ReorderHandler>(reorderHandler)); | ||
} | ||
|
||
template <typename ReorderHandler> | ||
void decrease(HeapHandle handle, HeapData &&data, ReorderHandler &&reorderHandler) | ||
{ | ||
BOOST_ASSERT(handle < heap.size()); | ||
|
||
heap[handle] = std::forward<HeapData>(data); | ||
heapifyUp(handle, std::forward<ReorderHandler>(reorderHandler)); | ||
} | ||
|
||
void clear() { heap.clear(); } | ||
|
||
template <typename ReorderHandler> void pop(ReorderHandler &&reorderHandler) | ||
{ | ||
BOOST_ASSERT(!heap.empty()); | ||
heap[0] = std::move(heap.back()); | ||
heap.pop_back(); | ||
if (!heap.empty()) | ||
{ | ||
heapifyDown(0, std::forward<ReorderHandler>(reorderHandler)); | ||
} | ||
} | ||
|
||
private: | ||
size_t parent(size_t index) { return (index - 1) / Arity; } | ||
|
||
size_t kthChild(size_t index, size_t k) { return Arity * index + k + 1; } | ||
|
||
template <typename ReorderHandler> void heapifyUp(size_t index, ReorderHandler &&reorderHandler) | ||
{ | ||
HeapData temp = std::move(heap[index]); | ||
while (index > 0 && comp(temp, heap[parent(index)])) | ||
{ | ||
size_t parentIndex = parent(index); | ||
heap[index] = std::move(heap[parentIndex]); | ||
reorderHandler(heap[index], index); | ||
index = parentIndex; | ||
} | ||
heap[index] = std::move(temp); | ||
reorderHandler(heap[index], index); | ||
} | ||
|
||
template <typename ReorderHandler> | ||
void heapifyDown(size_t index, ReorderHandler &&reorderHandler) | ||
{ | ||
HeapData temp = std::move(heap[index]); | ||
size_t child; | ||
while (kthChild(index, 0) < heap.size()) | ||
{ | ||
child = minChild(index); | ||
if (!comp(heap[child], temp)) | ||
{ | ||
break; | ||
} | ||
heap[index] = std::move(heap[child]); | ||
reorderHandler(heap[index], index); | ||
index = child; | ||
} | ||
heap[index] = std::move(temp); | ||
reorderHandler(heap[index], index); | ||
} | ||
|
||
size_t minChild(size_t index) | ||
{ | ||
size_t bestChild = kthChild(index, 0); | ||
for (size_t k = 1; k < Arity; ++k) | ||
{ | ||
size_t pos = kthChild(index, k); | ||
if (pos < heap.size() && comp(heap[pos], heap[bestChild])) | ||
{ | ||
bestChild = pos; | ||
} | ||
} | ||
return bestChild; | ||
} | ||
|
||
private: | ||
Comparator comp; | ||
std::vector<HeapData> heap; | ||
}; | ||
} // namespace osrm::util |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having the reorder handler as a template parameter seems somewhat confusing.
Do have a need to specify multiple ones at all? If this is needed, should the be implemented inside the heap?
Can we possibly aim for a simpler implementation of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the intention here was to be able to pass lambda and avoid using std::function which has certain performance impact... Such template parameters is frequent pattern for such things - if you want to pass lambda, compiler will infer type of this lambda for you in a most efficient way. At the same time I agree that probably having the fact that we pass only single type of function in there worth trying to make this more obvious (not sure it will be simple though, but I'll try 😄 )
What do you mean by "inside the heap"? In my initial implementation
DAryHeap
wasn't a separate class and all code from here "lived" directly inQueryHeap
, but I found it a bit messy and decided to decouple things a bit to keep code structure a bit similar to what we have now (QueryHeap and separate boost::d_ary_heap before and QueryHeap and separate DAryHeap after). I could try to return back to initial implementation...WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or you mean that implementation of
reorderHandler
should be a part of DAryHeap? I am not sure it is also doable without merging DAryHeap to QueryHeap - thisreorderHandler
is kind of a "glue" betweenDAryHeap
andQueryHeap
...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option I see is... make it super straightforward. DAryHeap now is kind of generic DAryHeap implementation, but we could "couple" it to our needs, i.e. just take reference to
inserted_nodes
from QueryHeap and do all things we are currently doing in reorderHandler directly on that vector. Should also work without issues as well.Anyway @DennisOSRM would love to hear your opinion on this please 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SO question about passing lambdas as parameters https://stackoverflow.com/questions/6458612/proper-way-to-receive-a-lambda-as-parameter-by-reference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And one more blogpost with concrete example of why template can be more preferable here… https://wolchok.org/posts/cxx-trap-2-std-function/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DennisOSRM WDYT? Tbh I am not sure I have good ideas how to remove template parameter here - all ideas have their own drawbacks. May be you have some? Or may be worth introducing a bit of complexity in favor of performance? 😀