2.3CUDA矩阵乘法

CPU 矩阵乘法

能相乘的两个矩阵,必须满足一个矩阵的行数和第二个矩阵的列数相同.

A(N*P) * B(P*M) = C(N*M). 其中P是行数,N是列数, 从宽高的角度来说,即 A的宽度和B的高度是相同的.C矩阵 = ha * wb.

其中C(i,j) = A矩阵中的i行和B矩阵中的j列进行点乘得到该点的值.

//C = A*B
void MatrixMulCPU(float* _C,const float *_A,const float *_B,int _wa,int _ha,int _wb)
{
    float sum = 0;
    for (int i = 0; i < _ha; ++i)
    {
        for (int j = 0; j < _wb; ++j)
        {
            sum = 0;
            for (int k = 0; k < _wa; ++k)
            {       //i*_wa得到当前对应的是A矩阵中的哪一行,+k对应当前行的哪一列.矩阵A的stride是wa       //j对应当前矩阵B的哪一列,+k*wb依次表示第0行的j列,第1行的j列...第wa-1行的j列.矩阵B的stride是wb
                sum += (float)_A[i*_wa+k]*(float)_B[k*_wb+ j];
            }
            _C[i*_wb+j] = (float)sum;
        }
    }
}

简单矩阵乘法

C(i,j) = sum { A(i,k)*B(k,j) } 0<=k < _wa;耦合程度很小,所以我们可以通过划分区域的方法,让每个线程负责一个区域。
怎么划分呢?首先最初的想法是让每一个线程计算一个C(i,j),那么估算一下,应该需要height_c*width_c,也就是ha*wb个线程。进一步,我们将矩阵按一个大方格Block划分,如果一个方格Block大小是16*16,那么矩阵80*48的可以表示为5(*16) * 3(*16),即5*3个大格子(Grid),所以grid的划分自然就是(height_c/16) *(width_c/16)个线程了。

好了,划分完后,内核代码如下: 这个kernel的代码只是把外层两个循环变成

计算版本0:

__global__ void matrix_kernel_0(float* _C,const float* _A,const float *_B,int _wa,int _wb)
{
    float sum = 0;
    //找出该线程所在的行列
    int row = blockIdx.y*blockDim.y + threadIdx.y;
    int col = blockIdx.x*blockDim.x + threadIdx.x;

    //线程Thread(row,col)负责计算C(row,col)
    for (int i = 0; i < _wa; ++i)
    {
        sum += _A[row*_wa + i]*_B[i*_wb + col];
    }
    _C[row*_wb + col] = sum;
}

这个是Heterogeneous Parallel Programming lab3:Basic Matrix Matrix Multiplication的代码:

#include <wb.h>

#define wbCheck(stmt)                                                            do {                                                                             cudaError_t err = stmt;                                                        if (err != cudaSuccess) {                                                        wbLog(ERROR, "Failed to run stmt ", #stmt);                                    wbLog(ERROR, "Got CUDA error ...  ", cudaGetErrorString(err));                 return -1;                                                                   }                                                                            } while (0)

#if 0 //This is C verison matrixMUl
//C = A*B
void MatrixMulCPU(float* _C,const float *_A,const float *_B,int _wa,int _ha,int _wb)
{
    float sum = 0;
    for (int i = 0; i < _ha; ++i)
    {
        for (int j = 0; j < _wb; ++j)
        {
            sum = 0;
            for (int k = 0; k < _wa; ++k)
            {
                sum += (float)_A[i*_wa+k]*(float)_B[k*_wb+ j];
            }
            _C[i*_wb+j] = (float)sum;
        }
    }
}
#endif

// Compute C = A * B , Matrix C = hA * wB = rowA * columnB
__global__ void matrixMultiply(float *A, float *B, float *C, int numARows,
                               int numAColumns, int numBRows, int numBColumns,
                               int numCRows, int numCColumns) {
  //@@ Insert code to implement matrix multiplication here
     float sum = 0.0f;

    int row = blockIdx.y*blockDim.y + threadIdx.y;
    int col = blockIdx.x*blockDim.x + threadIdx.x;

    if(row < numCRows && col < numCColumns){
        for (int i = 0; i < numAColumns; ++i)
        {
            sum += A[row*numAColumns + i] * B[i*numBColumns + col];
        }
        C[row*numBColumns + col] = sum;
    }
    printf("C = %f\n",C[row*numBColumns + col]);

}

