std::queue<T, list<T> >::size() 在 O(n) 中很慢?

std::queue<T, list<T> >::size() is slow in O(n)?

本文关键字:gt lt list std size queue      更新时间:2023-10-16

我在使用队列的代码中遇到了意想不到的性能行为。我意识到当队列中有更多元素时,性能会下降。结果表明,size()方法的使用是原因。下面是显示问题的一些代码:

#include <queue>
#include <list>
#include <iostream>
#include "Stopwatch.h"
using namespace std;
struct BigStruct
{
    int x[100];
};
int main()
{
    CStopwatch queueTestSw;
    typedef BigStruct QueueElementType;
    typedef std::queue<QueueElementType, std::list<QueueElementType> > QueueType;
    //typedef std::queue<QueueElementType > QueueType; //no surprise, this queue is fast and constant
    QueueType m_queue;
    for (int i=0;i<22000;i++)
        m_queue.push(QueueElementType());
    CStopwatch sw;
    sw.Start();
    int dummy;
    while (!m_queue.empty())
    {
        //remove 1000 elements:
        for (int i=0;i<1000;i++)
        {
            m_queue.pop();
        }
        //call size() 1000 times and see how long it takes
        sw = CStopwatch();
        sw.Start();
        for (int i=0;i<1000;i++)
        {   
            if (m_queue.size() == 123456)
            {
                dummy++;
            }
        }
        std::cout << m_queue.size() << " items left. time: " << sw.GetElapsedTimeInSeconds() << std::endl;  
    }   
    return dummy;

}

其中CStopwatch是使用clock_gettime(CLOCK_REALTIME, ..)的类。结果是:

21000 items left. time: 1.08725
20000 items left. time: 0.968897
19000 items left. time: 0.818259
18000 items left. time: 0.71495
17000 items left. time: 0.583725
16000 items left. time: 0.497451
15000 items left. time: 0.422712
14000 items left. time: 0.352949
13000 items left. time: 0.30133
12000 items left. time: 0.227588
11000 items left. time: 0.178617
10000 items left. time: 0.124512
9000 items left. time: 0.0893425
8000 items left. time: 0.0639174
7000 items left. time: 0.0476441
6000 items left. time: 0.033177
5000 items left. time: 0.0276136
4000 items left. time: 0.022112
3000 items left. time: 0.0163013
2000 items left. time: 0.0101932
1000 items left. time: 0.00506138

这似乎与http://www.cplusplus.com/reference/stl/queue/size/:

复杂性:常数。

如果结构体更大,问题会更严重。我使用的是GCC 4.3.2.

你能解释一下吗?有没有办法用列表来解决这个问题?

(请注意,我需要使用列表作为队列的底层容器,因为我需要恒定时间的插入复杂度。)

queue是一个容器适配器,因此您必须理解复杂性描述可能仅指适配器本身所做的工作(这实际上是常量,即只是将调用传递给底层容器)。

例如,size()调用底层容器的size()函数。对于list,在c++ 98/03中复杂度为0 (n),在c++ 11中复杂度为0(1)。

您的queue使用list容器,而不是deque默认:

typedef std::queue<QueueElementType, std::list<QueueElementType> > QueueType;

您不应该对size需要O(n)感到惊讶,因为这是c++ 11之前的list容器的完全有效实现。标准的前一个版本没有保证列表的size成员函数的复杂性。

如果您将代码更改为:

typedef std::queue<QueueElementType, std::deque<QueueElementType> > QueueType;

您将看到您想要的行为(O(1)大小复杂度)。

补充Michael的回答:您使用std::list作为队列容器,因此size()方法取决于您的std::list实现的大小。如果你在同一个网站上查找,你会发现http://www.cplusplus.com/reference/stl/list/size/,一些实现的复杂性是恒定的,而另一些是线性的。如果您需要恒定的插入和大小,那么您需要不同的数据结构或更好的std::list实现。