【发布时间】:2017-02-27 19:58:30
【问题描述】:
假设 STL 列表(作为双链表实现)的“push_back”和“pop_front”方法应该是常数 O(1)。然而,我们在 linux 上运行的应用程序中遇到了 cpu 问题,我们发现“pop_front”方法在使用列表时效率非常低。这是列表实施问题还是预期行为?
这是示例代码:
class A {
public:
A() { mA = rand(); mB = rand(); mC = rand(); mD = rand(); }
u32 mA;
u32 mB;
u32 mC;
u32 mD;
};
#define DELTA(t1, t0) ((t1.tv_sec - t0.tv_sec)*1000 + ((t1.tv_usec - t0.tv_usec)/1000))
int main(int argc, char* argv[]) {
std::list<A> l;
std::queue<A> q;
std::deque<A> dq;
printf("Creating nodes...");
std::vector<A> v;
for (int i = 0; i < 100000; ++i) {
A a;
v.push_back(a);
}
printf("OK\n");
timeval t0, t1;
printf("std::deque test: push back...");
gettimeofday(&t0, NULL);
for (std::vector<A>::const_iterator iter = v.begin(); iter != v.end(); ++iter) {
dq.push_back(*iter);
}
gettimeofday(&t1, NULL);
printf("Done in %d ms, size = %d\n", DELTA(t1, t0), dq.size());
printf("std::deque test: pop front...");
gettimeofday(&t0, NULL);
while (dq.size() > 0) {
A a = dq.front();
dq.pop_front();
}
gettimeofday(&t1, NULL);
printf("Done in %d ms, size = %d\n", DELTA(t1, t0), dq.size());
printf("std::queue test: push back...");
gettimeofday(&t0, NULL);
for (std::vector<A>::const_iterator iter = v.begin(); iter != v.end(); ++iter) {
q.push(*iter);
}
gettimeofday(&t1, NULL);
printf("Done in %d ms, size = %d\n", DELTA(t1, t0), q.size());
printf("std::queue test: pop front...");
gettimeofday(&t0, NULL);
while (q.size() > 0) {
A a = q.front();
q.pop();
}
gettimeofday(&t1, NULL);
printf("Done in %d ms, size = %d\n", DELTA(t1, t0), q.size());
printf("std::list test: push back...");
gettimeofday(&t0, NULL);
for (std::vector<A>::const_iterator iter = v.begin(); iter != v.end(); ++iter) {
l.push_back(*iter);
}
gettimeofday(&t1, NULL);
printf("Done in %d ms, size = %d\n", DELTA(t1, t0), l.size());
printf("std::list test: pop front...");
gettimeofday(&t0, NULL);
while (l.size() > 0) {
A a = l.front();
l.pop_front();
}
gettimeofday(&t1, NULL);
printf("Done in %d ms, size = %d\n", DELTA(t1, t0), l.size());
return 0;
}
对于不同数量的节点我们得到:
5000 个节点:
std::deque test: push back...Done in 0 ms, size = 5000
std::deque test: pop front...Done in 0 ms, size = 0
std::queue test: push back...Done in 0 ms, size = 5000
std::queue test: pop front...Done in 0 ms, size = 0
std::list test: push back...Done in 0 ms, size = 5000
std::list test: pop front...Done in 202 ms, size = 0
10000 个节点:
std::deque test: push back...Done in 0 ms, size = 10000
std::deque test: pop front...Done in 0 ms, size = 0
std::queue test: push back...Done in 0 ms, size = 10000
std::queue test: pop front...Done in 0 ms, size = 0
std::list test: push back...Done in 1 ms, size = 10000
std::list test: pop front...Done in 279 ms, size = 0
100000 个节点:
std::deque test: push back...Done in 5 ms, size = 100000
std::deque test: pop front...Done in 4 ms, size = 0
std::queue test: push back...Done in 3 ms, size = 100000
std::queue test: pop front...Done in 4 ms, size = 0
std::list test: push back...Done in 12 ms, size = 100000
std::list test: pop front...Done in 31148 ms, size = 0
谢谢!
维森特
【问题讨论】:
-
这样的事情非常依赖于实现。您使用的是哪个编译器?它的哪个版本?您使用的是哪个标准库(某些编译器允许您选择),以及哪个版本?你是在优化还是不优化的情况下进行构建(基准测试应该始终通过优化来完成)?对于“pop front”测试,您还需要对
A的复制或移动构造函数以及front函数进行测试和基准测试。最后,您是在 C++11(或更高版本)模式下构建的吗?还是使用 C++03(或更早版本)? -
你应该默认使用std::vector,并且在做详细的性能分析之后才考虑std::list。 Bjarne Stroustrup 在stroustrup.com/bs_faq.html#liststroustrup.com/bs_faq.html#list
-
@hungptit:考虑到队列行为(push_back/pop_front),我们知道向量非常糟糕。