C++ 为什么使用std::multiset作为优先级队列比使用std::priority\u队列快?
我尝试用std::priority\u队列替换std::multiset。但我对速度结果感到失望。算法的运行时间增加了50% 以下是相应的命令:C++ 为什么使用std::multiset作为优先级队列比使用std::priority\u队列快?,c++,performance,priority-queue,multimap,multiset,C++,Performance,Priority Queue,Multimap,Multiset,我尝试用std::priority\u队列替换std::multiset。但我对速度结果感到失望。算法的运行时间增加了50% 以下是相应的命令: top() = begin(); pop() = erase(knn.begin()); push() = insert(); 我对优先级队列实现的速度感到惊讶,我期望得到不同的结果(对PQ更好)。。。从概念上讲,multiset被用作优先级队列。为什么优先级队列和multiset具有如此不同的性能,即使使用-O2 十个结果的平均值,MSVS 201
top() = begin();
pop() = erase(knn.begin());
push() = insert();
我对优先级队列实现的速度感到惊讶,我期望得到不同的结果(对PQ更好)。。。从概念上讲,multiset被用作优先级队列。为什么优先级队列和multiset具有如此不同的性能,即使使用-O2
十个结果的平均值,MSVS 2010,Win XP,32位,方法findallkn2()(请参见下面的内容)
是什么导致了这些结果?没有对源代码进行任何其他更改。。。谢谢你的帮助
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;
};
模板
结构TKDNodePriority
{
KDNode*节点;
类型名称点::类型优先级;
TKDNodePriority():节点(NULL),优先级(0){}
TKDNodePriority(KDNode*node,typename Point::Type priority):节点(node),优先级(priority){
布尔运算符<(常数TKDNodePriority&n1)常数
{
返回优先级>n1.priority;
}
};
模板
结构列表
{
typedef std::multisetType;
};
方法:
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 );
}
}
}
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 );
}
}
}
模板
模板
void KDTree2D::findAllKNN2(常量Point2*point,typename TNNeighboursList::Type&knn,unsigned int k,KDNode*node,常量unsigned int depth)常量
{
if(node==NULL)
{
返回;
}
如果(点->获取坐标(深度%2)获取数据()->获取坐标(深度%2))
{
FindAllKN2(点,knn,k,节点->getLeft(),深度+1);
}
其他的
{
FindALKNN2(点,knn,k,节点->getRight(),深度+1);
}
typename Point::Type dist\u q\u node=(node->getData()->getX()-Point->getX())*(node->getData()->getX()-Point->getX())+
(node->getData()->getY()-point->getY())*(node->getData()->getY()-point->getY());
如果(knn.size()==k)
{
if(dist_q_节点优先级)
{
knn.erase(knn.begin());
knn.insert(TKDNodePriority(node,dist_q_node));
}
}
其他的
{
knn.insert(TKDNodePriority(node,dist_q_node));
}
typename Point::Type dist\u q\u node\u stright=(点->获取坐标(节点->获取深度()%2)-节点->获取数据()->获取坐标(节点->获取深度()%2))*
(点->获取坐标(节点->获取深度()%2)-节点->获取数据()->获取坐标(节点->获取深度()%2));
typename Point::Type top_priority=knn.begin()->priority;
if(knn.size()获取坐标(节点->获取深度()%2)getData()->get坐标(节点->获取深度()%2))
{
FindALKNN2(点,knn,k,节点->getRight(),深度+1);
}
其他的
{
FindAllKN2(点,knn,k,节点->getLeft(),深度+1);
}
}
}
PQ实施(速度较慢,为什么?
模板
结构TKDNodePriority
{
KDNode*节点;
类型名称点::类型优先级;
TKDNodePriority():节点(NULL),优先级(0){}
TKDNodePriority(KDNode*node,typename Point::Type priority):节点(node),优先级(priority){
布尔运算符<(常数TKDNodePriority&n1)常数
{
返回优先级>n1.priority;
}
};
模板
结构列表
{
typedef std::priority_queueType;
};
方法:
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 );
}
}
}
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 );
}
}
}
模板
模板
void KDTree2D::findAllKNN2(常量Point2*point,typename TNNeighboursList::Type&knn,unsigned int k,KDNode*node,常量unsigned int depth)常量
{
if(node==NULL)
{
返回;
}
如果(点->获取坐标(深度%2)获取数据()->获取坐标(深度%2))
{
FindAllKN2(点,knn,k,节点->getLeft(),深度+1);
}
其他的
{
FindALKNN2(点,knn,k,节点->getRight(),深度+1);
}
typename Point::Type dist\u q\u node=(node->getData()->getX()-Point->getX())*(node->getData()->getX()-Point->getX())+
(node->getData()->getY()-point->getY())*(node->getData()->getY()-point->getY());
如果(knn.size()==k)
{
if(dist_q_节点获取坐标(节点->获取深度()%2)-节点->获取数据()->获取坐标(节点->获取深度()%2))*
(点->获取坐标(节点->获取深度()%2)-节点->获取数据()->获取坐标(节点->获取深度()%2));
typename Point::Type top_priority=knn.top().priority;
if(knn.size()获取坐标(节点->获取深度()%2)getData()->get坐标(节点->获取深度()%2))
{
FindALKNN2(点,knn,k,节点->getRight(),深度+1);
}
其他的
{
FindAllKN2(点,knn,k,节点->getLeft(),深度+1);
}
}
}
据我所知,优先级队列的实现是罪魁祸首<代码>优先级\u队列
作为专用的向量
或deque
实现(在下面)。因为优先级队列需要有随机访问迭代器。当您将项目弹出或推入优先级\u队列
时,需要将队列中的剩余项目复制到空白空间,插入时也会发生同样的情况<代码>多组基于键
编辑:非常抱歉,multi_集合不是基于哈希键的。不知什么原因,我把它和多重地图混淆了。但是multi_集是一个多排序的关联
#include <iostream>
#include <queue>
#include <deque>
#include <set>
#include <ctime>
#include <cstdlib>
#include <unistd.h>
using namespace std;
template <typename T>
double run_test(T& pq, int size, int iterations)
{
struct timespec start, end;
for(int i = 0; i < size; ++i)
pq.push(rand());
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
for(int i = 0; i < iterations; ++i)
{
if(rand()%2)
pq.pop();
else
pq.push(rand());
}
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
end.tv_sec -= start.tv_sec;
end.tv_nsec -= start.tv_nsec;
if (end.tv_nsec < 0)
{
--end.tv_sec;
end.tv_nsec += 1000000000ULL;
}
return (end.tv_sec*1e3 + end.tv_nsec/1e6);
}
template <class T>
class multiset_pq: public multiset<T>
{
public:
multiset_pq(): multiset<T>() {};
void push(T elm) { this->insert(elm); }
void pop() { if(!this->empty()) this->erase(this->begin()); }
const T& top() { return *this->begin(); }
};
int main(void)
{
const int size = 5000;
const int iterations = 100000;
priority_queue<int, vector<int> > pqv;
priority_queue<int, deque<int> > pqd;
multiset_pq<int> pqms;
srand(time(0));
cout<<"pq-w-vector: "<<run_test(pqv, size, iterations)<<"ms"<<endl;
cout<<"pq-w-deque: "<<run_test(pqd, size, iterations)<<"ms"<<endl;
cout<<"pq-w-multiset: "<<run_test(pqms, size, iterations)<<"ms"<<endl;
return 0;
}
#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;
}
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
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
31622 37891 54884
27092 33919 54878
27324 35870 51427
27961 35348 53170
26746 34753 54191