手臂上使用openMP进行矩阵向量乘法

时间:2016-04-20 09:09:07

标签: c++ multithreading arm openmp

我对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上,该程序能够执行

  • 60.000乘法单线程
  • 150.000乘法多线程

所以差不多2个内核正在忙于创建线程?

在我的手臂Exynos5422 Cortex™-A15 2Ghz上运行更糟糕的4个内核:

  • 15.000乘法单线程
  • 9.000 乘法多线程
  • 这两个测试都是在linux上用gcc-5.3(-O2 -fopenmp)
  • 运行的
  • top 我检查过,4个核心实际上是100%

  • 有人可以向我解释一下吗?

  • 每次乘法创建4个线程真的太贵了吗?

我的意思是,我没有数据依赖,没有同步(除了每个多操作结束时的隐式'join'

提前告诉我,我的英语(非母语人士;))

1 个答案:

答案 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;