C++ 访问数组效率对比
在C++中, 访问一个多维数据有很多方式, 例如使用[ ], 使用指针等. 这里, 我们考虑以下五种方式, 并计算他们运行的时间. 以二维数组为例,
- 按行访问, row major.
for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { arr[i][j] = i; } }
- 按列访问, col major.
for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { arr[j][i] = i; } }
- 使用指针, 当做一维数组线性递增寻址访问.
for (int i = 0; i < res_square; i++) { *(arr_ptr + i) = i; }
- 使用指针, 双循环按行计算地址后寻址访问.
for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { *(arr_ptr + i * resolution + j) = i; } }
- 使用指针, 双循环按列计算地址后寻址访问.
for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { *(arr_ptr + j * resolution + i) = i; } }
以const int K = 100; const int resolution = 4096;
的情况下, g++开启O6优化, 每种方式测试 K 次, 统计每种访问方式的时间为
1row major, test 100 times, average cost 0.0051966 s
2col major, test 100 times, average cost 0.0350176 s
3row major pointer, test 100 times, average cost 0.00527759 s
4row major pointer, test 100 times, average cost 0.00530057 s
5col major pointer, test 100 times, average cost 0.0346161 s
关闭编译器优化后, 结果如下
1row major, test 100 times, average cost 0.0326994 s
2col major, test 100 times, average cost 0.270378 s
3row major pointer, test 100 times, average cost 0.0355432 s
4row major pointer, test 100 times, average cost 0.0253486 s
5col major pointer, test 100 times, average cost 0.276818 s
notice: 值得注意的是, 第三种方式的执行效率是比第四中低的. 直觉上, 线性寻址应该会比双循环计算更快. 并且第四种方式的效率高于第一种和第三种. 对此, 有人给出了一下解释, mark一下:
用cpu工具大概看了下,第四种比第三种更优是因为拆开后自增依赖降低了,所以流水线能够更有效的隐藏延迟.
另外, 这也反映了c++数组是按行优先存储的.