栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

cuda矩阵乘法初级(简单理解)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

cuda矩阵乘法初级(简单理解)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

CUDA矩阵乘法
  • 矩阵规模
  • 一、一维并行
    • 1.一维线程并行(thread)
    • 2.一维块线程并行(block)
    • 3.一维共享A一行程并行(shared)
  • 二、二维并行
    • 1.共享存储二维分块


矩阵规模

矩阵A: n行,l列
矩阵B:l行,m列
结果矩阵C=A✖️B: n行,m列


一、一维并行 1.一维线程并行(thread)

一个线程并行一个A1行✖️B1列

  1. 需要线程个数:n✖️m个,一个线程并行一个A1行✖️B1列, 即一个线程对应结果矩阵的C的一个点,e.g. c(1,1)=xid0的计算结果,所以一共需要:
xid0=a1hang✖️b1liexid1=a1hang✖️b2lie…xid(m-1)=a1hang✖️bmlie
xid(m)=a2hang✖️b1liexid(m+1)=a2hang✖️b2lie…xid(2m-1)=a2hang✖️bmlie
.………
xid((n-1)*m)=anhang✖️b1liexid((n-1)*m+1)=anhang✖️b2lie…xid((n-1)*(m-1))=anhang✖️bmlie

即可知一共需要n✖️m个线程并行,每个线程处理一个向量✖️向量
2. 总共的第几个线程:xid=block.x*blockDim.x+threadIdx.x;
3. 因为线程总数为n✖️m个,所以当前线程xid对应A的第几行=xid/n,
当前线程xid对应B的第几列=xid%n;

 const int idx = blockIdx.x*blockDim.x+threadIdx.x;
    const int Arow = idx / n;
    const int Bcolumn = idx % n;

    //计算矩阵乘法
    if (Arow < n && Bcolumn < m)
    {
        float t = 0;

        for (i = 0; i < l; i++)
        {
            t += A[Arow * l + i] * B[i * m + Bcolumn];
        }
        C[Arow * m + Bcolumn] = t;
    }
2.一维块线程并行(block)

一个block代表矩阵A1行,块中一个线程代表B1列,B矩阵的列已每块线程数跳步.

  1. 一共需要n个block,如果可以给的最大块数小于A的行数则已gridDim.x为block的跳步.
    线程数为j个,如果j小于B的列数则以blockDim.x为跳步
thread0thread1thread2thread3thread0+blockDim.xhread1+blockDim.x…thread3+m/blockDim.x
block0a1hang✖️b1liea1hang✖️b2liea1hang✖️b3liea1hang✖️b4liea1hang✖️b5liea1hang✖️b6lie…a1hang✖️bmlie
block1a2hang✖️b1liea2hang✖️b2liea2hang✖️b3liea2hang✖️b4liea2hang✖️b5liea2hang✖️b6lie…a2hang✖️bmlie
.…………………
block(n-1)anhang✖️b1lieanhang✖️b2lieanhang✖️b3lieanhang✖️b4lieanhang✖️b5lieanhang✖️b6lie…anhang✖️bmlie
    
    //计算矩阵乘法
    for (int Arow=blockIdx.x;Arow
    for (int Bcolumn=threadIdx.x; Bcolumn < m;Bcolumn+=blockDim.x)
    {
        float num = 0.0;

        for (int i = 0; i < l; i++)
        {
            num += A[Arow * l + i] * B[i * m + Bcolumn];
        }
        C[Arow * m + Bcolumn] = num;
    }
    }
3.一维共享A一行程并行(shared)

上个方法中A的每行一个block,把A的每行给共享存储.

  1. 共享存储速的关键是数据重复利用,因为从全局读一次数据,然后重复多用几次最合适,e.g.矩阵乘法中,A的每一行就成了B的m个列,即A1的每行都用重复用了m次.可将A的每行放进共享存储.
__shared__ shA[l];
int Arow=blockIdx.x;
for(int Acolumn=threadIdx.x; Acolumn < l;Bcolumn+=blockDim.x)
{
    shA[Acolumn]=A[Arow*l+Acolumn];//把A的每行给shared
}
__syncthreads();

for(int Bcolumn=threadIdx.x; Bcolumn
    float num = 0.0;
    for (int i = 0; i < l; i++)//A1行(已在shared中)乘B1列
    {
        num += shA[i] * B[i * m + Bcolumn];
    }
     C[Arow * m + Bcolumn] = num;
}    
   
二、二维并行 1.共享存储二维分块
  1. 问题与改进
    当A的列数>shared memory最大容量时->采用分块存在shared中;
    一维时只存A矩阵->二维将B矩阵也分块存入;
  2. 解决办法:设置两个二维的share memory : shA[][Tl_size]放A的shared子矩阵,shB[Tl_size][]放B的shared子矩阵,A横着更新shA子矩阵,B竖着更新shB子矩阵

template  __global__ void
matrixMulCUDA(float *C, float *A, float *B, int wA, int wB)
{
 // 累加,得到行 * 列的值
 float numsub = 0.0;

 // 循环次数等于widthA / 16,把长向量点积运算转化为两个短向量点积后的和
 //因为B的宽条是竖着的,每宽条中宽是往下移动的,往下移动即代表动一行要增长m个元素,动一块即移动块宽Bsize✖️m个元素,所以步长为每次BLOCK_SIZE * m; 因为A每宽条是横着的,所以宽条中的块是横着移动,当前行每往右移动一块,增长块的块度Bsize的元素,即每宽条每移动一块步长就为Bsize
 for (int j = 0; a 
 // 定义A的共享子矩阵变量,因为__shared__声明,所以同一个block中的所有threads都可见,
 //每个thread填充一个元素,并计算一个行列乘积,减小带宽使用
 __shared__ float As[BLOCK_SIZE][BLOCK_SIZE];

 // 定义A的共享子矩阵变量
 __shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];

 // 每个block包含16 * 16 个线程,所以每个线程负责一个矩阵元素的拷贝(注意同步)
 //A的shared块更新方式:1.块横着一块一块更新一宽条:l * BLOCK_SIZE * blockIdx.y为前面有的宽条:blockIdx.y表示第几个宽条,宽条的宽度为Bsize,则BLOCK_SIZE * blockIdx.y为前面一共有几行,每行有l列(BLOCK_SIZE * blockIdx.y)*l则为一共前面有多少元素. 2.hreadIdx.y 表示当前宽条的第几行,l * hreadIdx.y表示本块中本行在本块前的前面行一共的元素.  3.threadIdx.x表示本行第几个元素,所以l * BLOCK_SIZE * blockIdx.y + l * hreadIdx.y + threadIdx.x表示前面宽行中元素个数➕本宽条中前几行元素个数➕本行第几个元素
 As[ty][tx] = A[l * BLOCK_SIZE * blockIdx.y + l * threadIdx.y + threadIdx.x+i];
 Bs[ty][tx] = B[ BLOCK_SIZE * blockIdx.x + m * (threadIdx.y + i) + threadIdx.x];

 // Synchronize to make sure the matrices are loaded
 __syncthreads();

 // 每个线程计算 子矩阵的行列乘积,大循环外边还有累加,累加的是不同子矩阵点积和
 for (int k = 0; k < BLOCK_SIZE; ++k)
 {
 numsub += As[ty][k] * Bs[k][tx];
 }

 // 再次同步
 __syncthreads();
 }

if(BLOCK_SIZE * blockIdx.y + threadIdx.y 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1019127.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号