我对OpenMP的性能非常失望(特别是在我的手臂设备上)
在我计划的项目中,我需要进行大量简单的矩阵 - vec乘法(仿射变换,有些人可能称之为)
所以我开始做一些测试,看看最快的方法是什么
我所谈论的矩阵的维度大约为1000x1000
起初我想展示我的代码,也许你发现了一些明显错误的东西
1)时间测量
auto start_time = std::chrono::system_clock::now();
std::time_t ttp = std::chrono::system_clock::to_time_t(start_time);
std::cout << std::ctime(&ttp) << std::endl;
while((std::chrono::system_clock::now() - start_time) < std::chrono::seconds(time_span)) {
const Vector& calc_vec = in_vecs[distr(eng)];
const Matrix& calc_mat = in_mats[distr(eng)];
calc_mat.mulVec(calc_vec, o);
i++;
}
std::cout << "Performed : " << i << " Matrix-Vector multiplications in " << time_span << " sec's with naiv impl" << std::endl;
i=0;
start_time = std::chrono::system_clock::now();
ttp = std::chrono::system_clock::to_time_t(start_time);
std::cout << std::ctime(&ttp) << std::endl;
while((std::chrono::system_clock::now() - start_time) < std::chrono::seconds(time_span)) {
const Vector& calc_vec = in_vecs[distr(eng)];
const Matrix& calc_mat = in_mats[distr(eng)];
calc_mat.mulVec(calc_vec, o, ParallelMode::OpenMP);
i++;
}
std::cout << "Performed : " << i << " Matrix-Vector multiplications in " << time_span << " sec's with openmp impl" << std::endl;
我之前创建了200个随机输入矩阵和输入向量,并且在循环中我随机选择一个,以废弃缓存(我相信这将更好地模拟真实应用程序), time_span 设定为20秒
2)现在实现mat-vec乘法
static inline void NaivMultiplication
(const double* const * mat, const double* in, double* out, size_t inVecSize, size_t numRows) {
for(size_t row=0; row < numRows; row++) {
double sum=0;
for(size_t col=0; col < inVecSize; col++) {
sum += mat[row][col] * in[col];
}
out[row] = sum;
}
}
static inline void openMPMultiplication
(const double* const * mat, const double* in, double* out, size_t inVecSize, size_t numRows) {
#pragma omp parallel for schedule(static)
for(size_t row=0; row < numRows; row++) {
double sum=0;
for(size_t col=0; col < inVecSize; col++) {
sum += mat[row][col] * in[col];
}
out[row] = sum;
}
}
这些函数的签名相当奇怪,因为我想稍后使用OpenCL进行测试,我决定这种类似指针的重度实现
好的,结果如下: 在我的带有4个内核的i5-6600上,该程序能够执行
所以差不多2个内核正在忙于创建线程?
在我的手臂Exynos5422 Cortex™-A15 2Ghz上运行更糟糕的4个内核:
top 我检查过,4个核心实际上是100%
有人可以向我解释一下吗?
我的意思是,我没有数据依赖,没有同步(除了每个多操作结束时的隐式'join'
提前告诉我,我的英语(非母语人士;))答案 0 :(得分:0)
对于足够大的矩阵,mat-vec-mul
是内存带宽限制操作而不是cpu有界操作,这意味着通过从/向RAM读取/写入矩阵数据来限制速度。在这种情况下,您将无法通过使用多线程获得预期的加速。
The 3rd figure of this link显示当矩阵足够大(大于缓存)时,mat-vec-mul
的性能会下降很多。
由于ARM上的缓存大小和RAM带宽通常远低于桌面CPU,因此您可能会在多线程上受到更多惩罚。
另一方面,当你不知道里面的技巧/理论时,你自己实现基本矩阵/向量运算通常是一种非常糟糕的做法。好的方法是使用现有的高性能BLAS库,如OpenBLAS,MKL,cuBLAS,Eigen ......
由于您在ARM上使用现代C ++,我建议您使用Eigen。与OpenBLAS相比,它具有更友好的API,OpenBLAS也具有ARM优化的代码路径。您可以控制多线程以非常轻松地将性能与Eigen进行比较。您需要做的就是:
int len = 1000;
Eigen::MatrixXf mat = Eigen::MatrixXf::Random(len,len);
Eigen::VectorXf in = Eigen::VectorXf::Random(len);
Eigen::VectorXf out(len);
int num_threads = 4;
Eigen::setNbThreads(num_threads);
out = mat * in;