C++ RowMajor和ColMajor数据排列矩阵行求和的奇怪性能差异
我决定检查矩阵中的数据排列如何影响简单操作的性能。 我使用C++ RowMajor和ColMajor数据排列矩阵行求和的奇怪性能差异,c++,performance,gcc,memory,eigen,C++,Performance,Gcc,Memory,Eigen,我决定检查矩阵中的数据排列如何影响简单操作的性能。 我使用Eigen::Matrix作为数据存储编写了简单的行求和算法。 我认为,由于缓存利用率提高,RowMajor存储应该表现出更好的性能 我使用了带有-O2选项的g++编译器,它给出了以下结果: ColMajor:40791546µs RowMajor:28790948µs 很好。但是对于-O3来说,它给了我一个非常奇怪的区别: ColMajor:10353619µs RowMajor:28359348µs 看起来ColMajor在使用-O3
Eigen::Matrix
作为数据存储编写了简单的行求和算法。
我认为,由于缓存利用率提高,RowMajor存储应该表现出更好的性能
我使用了带有-O2
选项的g++
编译器,它给出了以下结果:
ColMajor:40791546µs
RowMajor:28790948µs
很好。但是对于-O3
来说,它给了我一个非常奇怪的区别:
ColMajor:10353619µs
RowMajor:28359348µs
看起来ColMajor在使用-O3
时变得非常快。为什么从-O2
切换到-O3
会极大地改变性能
我的CPU:intel i7-6700K,gcc版本:7.5.0-3ubuntu1~19.10
我的“基准”:
#包括
#包括
#包括
#包括“特征/核心”
模板
无效运行测试(常数特征::MatrixBase&mat,特征::MatrixBase&res){
const int64_t nRows=mat.rows();
常量int64_t nCols=mat.cols();
对于(int64_t row=0;row std::cout基于ColMajor
的循环在-O3
中要快得多,因为GCC 7.5能够自动将其矢量化,而不是基于RowMajor
的循环。您可以看到(L11
-标记的循环)。
自动矢量化是非常重要的
事实上,提到的缓存效应特别适用于不适合缓存的大矩阵,对于相对较小的矩阵,矢量化可能比缓存效率更重要。问题是GCC在简单约化的矢量化方面有一些困难。
您可以帮助他使用OpenMP指令,例如#pragma omp simd reduction(+:acgregatorvar)
。或者,您可以使用应矢量化的Eigen提供的行求和(特别是对于连续数据)。
生成的代码应该是以前所有代码中最快的。
是生成的程序集代码
#include <iostream>
#include <vector>
#include <chrono>
#include "Eigen/Core"
template<typename DerivedMat, typename DerivedRes>
void runTest(const Eigen::MatrixBase<DerivedMat> &mat, Eigen::MatrixBase<DerivedRes> &res) {
const int64_t nRows = mat.rows();
const int64_t nCols = mat.cols();
for(int64_t row = 0; row < nRows; ++row){
for(int64_t col = 0; col < nCols; ++col){
res(row, 0) += mat(row, col);
}
}
}
const int64_t nRows = 300;
const int64_t nCols = 5000;
const int nAttempts = 20000;
template<int Alignment>
void bench() {
Eigen::Matrix<float, -1, -1, Alignment> mat(nRows, nCols);
srand(42);
mat.setRandom();
Eigen::VectorXf res(nRows);
res.setZero();
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
for(int iter = 0; iter < nAttempts; ++iter)
runTest(mat, res);
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::cout << "Elapsed " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << "[µs]" << std::endl;
}
int main() {
bench<Eigen::ColMajor>();
//bench<Eigen::RowMajor>();
return 0;
}