简体   繁体   English

如果 find_if() 花费的时间太长,是否有替代方案可用于提高程序性能?

[英]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:使用起来可能更快:

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如果删除需要在搜索之间是原子的,那么您可以

  1. swap it with end() - 1 ,itend() - 1交换,
  2. erase end() - 1 , and擦除end() - 1
  3. re-heapify.重新堆化。

Erasing end() - 1 is O(1) while erasing it is linear in std::distance(it, end) .擦除end() - 1O(1)而擦除itstd::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 很昂贵。

  1. it = remove_if(begin, end, [](){ lambda }
  2. erase(it, end)
  3. re-heapify再堆
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)
   ...
}

Doing better做得更好

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.这听起来写起来很痛苦,只有在生成的程序仍然太慢的情况下我才会这样做。

Have you thought of...你有没有想过...

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM