通过向量乘以矩阵的行(低级优化)?

时间:2017-09-23 16:15:30

标签: c++ optimization 64-bit intel-mkl avx2

我正在优化一个函数,我想摆脱缓慢的for循环。我正在寻找一种更快的方法来将矩阵的每一行乘以一个向量。

我不是在寻找'经典'乘法。

EG。我有一个1024列和20行的矩阵和一个长度为1024的向量。结果,我希望矩阵1024 x 20,每行乘以向量。

我现在在做什么我在矩阵行上的for循环中迭代并使用mkl v?Mul执行当前矩阵行和向量的元素乘法元素。任何想法如何改善这个?

问题是副本Multiply rows of matrix by vector? ,但对于C ++,可能有低级优化和MKL,而不是R

2 个答案:

答案 0 :(得分:2)

使用Eigen matrix library,您所做的实际上是乘以对角矩阵。如果你有一个任意多行和20列的矩阵,你可以写下面的内容(不值得为它做一个函数):

void multRows(Eigen::Matrix<double, Eigen::Dynamic, 20>& mat,
              const Eigen::Matrix<double,20,1>& vect)
{
    mat = mat * vect.asDiagonal();
}

如果编译器启用了EX,它会生成AVX2代码。 您可能想要尝试在您的用例中存储mat行主要或专栏主题是否更有效。

附录(由于编辑过的问题):如果你有(超过)20多列,你应该只使用动态大小的矩阵:

void multRows(Eigen::MatrixXd& mat, const Eigen::VectorXd& vect)
{
    mat = mat * vect.asDiagonal();
}

答案 1 :(得分:2)

大多数最近的处理器都支持AVX技术。它提供了一个包含4个双精度(256位寄存器)的向量。因此,此优化的解决方案可能是使用AVX。为此,我使用x86intrin.h库实现了它,它是GCC编译器的一部分。我还使用OpenMP使解决方案成为多线程的。

//gcc -Wall  -fopenmp -O2 -march=native -o "MatrixVectorMultiplication" "MatrixVectorMultiplication.c" 
//gcc 7.2, Skylake Corei7-6700 HQ
//The performance improvement is significant (5232 Cycle in my machine) but MKL is not available to test
#include <stdio.h>
#include <x86intrin.h>
double A[20][1024] __attribute__(( aligned(32))) = {{1.0, 2.0, 3.0, 3.5, 1.0, 2.0, 3.0, 3.5}, {4.0, 5.0, 6.0, 6.5,4.0, 5.0, 6.0, 6.5},{7.0, 8.0, 9.0, 9.5, 4.0, 5.0, 6.0, 6.5 }};//The 32 is for 256-bit registers of AVX
double B[1024]  __attribute__(( aligned(32))) = {2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0 }; //the vector
double C[20][1024] __attribute__(( aligned(32)));//the results are stored here

int main()
{
    int i,j;
    __m256d vec_C1, vec_C2, vec_C3, vec_C4;

    //begin_rdtsc
    //get the start time here
    #pragma omp parallel for
    for(i=0; i<20;i++){
        for(j=0; j<1024; j+=16){

            vec_C1 = _mm256_mul_pd(_mm256_load_pd(&A[i][j]), _mm256_load_pd(&B[j]));
            _mm256_store_pd(&C[i][j], vec_C1);

            vec_C2 = _mm256_mul_pd(_mm256_load_pd(&A[i][j+4]), _mm256_load_pd(&B[j+4]));
            _mm256_store_pd(&C[i][j+4], vec_C2);

            vec_C3 = _mm256_mul_pd(_mm256_load_pd(&A[i][j+8]), _mm256_load_pd(&B[j+8]));
            _mm256_store_pd(&C[i][j+8], vec_C3);

            vec_C4 = _mm256_mul_pd(_mm256_load_pd(&A[i][j+12]), _mm256_load_pd(&B[j+12]));
            _mm256_store_pd(&C[i][j+12], vec_C4);

        }
    }
    //end_rdtsc
    //calculate the elapsead time

    //print the results
    for(i=0; i<20;i++){
        for(j=0; j<1024; j++){
            //printf(" %lf", C[i][j]);
        }
        //printf("\n");
    }

    return 0;
}