【问题标题】:std::queue<T, list<T> >::size() is slow in O(n)?std::queue<T, list<T> >::size() 在 O(n) 中很慢?
【发布时间】:2011-12-10 04:04:39
【问题描述】:

我的代码使用队列时遇到了意外的性能行为。我意识到当队列中有更多元素时性能会下降。事实证明,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。

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

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

【问题讨论】:

    标签: c++ performance stl queue


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      如果您将代码更改为:

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

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

      【讨论】:

      • @BjörnPollex:cppreference 说了同样的话。关键是queue 是一个适配器类。
      • 另一方面,它确实建议 O(1) 对任何容器(包括列表)进行大小操作,这可以通过跟踪列表本身的大小来获得。
      • O(1) for std::list::size() 意味着昂贵的splice。没有什么是不折不扣的。
      • 常量 size() 是否在 GCC 4.6.1 中实现?我能找到的只有std::distance(begin(), end());,对我来说看起来像 O(n)。
      • @KerrekSB:确实不存在,它只在 4.7 中实现
      【解决方案3】:

      添加到迈克尔的答案:您使用 std::list 作为队列容器,因此 size() 方法取决于您的 std::list 大小实现。如果您在同一个网站上查找,您会找到http://www.cplusplus.com/reference/stl/list/size/,并且在某些实现上复杂性是恒定的,而在其他实现上是线性的。如果您需要恒定的插入和大小,那么您需要不同的数据结构或更好的std::list 实现。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-24
        • 2020-05-01
        • 2014-03-09
        • 1970-01-01
        • 1970-01-01
        • 2010-09-18
        相关资源
        最近更新 更多