[英]If find_if() takes too long, are there alternatives that can be used for better program performance?
I'm working on a D* Lite path planner in C++. The program maintains a priority queue of cells (U) , each cell have two cost values, and a key can be calculated for a cell which determine it's order on the priority queue.我正在研究 C++ 中的 D* Lite 路径规划器。该程序维护一个单元格(U)的优先级队列,每个单元格都有两个成本值,并且可以为一个单元格计算一个键,以确定它在优先级队列中的顺序.
using Cost = float;
using HeapKey = pair<Cost, Cost>;
using KeyCompare = std::greater<std::pair<HeapKey, unsigned int>>;
vector<pair<HeapKey, unsigned int>> U;
When a cell is added it is done so by using:添加单元格时,使用以下方法完成:
U.push_back({ k, id });
push_heap(U.begin(), U.end(), KeyCompare());
As part of the path planning algorithm cells sometimes need to be removed, and here lies the current problem as far as I can see.作为路径规划算法的一部分,有时需要删除单元格,据我所知,这就是当前的问题。 I recently had help on this site to speed my program up quite a bit by using push_heap instead of make_heap , but now it seems that the part of the program that removes cells is the slowest part.
我最近在这个网站上得到了帮助,通过使用push_heap而不是make_heap来加快我的程序,但现在看来程序中删除单元格的部分是最慢的部分。 Cells are removed from the priority queue by:
通过以下方式从优先级队列中删除信元:
void DstarPlanner::updateVertex(unsigned int id) {
...
...
auto it = find_if(U.begin(), U.end(), [=](auto p) { return p.second == id; });
U.erase(it);
...
...
}
From my tests this seems to take roughly 80% of the time my program use for path planning.从我的测试来看,这似乎占用了我程序用于路径规划的大约 80% 的时间。 It was my hope coming here that a more time-saving method existed.
我希望来到这里能有一种更省时的方法。
Thank you.谢谢你。
EDIT - Extra information.编辑 - 额外信息。
void DstarPlanner::insertHeap(unsigned int id, HeapKey k) {
U.push_back({ k, id });
push_heap(U.begin(), U.end(), KeyCompare());
in_U[id]++;
}
void DstarPlanner::updateVertex(unsigned int id) {
Cell* u = graph.getCell(id);
if (u->id != id_goal) {
Cost mincost = infinity;
for (auto s : u->neighbors) {
mincost = min(mincost, graph.getEdgeCost(u->id, s->id) + s->g);
}
u->rhs = mincost;
}
if (in_U[id]) {
auto it = find_if(U.begin(), U.end(), [=](auto p) { return p.second == id; });
U.erase(it);
in_U[id]--;
}
if (u->g != u->rhs) {
insertHeap(id, u->calculateKey());
}
}
vector<int> DstarPlanner::ComputeShortestPath() {
vector<int> bestPath;
vector<int> emptyPath;
Cell* n = graph.getCell(id_start);
while (U.front().first < n->calculateKey() || n->rhs != n->g) {
auto uid = U.front().second;
Cell* u = graph.getCell(uid);
auto kold = U.front().first;
pop_heap(U.begin(), U.end(), KeyCompare());
U.pop_back();
in_U[u->id]--;
if (kold < u->calculateKey()) {
insertHeap(u->id, u->calculateKey());
} else if (u->g > u->rhs) {
u->g = u->rhs;
for (auto s : u->neighbors) {
if (!occupied(s->id)) {
updateVertex(s->id);
}
}
} else {
u->g = infinity;
for (auto s : u->neighbors) {
if (!occupied(s->id)) {
updateVertex(s->id);
}
}
updateVertex(u->id);
}
}
bestPath=constructPath();
return bestPath;
}
find_if
does a linear search. find_if
进行线性搜索。 It maybe faster to use:使用起来可能更快:
std::map
/ std::set
-> Standard binary search tree implementations std::map
/ std::set
-> 标准二叉搜索树实现std::unordered_map
/std::unordered_set
-> Standard hash table implementations std::unordered_map
/std::unordered_set
-> 标准 hash 表实现These may use a lot of memory if your elements (key-value pairs) are small integers.如果您的元素(键值对)是小整数,这些可能会使用很多 memory。 To avoid that you can use 3rd party alternatives like
boost::unordered_flat_map
.为避免这种情况,您可以使用第三方替代方案,例如
boost::unordered_flat_map
。
How do you re-heapify after U.erase(it)
?你如何在
U.erase(it)
之后重新堆化? Do you ever delete multiple nodes at once?您曾经一次删除多个节点吗?
If deletions need to be atomic between searches , then you can如果删除需要在搜索之间是原子的,那么您可以
it
with end() - 1
,it
与end() - 1
交换,end() - 1
, andend() - 1
和 Erasing end() - 1
is O(1) while erasing it
is linear in std::distance(it, end)
.擦除
end() - 1
是O(1)而擦除it
在std::distance(it, end)
中是线性的。
void DstarPlanner::updateVertex(unsigned int id) {
...
// take the id by reference since this is synchronous
auto it = find_if(U.begin(), U.end(), [&](const auto& p) { return p.second == id; });
*it = std::move(*(U.end() - 1));
U.erase((U.end() - 1));
std::make_heap(U.begin(), U.end()); // expensive!!! 3*distance(begin, end)
...
}
If you can delete multiple nodes between searches , then you can use a combination of erase
+ remove_if
to only perform one mass re-heapify.如果你可以在搜索之间删除多个节点,那么你可以使用
erase
+ remove_if
的组合来只执行一次 mass re-heapify。 This is important be heapify is expensive.这很重要,因为 heapify 很昂贵。
it = remove_if(begin, end, [](){ lambda }
erase(it, end)
void DstarPlanner::updateVertex(const std::vector<unsigned int>& sorted_ids) {
...
auto it = remove_if(U.begin(), U.end(), [&](const auto& p) { return std::binary_search(ids.begin(), ids.end(), p.second); });
U.erase(it, U.end());
std::make_heap(U.begin(), U.end()); // expensive!!! 3*distance(begin, end)
...
}
You can possibly improve on this by replacing std::make_heap
(which makes no assumptions about the heapiness of [begin(), end())
with a custom method that re-heapifies a former heap around "poison points" -- it only needs to initially inspect the elements around the elements that were swapped.您可以通过用自定义方法替换
std::make_heap
(它不假设[begin(), end())
的堆度)来对此进行改进,该自定义方法围绕“毒点”重新堆砌以前的堆——它只需要首先检查被交换元素周围的元素。 This sounds like a pain to write and I'd only do it if the resulting program was still too slow.这听起来写起来很痛苦,只有在生成的程序仍然太慢的情况下我才会这样做。
Just not even removing elements from the heap?甚至不从堆中删除元素? The fact you're using a heap tells me that the algorithm designers suggested a heap.
您使用堆的事实告诉我算法设计者建议使用堆。 If they suggested a heap, then they likely didn't envision random removals.
如果他们建议堆,那么他们很可能没有想到随机删除。 This is speculation on my part.
这是我的猜测。 I'm otherwise not familiar with D* lite.
否则我不熟悉 D* lite。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.