c++ - 为什么迭代 std::array 比迭代 std::vector 快得多?
问题描述
编者注:
启用优化的后续问题仅对循环进行计时:
为什么通过`std::vector`进行迭代比通过`std::array`进行迭代更快?
我们可以看到延迟分配页面错误在读取未初始化的 BSS 内存与在定时循环之外初始化的动态分配 + 写入内存的影响。
我尝试分析此代码:
#include <vector>
#include <array>
#include <stdio.h>
using namespace std;
constexpr int n = 400'000'000;
//vector<int> v(n);
array<int, n> v;
int main()
{
int res = 0;
for(int x : v)
res += x;
printf("%d\n", res);
}
在我的机器上,array
版本比vector
.
在这种情况下,内存分配无关紧要,因为它只有一次。
$ g++ arrVsVec.cpp -O3
$ time ./a.out
0
real 0m0,445s
user 0m0,203s
sys 0m0,238s
$ g++ arrVsVec.cpp -O3
$ time ./a.out
0
real 0m0,749s
user 0m0,273s
sys 0m0,476s
我发现反汇编要复杂得多std::vector
:https ://godbolt.org/z/111L5G
解决方案
( ) 上的优化答案g++ -O2
:
您没有使用最终结果,因此编译器可以自由优化整个循环。
这就是在这种情况下发生的array
事情。
main:
xor eax, eax
ret
但是vector
分配和释放堆内存,这使优化复杂化,编译器倾向于安全地使用它并将其留在原处:
main:
xor eax, eax
ret
_GLOBAL__sub_I_v:
sub rsp, 8
mov edi, 400000000
mov QWORD PTR v[rip], 0
mov QWORD PTR v[rip+8], 0
mov QWORD PTR v[rip+16], 0
call operator new(unsigned long)
lea rdx, [rax+400000000]
mov QWORD PTR v[rip], rax
mov QWORD PTR v[rip+16], rdx
.L6:
mov DWORD PTR [rax], 0
add rax, 4
cmp rdx, rax
jne .L6
mov QWORD PTR v[rip+8], rdx
mov esi, OFFSET FLAT:v
mov edx, OFFSET FLAT:__dso_handle
mov edi, OFFSET FLAT:_ZNSt6vectorIiSaIiEED1Ev
add rsp, 8
jmp __cxa_atexit
这就是为什么array
在这种特殊情况下版本更快的原因。在更真实的应用程序中,差异不会那么显着,并且主要归结为这种vector
情况下的堆分配/释放时间。
关闭优化答案(g++
):
不要对未经优化的编译内容进行基准测试。
差异可能是由于vector
迭代器没有被内联。因此,与数组访问相比,在调试中访问向量元素会产生额外的间接性。
推荐阅读
- python - 如何链接 Anaconda3 库 python?
- haskell - 阅读器单子 - 阅读器与询问功能差异?
- typescript - 如何将 IResolvable 类型的组件添加到 CfnImageRecipe CDK 资源?
- python - ValueError: ('事务 %r 未在此连接上打开',
) - algorithm - 给定一个集合,你将如何生成它的排列/子集(Skiena 方法)?
- javascript - 在循环内的字符串开头连接
- arrays - Julia 函数返回的值超出预期
- r - 几个csv文件到一个xlsx中,r中有多个选项卡?
- python - 使用Python导入超过10 000行的api url数据
- javascript - 在 Electron BrowserView 中访问 DOM