为什么 C 数组比 std::array 快得多?
Why is C array so much faster than std::array?
我们目前正在用C++编写一些性能关键型代码,这些代码在许多大型矩阵和向量上运行。关于我们的研究,std::array
和标准 C 阵列之间应该没有重大的性能差异 (请参阅此问题或此问题)。 但是,在测试时,我们通过使用 C 数组std::array
经历了巨大的性能改进。 这是我们的演示代码:
#include <iostream>
#include <array>
#include <sys/time.h>
#define ROWS 784
#define COLS 100
#define RUNS 50
using std::array;
void DotPComplex(array<double, ROWS> &result, array<double, ROWS> &vec1, array<double, ROWS> &vec2){
for(int i = 0; i < ROWS; i++){
result[i] = vec1[i] * vec2[i];
}
}
void DotPSimple(double result[ROWS], double vec1[ROWS], double vec2[ROWS]){
for(int i = 0; i < ROWS; i++){
result[i] = vec1[i] * vec2[i];
}
}
void MatMultComplex(array<double, ROWS> &result, array<array<double, COLS>, ROWS> &mat, array<double, ROWS> &vec){
for (int i = 0; i < COLS; ++i) {
for (int j = 0; j < ROWS; ++j) {
result[i] += mat[i][j] * vec[j];
}
}
}
void MatMultSimple(double result[ROWS], double mat[ROWS][COLS], double vec[ROWS]){
for (int i = 0; i < COLS; ++i) {
for (int j = 0; j < ROWS; ++j) {
result[i] += mat[i][j] * vec[j];
}
}
}
double getTime(){
struct timeval currentTime;
gettimeofday(¤tTime, NULL);
double tmp = (double)currentTime.tv_sec * 1000.0 + (double)currentTime.tv_usec/1000.0;
return tmp;
}
array<double, ROWS> inputVectorComplex = {{ 0 }};
array<double, ROWS> resultVectorComplex = {{ 0 }};
double inputVectorSimple[ROWS] = { 0 };
double resultVectorSimple[ROWS] = { 0 };
array<array<double, COLS>, ROWS> inputMatrixComplex = {{0}};
double inputMatrixSimple[ROWS][COLS] = { 0 };
int main(){
double start;
std::cout << "DotP test with C array: " << std::endl;
start = getTime();
for(int i = 0; i < RUNS; i++){
DotPSimple(resultVectorSimple, inputVectorSimple, inputVectorSimple);
}
std::cout << "Duration: " << getTime() - start << std::endl;
std::cout << "DotP test with C++ array: " << std::endl;
start = getTime();
for(int i = 0; i < RUNS; i++){
DotPComplex(resultVectorComplex, inputVectorComplex, inputVectorComplex);
}
std::cout << "Duration: " << getTime() - start << std::endl;
std::cout << "MatMult test with C array : " << std::endl;
start = getTime();
for(int i = 0; i < RUNS; i++){
MatMultSimple(resultVectorSimple, inputMatrixSimple, inputVectorSimple);
}
std::cout << "Duration: " << getTime() - start << std::endl;
std::cout << "MatMult test with C++ array: " << std::endl;
start = getTime();
for(int i = 0; i < RUNS; i++){
MatMultComplex(resultVectorComplex, inputMatrixComplex, inputVectorComplex);
}
std::cout << "Duration: " << getTime() - start << std::endl;
}
编译者:icpc demo.cpp -std=c++11 -O0
结果如下:
DotP test with C array:
Duration: 0.289795 ms
DotP test with C++ array:
Duration: 1.98413 ms
MatMult test with C array :
Duration: 28.3459 ms
MatMult test with C++ array:
Duration: 175.15 ms
带有-O3
标志:
DotP test with C array:
Duration: 0.0280762 ms
DotP test with C++ array:
Duration: 0.0288086 ms
MatMult test with C array :
Duration: 1.78296 ms
MatMult test with C++ array:
Duration: 4.90991 ms
C 数组实现速度要快得多,无需优化编译器。为什么? 使用编译器优化,点积同样快。但是对于矩阵乘法,使用 C 数组时仍然有显着的加速。 有没有办法在使用std::array
时实现相同的性能?
更新:
使用的编译器:icpc 17.0.0
有了gcc 4.8.5
我们的代码运行速度比使用任何优化级别的英特尔编译器慢得多。因此,我们主要对英特尔编译器的行为感兴趣。
根据 Jonas 的建议,我们使用以下结果(英特尔编译器)调整了RUNS 50.000
:
带有-O0
标志:
DotP test with C array:
Duration: 201.764 ms
DotP test with C++ array:
Duration: 1020.67 ms
MatMult test with C array :
Duration: 15069.2 ms
MatMult test with C++ array:
Duration: 123826 ms
带有-O3
标志:
DotP test with C array:
Duration: 16.583 ms
DotP test with C++ array:
Duration: 15.635 ms
MatMult test with C array :
Duration: 980.582 ms
MatMult test with C++ array:
Duration: 2344.46 ms
首先,您使用的运行量实在太少了。就个人而言,我没有意识到(在运行代码之前)您的"持续时间"测量值以毫秒为单位
通过将DotPSimple
的RUNS
增加到 5,000,000 并DotPComplex
时间如下:
使用 C 数组进行点 P 测试:
持续时间: 1074.89
使用C++数组进行 DotP 测试:
持续时间: 1085.34
也就是说,它们非常接近同样快。事实上,由于基准的随机性,哪个是最快的,由于基准的随机性质,每个测试都有所不同。MatMultSimple
和MatMultComplex
也是如此,尽管他们只需要50,000次运行。
如果你真的想衡量和了解更多,你应该接受这个基准的随机性质,并近似地计算"持续时间"测量的分布。包括函数的随机顺序,以消除任何排序偏差。
编辑: 汇编代码(来自user2079303的答案)完全证明启用优化没有差异。因此,零成本抽象实际上是启用优化的零成本,这是一个合理的要求。
更新:
我使用的编译器:
g++ (Debian 6.3.0-6) 6.3.0 20170205
使用以下命令:
g++ -Wall -Wextra -pedantic -O3 test.cpp
使用此处理器:
Intel(R) Core(TM) i5-4300U CPU @ 1.90GHz
为什么...在没有编译器优化的情况下要快得多。为什么?
无论出于何种原因,编译器都会选择。如果不让编译器进行优化,则不能期望两个不同的代码段具有相似的性能,即使它们具有相同的行为。启用优化后,编译器可能能够将抽象的代码转换为高效的代码,并且性能应该相当。
std::array
的使用涉及函数调用,而指针的使用则不涉及。例如,std::array::operator[]
是一个函数,而指针的下标运算符不是。进行函数调用可能比不进行函数调用慢。所有这些函数调用都可以优化(内联扩展),但如果选择不启用优化,则函数调用将保留。
但是对于矩阵乘法,使用 C 数组时仍然有显着的加速。
可能是您的基准测试或编译器中的怪癖。在这里,这两个函数具有相同的程序集,因此具有相同的性能
编辑:我同意乔纳斯的回答。基准测试的迭代次数太少。此外,如果不重复基准并分析偏差,就不可能说两个测量之间的差异是否显着。
结论是:
-
启用优化时,C 数组的速度不会比
std::array
快。至少在使用 clang 3.9.1 编译时不会,如链接所示。也许您的编译器会产生不同的程序集,但我认为没有理由应该这样做。 -
C++的零成本抽象只有在优化后才是零成本。
-
编写有意义的微观基准并非易事。
- 为什么通过引用返回向量比通过移动返回要快得多?
- 禁用优化后,quick-bench.com 基准测试要快得多
- C++ OpenMP 斐波那契:1 个线程的执行速度比 4 个线程快得多
- 为什么迭代 std::array 比迭代 std::vector 快得多?
- 为什么<wstring>使用自定义 wcscmp 和 wmemcmp 比较器对向量进行排序比默认快得多?
- C 为什么在原始双阵列中的分配似乎比双变量分配要快得多
- Linux 在从文件中读取数据并放入矢量时比 Windows 快得多.txt.我将如何加速Windows以做同样的事情
- 为什么 C 数组比 std::array 快得多?
- 为什么 pcre 正则表达式比 c++11 正则表达式快得多
- 为什么数组比向量快得多
- 为什么 ifstream::read 比使用迭代器快得多
- 为什么其中一个比另一个快得多
- GDI+Image比C#Image快得多
- 为什么堆栈内存的 memcpy 比堆内存快得多
- 为什么使用TBB的OpenCV函数比基于Boost的实现快得多
- 为什么多个文件的编译速度比合并文件快得多
- 为什么Windows C++多线程IOPS比IOMeter快得多
- 为什么STL算法使用指针比std::向量迭代器快得多
- 为什么使用 mkdir () 函数比使用 system ('mkdir 路径')快得多?
- 为什么Eigens-mean()方法比sum()快得多