int main(int argc, char **argv) {
  wbArg_t args;
  float *hostA; // The A matrix
  float *hostB; // The B matrix
  float *hostC; // The output C matrix
  float *deviceA;
  float *deviceB;
  float *deviceC;
  int numARows;    // number of rows in the matrix A
  int numAColumns; // number of columns in the matrix A
  int numBRows;    // number of rows in the matrix B
  int numBColumns; // number of columns in the matrix B
  int numCRows;    // number of rows in the matrix C (you have to set this)
  int numCColumns; // number of columns in the matrix C (you have to set this)

  args = wbArg_read(argc, argv);

  wbTime_start(Generic, "Importing data and creating memory on host");
  hostA =
      ( float * )wbImport(wbArg_getInputFile(args, 0), &numARows, &numAColumns);
  hostB =
      ( float * )wbImport(wbArg_getInputFile(args, 1), &numBRows, &numBColumns);
  //@@ Set numCRows and numCColumns
  numCRows = 0;
  numCColumns = 0;
  if(numAColumns != numBRows){
    wbLog(TRACE, "numAColumns != numBRows, Break ");
    return 1;
  }
  numCRows = numARows;
  numCColumns = numBColumns;
  unsigned int A_size = numARows * numAColumns * sizeof(float);
  unsigned int B_size = numBRows * numBColumns * sizeof(float);
  unsigned int C_size = numCRows * numCColumns * sizeof(float);
  //@@ Allocate the hostC matrix
  hostC = ( float * )malloc(C_size);
  wbTime_stop(Generic, "Importing data and creating memory on host");

  wbLog(TRACE, "The dimensions of A are ", numARows, " x ", numAColumns);
  wbLog(TRACE, "The dimensions of B are ", numBRows, " x ", numBColumns);

  wbTime_start(GPU, "Allocating GPU memory.");
  //@@ Allocate GPU memory here
  wbCheck(cudaMalloc((void**)&deviceA, A_size));
  wbCheck(cudaMalloc((void**)&deviceB, B_size));
  wbCheck(cudaMalloc((void**)&deviceC, C_size));
  wbTime_stop(GPU, "Allocating GPU memory.");

  wbTime_start(GPU, "Copying input memory to the GPU.");
  //@@ Copy memory to the GPU here
  wbCheck(cudaMemcpy(deviceA, hostA, A_size, cudaMemcpyHostToDevice));
  wbCheck(cudaMemcpy(deviceB, hostB, B_size, cudaMemcpyHostToDevice));
  wbCheck(cudaMemcpy(deviceC, hostC, C_size, cudaMemcpyHostToDevice));
  wbTime_stop(GPU, "Copying input memory to the GPU.");

  //@@ Initialize the grid and block dimensions here
  dim3 DimGrid((numCColumns-1)/16+1, (numCRows-1)/16+1, 1);
  dim3 DimBlock(16, 16, 1);

  wbTime_start(Compute, "Performing CUDA computation");
  //@@ Launch the GPU Kernel here
  matrixMultiply<<< DimGrid, DimBlock >>>(deviceA, deviceB, deviceC, numARows, numAColumns, numBRows, numBColumns, numCRows, numCColumns);
  cudaDeviceSynchronize();
  wbTime_stop(Compute, "Performing CUDA computation");

  wbTime_start(Copy, "Copying output memory to the CPU");
  //@@ Copy the GPU memory back to the CPU here
  //@@ Copy the GPU memory back to the CPU here
  wbCheck(cudaMemcpy(hostC, deviceC, C_size, cudaMemcpyDeviceToHost));

  wbTime_stop(Copy, "Copying output memory to the CPU");

  wbTime_start(GPU, "Freeing GPU Memory");
  //@@ Free the GPU memory here
  wbCheck(cudaFree(deviceA));
  wbCheck(cudaFree(deviceB));
  wbCheck(cudaFree(deviceC));

  wbTime_stop(GPU, "Freeing GPU Memory");

  wbSolution(args, hostC, numCRows, numCColumns);

  free(hostA);
  free(hostB);
  free(hostC);

  return 0;
}

使用tile来划分矩阵乘法

另外一种思路,我们不让每一个线程完整计算一个C(i,j),通过C(i,j) = sum { A(i,k)*B(k,j) }发现,我们还可以再细度划分:
Csub(i,j) = sum{A(i,ksub+offsetA)*B(ksub+offsetB,j)}  0<=ksub < blockSize
C(i,j) = sum{Csub(i,j)}
就是把矩阵分成n*n个大的子块,然后每一个block负责计算子块i 和 子块j的子乘积,计算完毕后加起来则可。这里主要引入shared Memory来提高程序效率.

__global__ void matrix_kernel_1(float* _C,const float* _A,const float *_B,int _wa,int _wb)
{
    int bx = blockIdx.x; //Block X的当前位置
    int by = blockIdx.y; //Block y的当前位置
    int tx = threadIdx.x;
    int ty = threadIdx.y;

    //该block要处理的A
    int aBegin = _wa*(by*BLOCK_SIZE);//A(0,by)
    int aEnd = aBegin + _wa - 1;
    int aStep = BLOCK_SIZE;//offsetA

    int bBegin = BLOCK_SIZE*bx;//B(bx,0)
    int bStep = BLOCK_SIZE*_wb;//offsetB

    float cSub = 0;
    for (int a = aBegin,b = bBegin; a <= aEnd; a += aStep,b += bStep)
    {
        __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
        __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
        //每个线程负责一个元素拷贝
        As[ty][tx] = _A[a + _wa*ty + tx];
        Bs[ty][tx] = _B[b + _wb*ty + tx];

        __syncthreads();

        //每个线程负责计算一个子块i 和 子块j的子乘积
        for (int k = 0; k < BLOCK_SIZE; ++k)
        {
            cSub += As[ty][k]*Bs[k][tx];
        }

        __syncthreads();
    }

    //全局地址,向全局寄存器写回去
    //一个线程负责一个元素,一个block负责一个子块
    int cIndex = (by*BLOCK_SIZE + ty)*_wb + (bx*BLOCK_SIZE + tx);
    _C[cIndex] = cSub;
}
时间: 2024-11-09 06:18:22

