QVector<int> 写入/调整大小性能
Posted
技术标签:
【中文标题】QVector<int> 写入/调整大小性能【英文标题】:QVector<int> writing/resize performance 【发布时间】:2017-01-05 19:35:37 【问题描述】:我试图将一些使用 C 样式整数向量的代码重构为 QVector(因为其余代码使用 Qt)。 在我这样做之前,我进行了性能测试以检查这种变化会有多糟糕。
我使用以下代码:
#include <QVector>
#include <vector>
#include <cstdio>
#include <ctime>
void test1(int MAX_ELEMENTS, int TIMES)
int vec[MAX_ELEMENTS];
int nelems = 0;
for (int j=0; j<TIMES; j++)
nelems = MAX_ELEMENTS;
for (int i=0; i<MAX_ELEMENTS; i++)
vec[i] = 2;
printf("Vec[0] = %d\n", vec[0]);
void test2(int MAX_ELEMENTS, int TIMES)
std::vector<int> vec;
vec.reserve(MAX_ELEMENTS);
for (int j=0; j<TIMES; j++)
vec.clear();
for (int i=0; i<MAX_ELEMENTS; i++)
vec.push_back(2);
printf("Vec[0] = %d\n", vec[0]);
void test3(int MAX_ELEMENTS, int TIMES)
QVector<int> vec;
vec.reserve(MAX_ELEMENTS);
for (int j=0; j<TIMES; j++)
vec.clear();
for (int i=0; i<MAX_ELEMENTS; i++)
vec.push_back(2);
printf("Vec[0] = %d\n", vec[0]);
void test4(int MAX_ELEMENTS, int TIMES)
QVector<int> vec;
vec.reserve(MAX_ELEMENTS);
for (int j=0; j<TIMES; j++)
vec.resize(MAX_ELEMENTS);
for (int i=0; i<MAX_ELEMENTS; i++)
vec[i] = 2;
printf("Vec[0] = %d\n", vec[0]);
double measureExecutionTime(void (*func)(int, int))
const int MAX_ELEMENTS=30000;
const int TIMES=2000000;
clock_t begin, end;
begin = clock();
(*func)(MAX_ELEMENTS, TIMES);
end = clock();
return (double)(end - begin) / CLOCKS_PER_SEC;
int main()
double time_spent;
time_spent = measureExecutionTime(test1);
printf("Test 1 (plain c): %lf\n", time_spent);
time_spent = measureExecutionTime(test2);
printf("Test 2 (std::vector): %lf\n", time_spent);
time_spent = measureExecutionTime(test3);
printf("Test 3 (QVector clear): %lf\n", time_spent);
time_spent = measureExecutionTime(test4);
printf("Test 4 (QVector resize): %lf\n", time_spent);
return 0;
结果是:
Vec[0] = 2
Test 1 (plain c): 16.130129
Vec[0] = 2
Test 2 (std::vector): 92.719583
Vec[0] = 2
Test 3 (QVector clear): 109.882463
Vec[0] = 2
Test 4 (QVector resize): 46.261172
对于提高 QVector 性能的不同方式有什么想法吗? 这个向量每秒从 0 填充到它的新大小几次(用于时间表调度软件)。
Qt 版本:5.7.1(+dsfg1,来自 Debian 测试)。
我用来从 Linux shell 编译的命令行:
g++ -c -m64 -pipe -O2 -Wall -W -D_REENTRANT -fPIC -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -I. -I. -isystem /usr/include/x86_64-linux-gnu/qt5 -isystem /usr/include/x86_64-linux-gnu/qt5/QtGui -isystem /usr/include/x86_64-linux-gnu/qt5/QtCore -I. -I/usr/lib/x86_64-linux-gnu/qt5/mkspecs/linux-g++-64 -o teste.o teste.cpp
并且要明确:向量元素不相等,我只是将它们等于 2。向量中的有效元素数量随着时间表的活动数量成功安排而不断变化 - 当给定活动无法放入时剩下的插槽,算法开始回滚,删除一些最后放置的活动,以便开始另一次调度尝试。
【问题讨论】:
结果是: -- 用于构建测试的编译器选项是......?换句话说,如果您正在为未优化的构建计时,那么您的发现毫无价值。 如果性能对时间至关重要,则不应在代码的时间敏感部分分配然后清除 QVector(或任何容器,实际上)。 如果你想创建一个全是2的向量,最快的方法大概是std::vector<int> vec(MAX_ELEMENTS, 2);
。现在您将苹果与橙子进行比较。
如果您碰巧在使用 Visual Studio 并运行“调试”构建,那么所有这些都是有争议的。原因是 vector
由于调试版本中的迭代器检查而出名地慢。这就是为什么在询问性能时说明使用了哪些优化非常重要的原因。
@PaulMcKenzie,好的,对不起,我忘了这些信息很重要。帖子已编辑。
【参考方案1】:
其实正如MrEricSir在cmets部分指出的那样,clear()
操作才是test4
中真正的罪魁祸首。
如果您查看 Qt 文档,您会发现以下内容:
void QVector::clear()
从向量中删除所有元素。 注意:直到 Qt 5.6,这也释放了向量使用的内存。 从 Qt 5.7 开始,容量得以保留。
您可能正在使用 Qt 版本
【讨论】:
遗憾的是,它是 5.7.1(来自 Debian 测试包)【参考方案2】:您的测试不是将苹果与苹果进行比较。
int vec[MAX_ELEMENTS]
将分配在堆栈上,而 std::vector
和 QVector
将使用堆。基于堆栈的分配要快得多。如果您可以摆脱基于堆栈的分配,请考虑 std::array
。
如果您只是对用值填充向量感兴趣,请尝试仅对这部分进行基准测试。
换句话说,尝试进行基准测试:
for(int i = 0; i < max_size; ++i)
c_array[i] = i;
对
std_vector.resize(max_size); // <- initialization, not benchmarked
for(int i = 0; i < max_size; ++i)
std_vector[i] = i;
对
qvector.resize(max_size); // <- initialization, not benchmarked
for(int i = 0; i < max_size; ++i)
qvector[i] = i;
性能应该相当相似。
【讨论】:
调整大小经常发生。我希望我编辑了原始帖子,添加了一些说明。我更改了程序以便从基准测试中删除 .reserve() 方法。结果没有改变。测试 1(普通 c):16.044388 测试 2(std::array):16.055172 测试 3(std::vector):91.533684 测试 4(QVector 清除):112.354447 测试 5(QVector 调整大小):41.223804 我还意识到 resize() 不会影响性能,因为我每次都使用相同的参数调用它(因此,它应该什么都不做)。我测试了它,它花了类似的时间。【参考方案3】:测试开始时几乎是无稽之谈,不仅是无稽之谈,而且执行得很差。特别是在测试调整大小方面,使用静态大小的原始 C 数组。该测试如何在不进行任何调整大小的情况下调整大小?即使对于使用任何调整大小的容器,您也在调整到已经保留的值,这应该不会产生任何影响,因为您的 MAX_ELEMENTS
在整个测试过程中永远不会改变。
话虽如此,对于这种琐碎的操作QVector
很可能会成为一头奶牛。那是 Qt 对容器的隐式共享,使用 Copy-On-Write,这意味着执行的每个非常量方法都将检查容器数据是否正在共享,以便在这种情况下将其分离。该检查涉及原子,其中涉及同步,这是昂贵的。
如果您要对写访问执行更充分的测试,例如:
int elements = 30000, times = 200000;
QElapsedTimer t;
int * ia = new int[elements];
t.start();
for (int it = 0; it < times; ++ it)
for (int ic = 0; ic < elements; ++ic)
ia[ic] = 2;
qDebug() << t.elapsed() << " msec for raw array";
QVector<int> iqv(elements);
t.restart();
for (int it = 0; it < times; ++ it)
for (int ic = 0; ic < elements; ++ic)
iqv[ic] = 2;
qDebug() << t.elapsed() << " msec for qvector";
std::vector<int> isv;
isv.reserve(elements);
t.restart();
for (int it = 0; it < times; ++ it)
for (int ic = 0; ic < elements; ++ic)
isv[ic] = 2;
qDebug() << t.elapsed() << " msec for std::vector";
您会看到与上面所说的非常一致的结果,在我的系统上,结果是:
1491 msec for raw array
4238 msec for qvector
1491 msec for std::vector
原始 / C 数组和 std::vector
的时间几乎相同,而 QVector
则缓慢。
如果我们通过累积容器值来测试读取,情况正好相反,请注意,在这种情况下,我们使用at(index) const
代替QVector
:
2169 msec for raw array
2170 msec for qvector
2801 msec for std::vector
没有 COW 的惩罚 QVector
具有与原始 C 数组相同的性能。在这里输的是std::vector
,虽然我不确定具体原因,也许其他人可以详细说明。
【讨论】:
我不想单独测试矢量书写。我确实需要“调整”它。对不起,我自己没有说清楚。作为一个非英语母语的人,我避免写太多。阅读性能与我无关:详尽地完成的是写入 - 并更改数组/向量大小。 如果您对调整大小感兴趣,为什么要使用静态大小测试原始 C 数组?这是如何衡量任何事情的?在任何情况下调整大小都将涉及堆重新分配和数据复制,因此不同解决方案之间应该几乎相同。因此,就便利性和最佳写入速度而言,std::vector
似乎是正确的选择。
“调整大小”是指更改有效元素的数量。这就是为什么我对 Qt 和 std 向量使用 reserve() 方法,试图预先分配最大数量的元素。 Qt docs[1] 说 QVector::resize() 不会缩小向量容量。所以我认为重复这样做不会有问题。 [1]doc.qt.io/qt-5/qvector.html#resize
那么在这种情况下,您没有进行任何调整大小,您只是使用了不同大小的可用空间。以上是关于QVector<int> 写入/调整大小性能的主要内容,如果未能解决你的问题,请参考以下文章
QVector<int>[index] 返回另一个 QVector?
错误:“operator[]”不匹配(操作数类型为“QVector<int>”和“QCharRef”)