为什么在这种情况下STL priority_queue并不比multiset快多少
Why is the STL priority_queue not much faster than multiset in this case?
我正在比较STL(g++)priority_queue的性能,发现推送和弹出没有我预期的那么快。请参阅以下代码:
#include <set>
#include <queue>
using namespace std;
typedef multiset<int> IntSet;
void testMap()
{
srand( 0 );
IntSet iSet;
for ( size_t i = 0; i < 1000; ++i )
{
iSet.insert(rand());
}
for ( size_t i = 0; i < 100000; ++i )
{
int v = *(iSet.begin());
iSet.erase( iSet.begin() );
v = rand();
iSet.insert(v);
}
}
typedef priority_queue<int> IntQueue;
void testPriorityQueue()
{
srand(0);
IntQueue q;
for ( size_t i = 0; i < 1000; ++i )
{
q.push(rand());
}
for ( size_t i = 0; i < 100000; ++i )
{
int v = q.top();
q.pop();
v = rand();
q.push(v);
}
}
int main(int,char**)
{
testMap();
testPriorityQueue();
}
我编译了这个-O3,然后运行valgrind--tool=callgrind,KCachegrindtestMap占用总CPU的54%testPriorityQueue占用CPU 的44%
(没有-O3 testMap比testPriorityQueue快得多)testPriorityQueue似乎花费了大部分时间的函数称为
void std::__adjust_heap<__gbe_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, long, int, std::less<int> >
该函数似乎是从pop()调用中调用的。
这个函数到底做什么?有没有一种方法可以通过使用不同的容器或分配器来避免这种情况?
优先级队列被实现为堆:每次删除head元素时,都必须"重新平衡"。在链接描述中,delete-min
是O(log n)
操作,实际上是因为min
(或head)元素是扁平二叉树的根。
集合通常被实现为红黑树,最小元素将是最左边的节点(所以要么是叶子,要么最多有一个右边的子节点)。因此,它最多有一个子级要移动,并且可以根据允许的不平衡程度,在多个pop
调用中摊销再平衡。
请注意,如果堆有任何优势,那么它很可能位于引用的位置(因为它是连续的,而不是基于节点的)。这正是可能更难准确测量的优势,所以我建议在接受这个结果之前也运行一些经过的实时基准测试。
我实现了一个优先级队列,当使用-O3编译时,它似乎运行得更快。也许只是因为编译器比STL情况下能够内联更多?
#include <set>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
typedef multiset<int> IntSet;
#define TIMES 10000000
void testMap()
{
srand( 0 );
IntSet iSet;
for ( size_t i = 0; i < 1000; ++i ) {
iSet.insert(rand());
}
for ( size_t i = 0; i < TIMES; ++i ) {
int v = *(iSet.begin());
iSet.erase( iSet.begin() );
v = rand();
iSet.insert(v);
}
}
typedef priority_queue<int> IntQueue;
void testPriorityQueue()
{
srand(0);
IntQueue q;
for ( size_t i = 0; i < 1000; ++i ) {
q.push( rand() );
}
for ( size_t i = 0; i < TIMES; ++i ) {
int v = q.top();
q.pop();
v = rand();
q.push(v);
}
}
template <class T>
class fast_priority_queue
{
public:
fast_priority_queue()
:size(1) {
mVec.resize(1); // first element never used
}
void push( const T& rT ) {
mVec.push_back( rT );
size_t s = size++;
while ( s > 1 ) {
T* pTr = &mVec[s];
s = s / 2;
if ( mVec[s] > *pTr ) {
T tmp = mVec[s];
mVec[s] = *pTr;
*pTr = tmp;
} else break;
}
}
const T& top() const {
return mVec[1];
}
void pop() {
mVec[1] = mVec.back();
mVec.pop_back();
--size;
size_t s = 1;
size_t n = s*2;
T& rT = mVec[s];
while ( n < size ) {
if ( mVec[n] < rT ) {
T tmp = mVec[n];
mVec[n] = rT;
rT = tmp;
s = n;
n = 2 * s;
continue;
}
++n;
if ( mVec[n] < rT ) {
T tmp = mVec[n];
mVec[n] = rT;
rT = tmp;
s = n;
n = 2 * s;
continue;
}
break;
}
}
size_t size;
vector<T> mVec;
};
typedef fast_priority_queue<int> MyQueue;
void testMyPriorityQueue()
{
srand(0);
MyQueue q;
for ( size_t i = 0; i < 1000; ++i ) {
q.push( rand() );
}
for ( size_t i = 0; i < TIMES; ++i ) {
int v = q.top();
q.pop();
v = rand();
q.push(v);
}
}
int main(int,char**)
{
clock_t t1 = clock();
testMyPriorityQueue();
clock_t t2 = clock();
testMap();
clock_t t3 = clock();
testPriorityQueue();
clock_t t4 = clock();
cout << "fast_priority_queue: " << t2 - t1 << endl;
cout << "std::multiset: " << t3 - t2 << endl;
cout << "std::priority_queue: " << t4 - t3 << endl;
}
当在64位Linux上用g++4.1.2标志:-O3编译时,这给了我:
fast_priority_queue: 260000
std::multiset: 620000
std::priority_queue: 490000
相关文章:
- 复制列表初始化的隐式转换的等级是多少
- while循环中while循环的时间复杂度是多少
- 如何检查一个c++字符串中有多少相同的字符/数字
- C++有多少类型的循环
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 在条件变量中触发错误信号的频率是多少
- 函数的时间复杂度是多少?
- 必须为 C++20 协程帧保留多少内存?
- 对于四轴飞行器,PID中I控制器的理想值应该是多少
- C++,数组有多少个地址?
- 在C++中使用并行化的预期速度是多少(不是 OpenMp,而是 <thread>)
- 在 Linux 中存储区域设置名称的缓冲区大小应该是多少?
- 在内存不足之前,我可以声明多少个 const 变量?
- 可以读入进程内存的最大块大小是多少?
- 如何在不知道C++中有多少可选参数的情况下在循环中使用va_arg?
- 在二维向量或数组中可以存储的最大元素数是多少?
- 如何创建一个所有行大小不同的 2D 数组,并且用户将指定每行将有多少个元素?
- 计算出有多少客户端可以连接到我正在使用的一些tcp服务器代码
- 如果我不知道每个列表中有多少个数字,我如何将给定数量的数字列表作为输入?
- 为什么在这种情况下STL priority_queue并不比multiset快多少