- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我尝试用 std::priority_queue 替换 std::multiset。但我对速度结果感到失望。算法运行时间增加50%...
相应的命令如下:
top() = begin();
pop() = erase(knn.begin());
push() = insert();
我对 priority_queue 的实现速度感到惊讶,我期待不同的结果(对 PQ 更好)...从概念上讲,多重集被用作优先级队列。为什么优先级队列和多重集有如此不同的性能,即使使用 -O2
?
十个结果的平均值,MSVS 2010,Win XP,32 位,方法 findAllKNN2 ()(请参见下文)
MS
N time [s]
100 000 0.5
1 000 000 8
PQ
N time [s]
100 000 0.8
1 000 000 12
是什么导致了这些结果?没有对源代码进行其他更改...感谢您的帮助...
MS 实现:
template <typename Point>
struct TKDNodePriority
{
KDNode <Point> *node;
typename Point::Type priority;
TKDNodePriority() : node ( NULL ), priority ( 0 ) {}
TKDNodePriority ( KDNode <Point> *node_, typename Point::Type priority_ ) : node ( node_ ), priority ( priority_ ) {}
bool operator < ( const TKDNodePriority <Point> &n1 ) const
{
return priority > n1.priority;
}
};
template <typename Point>
struct TNNeighboursList
{
typedef std::multiset < TKDNodePriority <Point> > Type;
};
方法:
template <typename Point>
template <typename Point2>
void KDTree2D <Point>::findAllKNN2 ( const Point2 * point, typename TNNeighboursList <Point>::Type & knn, unsigned int k, KDNode <Point> *node, const unsigned int depth ) const
{
if ( node == NULL )
{
return;
}
if ( point->getCoordinate ( depth % 2 ) <= node->getData()->getCoordinate ( depth % 2 ) )
{
findAllKNN2 ( point, knn, k, node->getLeft(), depth + 1 );
}
else
{
findAllKNN2 ( point, knn, k, node->getRight(), depth + 1 );
}
typename Point::Type dist_q_node = ( node->getData()->getX() - point->getX() ) * ( node->getData()->getX() - point->getX() ) +
( node->getData()->getY() - point->getY() ) * ( node->getData()->getY() - point->getY() );
if (knn.size() == k)
{
if (dist_q_node < knn.begin()->priority )
{
knn.erase(knn.begin());
knn.insert ( TKDNodePriority <Point> ( node, dist_q_node ) );
}
}
else
{
knn.insert ( TKDNodePriority <Point> ( node, dist_q_node ) );
}
typename Point::Type dist_q_node_straight = ( point->getCoordinate ( node->getDepth() % 2 ) - node->getData()->getCoordinate ( node->getDepth() % 2 ) ) *
( point->getCoordinate ( node->getDepth() % 2 ) - node->getData()->getCoordinate ( node->getDepth() % 2 ) ) ;
typename Point::Type top_priority = knn.begin()->priority;
if ( knn.size() < k || dist_q_node_straight < top_priority )
{
if ( point->getCoordinate ( node->getDepth() % 2 ) < node->getData()->getCoordinate ( node->getDepth() % 2 ) )
{
findAllKNN2 ( point, knn, k, node->getRight(), depth + 1 );
}
else
{
findAllKNN2 ( point, knn, k, node->getLeft(), depth + 1 );
}
}
}
PQ 实现(较慢,为什么?)
template <typename Point>
struct TKDNodePriority
{
KDNode <Point> *node;
typename Point::Type priority;
TKDNodePriority() : node ( NULL ), priority ( 0 ) {}
TKDNodePriority ( KDNode <Point> *node_, typename Point::Type priority_ ) : node ( node_ ), priority ( priority_ ) {}
bool operator < ( const TKDNodePriority <Point> &n1 ) const
{
return priority > n1.priority;
}
};
template <typename Point>
struct TNNeighboursList
{
typedef std::priority_queue< TKDNodePriority <Point> > Type;
};
方法:
template <typename Point>
template <typename Point2>
void KDTree2D <Point>::findAllKNN2 ( const Point2 * point, typename TNNeighboursList <Point>::Type & knn, unsigned int k, KDNode <Point> *node, const unsigned int depth ) const
{
if ( node == NULL )
{
return;
}
if ( point->getCoordinate ( depth % 2 ) <= node->getData()->getCoordinate ( depth % 2 ) )
{
findAllKNN2 ( point, knn, k, node->getLeft(), depth + 1 );
}
else
{
findAllKNN2 ( point, knn, k, node->getRight(), depth + 1 );
}
typename Point::Type dist_q_node = ( node->getData()->getX() - point->getX() ) * ( node->getData()->getX() - point->getX() ) +
( node->getData()->getY() - point->getY() ) * ( node->getData()->getY() - point->getY() );
if (knn.size() == k)
{
if (dist_q_node < knn.top().priority )
{
knn.pop();
knn.push ( TKDNodePriority <Point> ( node, dist_q_node ) );
}
}
else
{
knn.push ( TKDNodePriority <Point> ( node, dist_q_node ) );
}
typename Point::Type dist_q_node_straight = ( point->getCoordinate ( node->getDepth() % 2 ) - node->getData()->getCoordinate ( node->getDepth() % 2 ) ) *
( point->getCoordinate ( node->getDepth() % 2 ) - node->getData()->getCoordinate ( node->getDepth() % 2 ) ) ;
typename Point::Type top_priority = knn.top().priority;
if ( knn.size() < k || dist_q_node_straight < top_priority )
{
if ( point->getCoordinate ( node->getDepth() % 2 ) < node->getData()->getCoordinate ( node->getDepth() % 2 ) )
{
findAllKNN2 ( point, knn, k, node->getRight(), depth + 1 );
}
else
{
findAllKNN2 ( point, knn, k, node->getLeft(), depth + 1 );
}
}
}
最佳答案
首先,作者没有提供导致提到的性能下降的最小代码示例。其次,这个问题是 8 年前提出的,我确信编译器对性能有巨大的提升。
我做了一个基准示例,我在队列中取出第一个元素,然后以另一个优先级推回(模拟插入新元素而不创建一个元素),通过数组 kNodesCount
中的元素计数来做到这一点与 kRunsCount
循环迭代。我正在比较 priority_queue
与 multiset
和 multimap
.我决定包括 multimap
为了更精确的比较。它的简单测试非常接近作者的用例,我也尝试重现他在代码示例中使用的结构。
#include <set>
#include <type_traits>
#include <vector>
#include <chrono>
#include <queue>
#include <map>
#include <iostream>
template<typename T>
struct Point {
static_assert(std::is_integral<T>::value || std::is_floating_point<T>::value, "Incompatible type");
using Type = T;
T x;
T y;
};
template<typename T>
struct Node {
using Type = T;
Node<T> * left;
Node<T> * right;
T data;
};
template <typename T>
struct NodePriority {
using Type = T;
using DataType = typename T::Type;
Node<T> * node = nullptr;
DataType priority = static_cast<DataType>(0);
bool operator < (const NodePriority<T> & n1) const noexcept {
return priority > n1.priority;
}
bool operator > (const NodePriority<T> & n1) const noexcept {
return priority < n1.priority;
}
};
// descending order by default
template <typename T>
using PriorityQueueList = std::priority_queue<T>;
// greater used because of ascending order by default
template <typename T>
using MultisetList = std::multiset<T, std::greater<T>>;
// greater used because of ascending order by default
template <typename T>
using MultimapList = std::multimap<typename T::DataType, T, std::greater<typename T::DataType>>;
struct Inner {
template<template <typename> class C, typename T>
static void Operate(C<T> & list, std::size_t priority);
template<typename T>
static void Operate(PriorityQueueList<T> & list, std::size_t priority) {
if (list.size() % 2 == 0) {
auto el = std::move(list.top());
el.priority = priority;
list.push(std::move(el));
}
else {
list.pop();
}
}
template<typename T>
static void Operate(MultisetList<T> & list, std::size_t priority) {
if (list.size() % 2 == 0) {
auto el = std::move(*list.begin());
el.priority = priority;
list.insert(std::move(el));
}
else {
list.erase(list.begin());
}
}
template<typename T>
static void Operate(MultimapList<T> & list, std::size_t priority) {
if (list.size() % 2 == 0) {
auto el = std::move(*list.begin());
auto & elFirst = const_cast<int&>(el.first);
elFirst = priority;
el.second.priority = priority;
list.insert(std::move(el));
}
else {
list.erase(list.begin());
}
}
};
template<typename T>
void doOperationOnPriorityList(T & list) {
for (std::size_t pos = 0, len = list.size(); pos < len; ++pos) {
// move top element and update priority
auto priority = std::rand() % 10;
Inner::Operate(list, priority);
}
}
template<typename T>
void measureOperationTime(T & list, std::size_t runsCount) {
std::chrono::system_clock::time_point t1, t2;
std::uint64_t totalTime(0);
for (std::size_t i = 0; i < runsCount; ++i) {
t1 = std::chrono::system_clock::now();
doOperationOnPriorityList(list);
t2 = std::chrono::system_clock::now();
auto castedTime = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
std::cout << "Run " << i << " time: " << castedTime << "\n";
totalTime += castedTime;
}
std::cout << "Average time is: " << totalTime / runsCount << " ms" << std::endl;
}
int main() {
// consts
const int kNodesCount = 10'000'000;
const int kRunsCount = 10;
// prepare data
PriorityQueueList<NodePriority<Point<int>>> neighboursList1;
MultisetList<NodePriority<Point<int>>> neighboursList2;
MultimapList<NodePriority<Point<int>>> neighboursList3;
std::vector<Node<Point<int>>> nodes;
nodes.reserve(kNodesCount);
for (auto i = 0; i < kNodesCount; ++i) {
nodes.emplace_back(decltype(nodes)::value_type{ nullptr, nullptr, { 0,0 } });
auto priority = std::rand() % 10;
neighboursList1.emplace(decltype(neighboursList1)::value_type{ &nodes.back(), priority });
neighboursList2.emplace(decltype(neighboursList2)::value_type{ &nodes.back(), priority });
neighboursList3.emplace(decltype(neighboursList3)::value_type{ priority, { &nodes.back(), priority } });
}
// do operation on data
std::cout << "\nPriority queue\n";
measureOperationTime(neighboursList1, kRunsCount);
std::cout << "\nMultiset\n";
measureOperationTime(neighboursList2, kRunsCount);
std::cout << "\nMultimap\n";
measureOperationTime(neighboursList3, kRunsCount);
return 0;
}
我已经使用 VS v15.8.9 使用/Ox 进行了发布构建。查看 10'000'000 个项目在 10 次运行中的结果:
Priority queue
Run 0 time: 764
Run 1 time: 933
Run 2 time: 920
Run 3 time: 813
Run 4 time: 991
Run 5 time: 862
Run 6 time: 902
Run 7 time: 1277
Run 8 time: 774
Run 9 time: 771
Average time is: 900 ms
Multiset
Run 0 time: 2235
Run 1 time: 1811
Run 2 time: 1755
Run 3 time: 1535
Run 4 time: 1475
Run 5 time: 1388
Run 6 time: 1482
Run 7 time: 1431
Run 8 time: 1347
Run 9 time: 1347
Average time is: 1580 ms
Multimap
Run 0 time: 2197
Run 1 time: 1885
Run 2 time: 1725
Run 3 time: 1671
Run 4 time: 1500
Run 5 time: 1403
Run 6 time: 1411
Run 7 time: 1420
Run 8 time: 1409
Run 9 time: 1362
Average time is: 1598 ms
嗯哼,如你所见multiset
与 multimap
的性能相同和 priority_queue
是最快的(大约快 43%)。那为什么会这样呢?
让我们从priority_queue
开始, C++ 标准并没有告诉我们如何实现一个或另一个容器或结构,但在大多数情况下它是基于一个 binary heap (寻找 msvc 和 gcc 实现)!如果是priority_queue
您无法访问除顶部之外的任何元素,您无法遍历它们,无法通过索引获取,甚至无法获取最后一个元素(它为优化留出了一些空间)。二叉堆的平均插入是 O(1),只有最坏的情况是 O(log n),删除是 O(log n),因为我们从底部取出元素然后搜索下一个高优先级。
那multimap
呢?和 multiset
.它们通常都在 red-black binary tree 上实现(寻找 msvc 和 gcc 实现),其中平均插入是 O(log n) 和删除 O(log n)。
从这个角度来看priority_queue
NEVER 可以比 multiset
慢或 multimap
.所以,回到你的问题,multiset
因为优先级队列不比priority_queue
快本身。可能有很多原因,包括原始 priority_queue
在旧编译器上实现或错误使用此结构(问题不包含最小可行示例),除了作者没有提到编译标志或编译器版本之外,有时优化会产生重大变化。
UPDATE 1 根据@noɥʇʎudʎzɐɹƆ 请求
不幸的是,我现在无法访问 linux 环境,但我安装了 mingw-w64,版本信息:g++.exe(x86_64-posix-seh,由 strawberryperl.com 项目构建)8.3.0。使用的处理器与 visual studio 相同:处理器 Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz,2001 Mhz,4 核,8 逻辑处理器。
因此 g++ -O2
的结果是:
Priority queue
Run 0 time: 775
Run 1 time: 995
Run 2 time: 901
Run 3 time: 807
Run 4 time: 930
Run 5 time: 765
Run 6 time: 799
Run 7 time: 1151
Run 8 time: 760
Run 9 time: 780
Average time is: 866 ms
Multiset
Run 0 time: 2280
Run 1 time: 1942
Run 2 time: 1607
Run 3 time: 1344
Run 4 time: 1319
Run 5 time: 1210
Run 6 time: 1129
Run 7 time: 1156
Run 8 time: 1244
Run 9 time: 992
Average time is: 1422 ms
Multimap
Run 0 time: 2530
Run 1 time: 1958
Run 2 time: 1670
Run 3 time: 1390
Run 4 time: 1391
Run 5 time: 1235
Run 6 time: 1088
Run 7 time: 1198
Run 8 time: 1071
Run 9 time: 963
Average time is: 1449 ms
您可能会注意到它与 msvc 的图片几乎相同。
更新 2 感谢@JorgeBellon
A quick-bench.com在线benchmark链接,自己查!
希望看到我的帖子有任何补充,干杯!
关于c++ - 为什么使用 std::multiset 作为优先级队列比使用 std::priority_queue 更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5895792/
我尝试使用 constructor 为 priority_queue 分配内存,但出现以下错误: No matching constructor for initialization of 'prio
#include #include #include #include struct Temp { int p; std::string str; }; struct Te
这个问题在这里已经有了答案: Boost heap Setting user defined compare function (1 个回答) 关闭 8 年前。 我正在尝试为自定义 Edge 类设置
我是 C++ 的初学者。我正在尝试使用 std::priority_queue 创建最大堆和最小堆。只创建一个 maxheap 可以正常工作,但不能同时创建两者。我似乎无法理解错误。我收到以下错误:无
如何清除使用用户定义比较的priority_queue? 来自 std::priority_queue documentation ,我将 priority_queue 的使用减少到我需要的情况(=
如何清除使用用户定义比较的priority_queue? 来自 std::priority_queue documentation ,我将 priority_queue 的使用减少到我需要的情况(=
我有一个类Node除了存储数据外,它还有一个指向其父节点的指针 Node .我将一些节点存储在 priority_queue 中并覆盖 queue; 问题是,父指针似乎搞砸了。我的猜测是,当我弹出
我想创建一个名为 Edge 的对象,它从其构造函数将自身插入到 priority_queue 中。也就是; Class Edge { int m_from; int m_to; in
代码如下: 比较算法 class PathComp{ public: virtual bool betterThan(const PathInfo& path1, const PathInfo& pa
我有 priority_queue。我的函数 delete 在内存消耗和时间方面不是很合理。我见过类似的主题,但它们对我没有帮助。 How to remove element not at top f
我有这样的代码 priority_queue, decltype(&VD::CompareByDistance)> pqDistances(&VD::CompareByDistance); 在哪里 c
我正在研究最小堆的解决方案,除了自定义比较器之外,它还需要支持删除任何元素。完全自定义的堆实现是一种方法。但我想依靠 C++ STL 来进行所需的操作。 C++ 文档和 StackOverflow 答
我有一个 Dijkstra使用 priority_queue 的类具有自定义比较功能。我将队列命名为 DijkstraPriorityQueue用using陈述。在类构造函数中,我初始化了队列。为此,
我正在为这个问题编写代码。当我遇到问题时,整数流的中位数。请注意,此问题不是算法问题,而是 priority_queue 大小的模糊行为。 #include using namespace std;
int main() { list letters; priority_queue, less>letters_trans; cout input(cin), input_e
我的文件顶部有这些: #include typedef struct cell_s { unsigned int x; unsigned int y; unsigned in
你好,我需要创建一个类,其中包含一个 priority_queue 字段,其比较函数需要访问类中的另一个字段。简而言之,我需要写这样的东西: class A { B foo; prio
当我尝试使用 priority_queue 作为类成员时,我卡住了。请查看下面的代码,让我知道为什么 L1 看不到 Type 但 L2 可以看到。我尝试了 struct,也尝试了 ctor。 如果这是
我有一个服务器应用程序,它接受传入的查询并执行它们。如果查询太多,则应将它们排队,如果执行了其他一些查询,则也应执行排队的查询。由于我想传递具有不同优先级的查询,我认为使用 priority_queu
考虑一个 std::priority_queue,其中 N 元素具有相同的优先级。现在考虑具有任意优先级的元素的一些 pop() 和 push(),因此生成的队列由所有这些 N 元素组成上面提到的加上
我是一名优秀的程序员,十分优秀!