STD Deque出奇地慢

std deque is surprisingly slow

本文关键字:Deque STD      更新时间:2023-10-16

我只是发现,与使用预分配数组的"自制"堆栈版本相比,标准标准 std deque 真的很慢。
这是我堆栈的代码:

template <class T>
class FastStack
{    
public:
T* st;
int allocationSize;
int lastIndex;
public:
FastStack(int stackSize);
FastStack();
~FastStack();
inline void resize(int newSize);
inline void push(T x);
inline void pop();
inline T getAndRemove();
inline T getLast();
inline void clear();
};
template <class T>
FastStack<T>::FastStack()
{
lastIndex = -1;
st = NULL;
}
template <class T>
FastStack<T>::FastStack(int stackSize)
{
st = NULL;
this->allocationSize = stackSize;
st = new T[stackSize];
lastIndex = -1;
}
template <class T>
FastStack<T>::~FastStack()
{
delete [] st;
}
template <class T>
void FastStack<T>::clear()
{
lastIndex = -1;
}
template <class T>
T FastStack<T>::getLast()
{
return st[lastIndex];
}
template <class T>
T FastStack<T>::getAndRemove()
{
return st[lastIndex--];
}
template <class T>
void FastStack<T>::pop()
{
--lastIndex;
}
template <class T>
void FastStack<T>::push(T x)
{
st[++lastIndex] = x;
}
template <class T>
void FastStack<T>::resize(int newSize)
{
if (st != NULL)
delete [] st;
st = new T[newSize];
}

.
我运行这个简单的基准测试:

std::deque<int> aStack;
int x;
HRTimer timer;
timer.Start();
for (int i = 0; i < 2000000000; i++)
{
aStack.push_back(i);
x = aStack.back();
if (i % 100 == 0 && i != 0)
for (int j = 0; j < 100; j++)
aStack.pop_back();
}
double totalTime = timer.Stop();
stringstream ss;
ss << "std::deque " << totalTime;
log(ss.str());

.
使用带有矢量的 std 堆栈作为容器(如"Michael Kohne"所建议的那样)

std::stack<int, std::vector<int>> bStack;
int x;
HRTimer timer;
timer.Start();
for (int i = 0; i < 2000000000; i++)
{
bStack.push(i);
x = bStack.top();
if (i % 100 == 0 && i != 0)
for (int j = 0; j < 100; j++)
bStack.pop();
}
double totalTime = timer.Stop();
stringstream ss;
ss << "std::stack " << totalTime;
log(ss.str());

.
我的快速堆栈也是如此:

FastStack<int> fstack(200);
int x;
HRTimer timer;
timer.Start();
for (int i = 0; i < 2000000000; i++)
{
fstack.push(i);
x = fstack.getLast();
if (i % 100 == 0 && i != 0)
for (int j = 0; j < 100; j++)
fstack.pop();
}
double totalTime = timer.Stop();
stringstream ss;
ss << "FastStack " << totalTime;
log(ss.str());

.
4 次运行后的结果如下:
deque 15.529 deque 15.3756
deque 15.429

deque 15.4778

堆栈 6.19099 堆栈 6.1834
堆栈 6.19315
堆栈 6.19841

快速堆栈3.01085
快速堆栈 2.9934
快速堆栈 3.02536
快速堆栈 3.00937

结果以秒为单位,我在英特尔 i5 3570k(默认时钟)上运行代码。我使用了VS2010编译器和所有可用的优化。 我知道我的 FastStack 有很多限制,但有很多情况,它可以在哪里使用以及何时可以很好地提升!(我在一个项目中使用它,与 std::queue 相比,我的速度提高了 2 倍)。
所以现在我的问题是:

C++中还有其他"抑制剂",每个人都在使用但没有人知道它们?
编辑:我不想冒犯,我只是好奇你是否知道一些未知的"加速"。

您正在比较苹果和橙子。取消排队是 DOUBLE-END,这要求它在内部与您实现的简单堆栈有很大不同。尝试针对std::stack<int, std::vector<int> >运行基准测试,看看您是如何做到的。 std::stack 是一个容器适配器,如果您使用矢量作为底层容器,它应该与您自行开发的实现一样快。

一个缺点是 std::stack 实现没有办法让你预先设置大小,所以在你知道最大大小需要是多少的情况下,它最初会慢一点,因为它必须重新分配几次。在那之后,它将几乎相同。

如果您知道在任何给定时间堆栈中的最大元素数量,则应使用预先预留容量的std::vector。即使您不知道容量,对于堆栈,您也应该使用会增长几倍的std::vector(增长时成本高于预分配的向量),但永远不会收缩,因此一段时间后它将停止分配内存。

性能的问题在于,std::deque会根据需要分配区块,并在不再需要时解除分配(遵循某种策略),因此,如果您经常填充和清除std::deque就会不断进行分配和分配。