2.3CUDA矩阵乘法的相关文章

矩阵乘法的Strassen算法详解

题目描述 请编程实现矩阵乘法,并考虑当矩阵规模较大时的优化方法. 思路分析 根据wikipedia上的介绍:两个矩阵的乘法仅当第一个矩阵B的列数和另一个矩阵A的行数相等时才能定义.如A是m×n矩阵和B是n×p矩阵,它们的乘积AB是一个m×p矩阵,它的一个元素其中 1 ≤ i ≤ m, 1 ≤ j ≤ p. 值得一提的是,矩阵乘法满足结合律和分配率,但并不满足交换律,如下图所示的这个例子,两个矩阵交换相乘后,结果变了: 下面咱们来具体解决这个矩阵相乘的问题. 解法一.暴力解法 其实,通过前面的分析

51nod 1137 矩阵乘法

基本的矩阵乘法 中间for(int j=0;i<n;i++)  //这里写错了   应该是j<n 晚上果然  效率不行 等会早点儿睡 //矩阵乘法 就是 两个矩阵 第一个矩阵的列 等与 第二个矩阵的行相同 // 然后ans[i][j] += a[i][k] * b[k][j]; #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 150; int n; ll a[ma

矩阵乘法

矩阵加法就是相同位置的数字加一下,矩阵减法也类似 矩阵乘以一个常数,就是所有位置都乘以这个数 矩阵乘以矩阵 计算规则是,第一个矩阵第一行的每个数字(2和1),各自乘以第二个矩阵第一列对应位置的数字(1和1),然后将乘积相加( 2 x 1 + 1 x 1),得到结果矩阵左上角的那个值3 矩阵的本质就是线性方程式,两者是一一对应关系.如果从线性方程式的角度,理解矩阵乘法就毫无难度.下面是一组线性方程式 矩阵的最初目的,只是为线性方程组提供一个简写形式 下面是严格的证明.有三组未知数 x.y 和 t,

矩阵乘法&lt;简单总结&gt;

原理:矩阵相乘最重要的方法是一般矩阵乘积.它只有在第一个矩阵的 行数 和第二个矩阵的 列数 相同时才可进行.若A为m×n矩阵,B为n×p矩阵,则他们的乘积AB会是一个m×p矩阵. 若A=    a    b    c        d    e    f        g    h    i    B=    A    D        B    E        C    F    A*B=CC=    aA+bB+cC    aD+bE+cF        dA+eB+fC    dD+eE

POJ2778 DNA Sequence Trie+矩阵乘法

题意:给定N个有A C G T组成的字符串,求长度为L的仅由A C G T组成的字符串中有多少个是不含给定的N个字符串的题解: 首先我们把所有的模式串(给定的DNA序列)建Trie,假定我们有一个匹配串,并且在匹配过程到S[i]这个字符时匹配到了Trie上的某个节点t,那么有两种可能: 匹配失败:t->child[S[i]]为空,跳转到t->fail,因此t->fail一定不能是某个模式串的结尾: 匹配成功:跳转到t->child[S[i+1]],因此t->child[S[i

【CDQ】BZOJ 2738 矩阵乘法

题意:给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数 思路: 整体二分+二维树状数组 二分询问的答案mid,将数值小等mid的全部插入二维树状数组 然后查询每个矩阵内的元素个数,若数量>K-1则放左边,否则放右边 继续向下分治,左边二分l-mid,右边mid-r 代码: #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include

codevs矩阵乘法系列

T1:矩阵乘法板子题,练手. #include <map> #include <set> #include <cmath> #include <ctime> #include <queue> #include <stack> #include <cstdio> #include <string> #include <vector> #include <cstdlib> #include

基于OpenMP的矩阵乘法实现及效率提升分析

一.  矩阵乘法串行实现 例子选择两个1024*1024的矩阵相乘,根据矩阵乘法运算得到运算结果.其中,两个矩阵中的数为double类型,初值由随机数函数产生.代码如下: #include <iostream> #include <omp.h> // OpenMP编程需要包含的头文件 #include <time.h> #include <stdlib.h> using namespace std; #define MatrixOrder 1024 #def

C语言 &#183; 矩阵乘法 &#183; 算法训练

问题描述 给定一个N阶矩阵A,输出A的M次幂(M是非负整数) 例如: A = 1 2 3 4 A的2次幂 7 10 15 22 输入格式 第一行是一个正整数N.M(1<=N<=30, 0<=M<=5),表示矩阵A的阶数和要求的幂数 接下来N行,每行N个绝对值不超过10的非负整数,描述矩阵A的值 输出格式 输出共N行,每行N个整数,表示A的M次幂所对应的矩阵.相邻的数之间用一个空格隔开 样例输入 2 21 23 4 样例输出 7 1015 22 代码如下: #include<s