OpenMP用于矩阵乘法

时间:2014-06-25 03:04:27

标签: c++ matrix-multiplication openmp

我是OpenMP的新手,我正拼命地学习。我试图在visual studio 2012中用C ++编写一个示例代码来实现矩阵乘法。我希望有OpenMP经验的人可以看看这段代码并帮助我获得最终的速度/并行化:

#include <iostream>
#include <stdlib.h>
#include <omp.h>
#include <random>
using namespace std;

#define NUM_THREADS 4

// Program Variables
double**        A;
double**        B;
double**        C;
double          t_Start;
double          t_Stop;
int             Am;
int             An;
int             Bm;
int             Bn;

// Program Functions
void            Get_Matrix();
void            Mat_Mult_Serial();
void            Mat_Mult_Parallel();
void            Delete_Matrix();


int main()
{
    printf("Matrix Multiplication Program\n\n");
    cout << "Enter Size of Matrix A: ";
    cin >> Am >> An;
    cout << "Enter Size of Matrix B: ";
    cin >> Bm >> Bn;

    Get_Matrix();
    Mat_Mult_Serial();
    Mat_Mult_Parallel();


    system("pause");
    return 0;

}


void Get_Matrix()
{
    A = new double*[Am];
    B = new double*[Bm];
    C = new double*[Am];
    for ( int i=0; i<Am; i++ ){A[i] = new double[An];}
    for ( int i=0; i<Bm; i++ ){B[i] = new double[Bn];}
    for ( int i=0; i<Am; i++ ){C[i] = new double[Bn]; }

    for ( int i=0; i<Am; i++ )
    {
         for ( int j=0; j<An; j++ )
         {
             A[i][j]= rand() % 10 + 1;
         }
    }

    for ( int i=0; i<Bm; i++ )
    {
        for ( int j=0; j<Bn; j++ )
        {
            B[i][j]= rand() % 10 + 1;
        }
    }
    printf("Matrix Create Complete.\n");
}


void Mat_Mult_Serial()
{
    t_Start = omp_get_wtime();
    for ( int i=0; i<Am; i++ )
    {
        for ( int j=0; j<Bn; j++ )
        {
            double temp = 0;
            for ( int k=0; k<An; k++ )
            {
                temp += A[i][k]*B[k][j];
            }
        }
    }
    t_Stop = omp_get_wtime() - t_Start;
    cout << "Serial Multiplication Time: " << t_Stop << " seconds" << endl;
    }


void Mat_Mult_Parallel()
{
    int i,j,k;
    t_Start = omp_get_wtime();

    omp_set_num_threads(NUM_THREADS);
    #pragma omp parallel for private(i,j,k) schedule(dynamic)
    for ( i=0; i<Am; i++ )
    {
        for ( j=0; j<Bn; j++ )
        {
            //double temp = 0;
            for ( k=0; k<An; k++ )
            {
                C[i][j] += A[i][k]*B[k][j];
            }
        }
    }

    t_Stop = omp_get_wtime() - t_Start;
    cout << "Parallel Multiplication Time: " << t_Stop << " seconds." << endl;
}


void Delete_Matrix()
{
    for ( int i=0; i<Am; i++ ){ delete [] A[i]; }
    for ( int i=0; i<Bm; i++ ){ delete [] B[i]; }
    for ( int i=0; i<Am; i++ ){ delete [] C[i]; }

    delete [] A;
    delete [] B;
    delete [] B;
}

2 个答案:

答案 0 :(得分:2)

我的示例基于我为并行教学创建的矩阵类。如果您有兴趣,请随时与我联系。 有几种方法可以加速矩阵乘法:

存储

以行主要顺序使用一维数组以更快的方式访问元素 您可以使用A [i * An + j]

访问A(i,j)

使用循环不变优化

for (int i = 0; i < m; i ++)
    for (int j = 0; j < p; j ++)
    {
        Scalar sigma = C(i, j);
        for (int k = 0; k < n; k ++)
            sigma += (*this)(i, k) * B(k, j);
        C(i, j) = sigma;
    }

这可以防止在最内循环中多次重新计算C(i,j)。

更改循环顺序&#34;对于k&lt; - &gt;对于我&#34;

for (int i = 0; i < m; i ++)
    for (int k = 0; k < n; k ++)
    {
        Aik = (*this)(i, k);
        for (int j = 0; j < p; j ++)
            C(i, j) += Aik * B(k, j);
    }

这允许玩空间data locality

使用循环阻止/平铺

for(int ii = 0; ii < m; ii += block_size)
    for(int jj = 0; jj < p; jj += block_size)
        for(int kk = 0; kk < n; kk += block_size)
            #pragma omp parallel for // I think this is the best place for this case
            for(int i = ii; i < ii + block_size; i ++)
                for(int k = kk; k < kk + block_size; k ++)
                {
                    Scalar Aik = (*this)(i, k);
                    for(int j = jj; j < jj + block_size; j ++)
                        C(i, j) +=  Aik * B(k, j);
                }

这可以使用更好的时态数据局部性。最佳的block_size取决于您的体系结构和矩阵大小。

然后并行化!

一般来说,#pragma omp parallel for应该是一个最好的循环。也许在两个第一个外部循环中使用两个并行循环可以提供更好的结果。这取决于你使用的架构,矩阵大小......你必须测试! 由于矩阵乘法具有静态工作负载,因此我将使用静态计划。

鼹鼠优化!

你可以loop nest optimization。 您可以对代码进行矢量化。 您可以查看BLAS如何执行此操作。

答案 1 :(得分:0)

我是OpenMP的新手,这段代码很有启发性。但是我发现串行版本中的错误使其比并行版本具有不公平的速度优势。

您没有像在并行版本中那样编写C[i][j] += A[i][k]*B[k][j];,而是在序列版本中编写了temp += A[i][k]*B[k][j];。这要快得多(但不能帮助您计算C矩阵)。所以你不是在比较苹果和苹果,这使得并行代码看起来比较慢。当我修复此行并在我的笔记本电脑上运行它(允许2个线程)时,并行版本的速度几乎快了两倍。还不错!