CUDA 并行计算优化策略总结

作者 | LustofLife@知乎

来源 | https://zhuanlan.zhihu.com/p/297201517

编辑 | 极市平台

导读

 

并行计算为了提高算法运行效率,本文通过以矩阵乘法(C = A * B)的各种实现思路以及优化方法总结为例子,过一遍cuda的几个基础优化策略。

3e054c8ce65f685f95a48cca93d2e9d9.png

本文脉络

  1. 关于矩阵乘法的问题描述 关于矩阵乘法的问题描述

  2. 优化策略的核心思想

  3. 例子

  • CPU上的代码实现:https://github.com/hova88/cuda-template/blob/main/src/matmul/naive.cu

  • GPU上的代码实现:https://github.com/hova88/cuda-template/blob/main/src/matmul/naive.cu

  • 优化策略一:https://github.com/hova88/cuda-template/blob/main/src/matmul/tiling.cu

  • 优化策略二:https://github.com/hova88/cuda-template/blob/main/src/matmul/coalescing.cu

  • 优化策略三:No Bank Conflict

  • 优化策略四:https://github.com/hova88/cuda-template/blob/main/src/matmul/comopt.cu

  • 优化策略五:https://github.com/hova88/cuda-template/blob/main/src/matmul/unroll.cu

  • 优化策略六:Prefetching

1 关于矩阵乘法的问题描述

参照NVIDIA官网教程——https://developer.nvidia.com/blog/cutlass-linear-algebra-cuda/

首先解决矩阵乘法问题更具体来说是解决GEMM(GEneral Matrix to Matrix Multiplication,通用矩阵乘法)问题。即C=αA*B+βC。其中ABC是矩阵。A是M×K矩阵,B是K×N矩阵,C是M×N矩阵。为了方便说明,后续的例子中假设标量alpha=beta=1。

855682362d4c32f1e9f14532752a6388.png

那么如何更高效的进行GEMM呢?

2:优化策略的核心思想

  • 1.减小缓存(cached)

  • 2.使写入速度跟上指令的计算速度

对于GEMM计算,最直接的想法就是loop,elements相乘再相加

for (int i = 0; i < M; ++i)                   //---->遍历A的行,行id记做i
    for (int j = 0; j < N; ++j)               //---->遍历B的列,列id记做j
       for (int k = 0; k < K; ++k)            //---->在行列i,j确定的前提下,进行对应元素的 相乘 和 加和 ,元素id记做k 
            C[i][j] += A[i][k] * B[k][j];     //---->最后输出到C的第i,j个元素

对于M=N=K的大型方阵,矩阵乘积中的数学运算数为O(N^3),而所需的数据量为O(N^2),从而产生N阶的计算强度。然而,利用理论计算强度(heoretical compute intensity)需要将重复使用每个元素O(N)。但是,上面的内积算法依赖于缓存(fast on-chip caches)中保存一个大的工作集,这会导致随着M、N和K增长时,CPU需要来回搬运数据,会累的要死。(不符合减小缓存的思想)

PS:一般来说,求两矩阵内积,K的维度数要远大于N,M(例如SVM中的核函数技巧),所以将大计算量的K维放在内循环不是一个聪明的决定。

一个更好的公式是通过构造K维上的循环作为最外层的循环来置换循环嵌套。这种形式的计算一次加载a列和B行,计算其外积,并将此外积的结果累加到矩阵C中。此后,a列和B行的结果将不再使用。

for (int k = 0; k < K; ++k)     // K dimension now outer-most loop
    for (int i = 0; i < M; ++i)
        for (int j = 0; j < N; ++j)
            C[i][j] += A[i][k] * B[k][j];

更进一步思考,如何进一步减少寄存空间的缓存大小?

上述方法的一个问题是,它要求矩阵C的所有M-by-N元素都是激活的,以存储每个乘法累加指令的结果。这样很难保证内存中的写入速度能够跟上CPU中的计算速度。

而如何去使得内存的写入速度与计算乘法累加指令的速度一样快呢?

-- 采用分块(Tile)的策略

重点来了,

首先 ,我们可以通过将矩阵C的工作空间Partitioning为大小为(M*tile-by-N*tile)Tile来矩阵C的工作空间大小(the working set size of C), 这些Tile的大小需要与存储器(on-chip memory)相适应。

然后,我们将用外积代替内积的策略应用到每一块Tile上。就像以下循环嵌套这样。

for (int m = 0; m < M; m += Mtile)                // iterate over M dimension
    for (int n = 0; n < N; n += Ntile)            // iterate over N dimension
        for (int k = 0; k < K; ++k)       //----> like above example
            for (int i = 0; i < Mtile; ++i)       // compute one tile 
                for (int j = 0; j < Ntile; ++j) {
                    int row = m + i;
                    int col = n + j;
                    C[row][col] += A[row][k] * B[k][col];
                }

对于C上的每一块Tile,都只取了一次A和B中的Tiles,使其达到*O(N)*的计算强度。示意图就类似下面这样。(如果对 global memory / shared memory / register file/ SM cores 不太清楚的话,还是建议好好研究研究一下,对后续的优化策略理解很有帮助!)

1fa8b1c2f5367c783354ce28e66a24d6.png

对于GPU来说,The size of each tile of C may be chosen to match the capacity of the L1 cache or registers of the target processor, and the outer loops of the nest may be trivially parallelized. This is a great improvement !

Here, you can see data movement from global memory to shared memory (matrix to thread block tile), from shared memory to the register file (thread block tile to warp tile), and from the register file to the CUDA cores for computation (warp tile to thread tile).

参考链接

Matrix Multiplication CUDA——https://ecatue.gitlab.io/gpu2018/pages/Cookbook/matrix_multiplication_cuda.html

本文仅做学术分享,如有侵权,请联系删文。

重磅!计算机视觉工坊-学习交流群已成立

扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。

同时也可申请加入我们的细分方向交流群,目前主要有ORB-SLAM系列源码学习、3D视觉CV&深度学习SLAM三维重建点云后处理自动驾驶、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、深度估计、学术交流、求职交流等微信群,请扫描下面微信号加群,备注:”研究方向+学校/公司+昵称“,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进去相关微信群。原创投稿也请联系。

5cec87edec6732002cf2ca000b9c8967.png

▲长按加微信群或投稿

815fd379573a6c55ce43d68d55b7c9da.png

▲长按关注公众号

3D视觉从入门到精通知识星球:针对3D视觉领域的视频课程(三维重建系列三维点云系列结构光系列手眼标定相机标定、激光/视觉SLAM、自动驾驶等)、知识点汇总、入门进阶学习路线、最新paper分享、疑问解答五个方面进行深耕,更有各类大厂的算法工程人员进行技术指导。与此同时,星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息,打造成集技术与就业为一体的铁杆粉丝聚集区,近4000星球成员为创造更好的AI世界共同进步,知识星球入口:

学习3D视觉核心技术,扫描查看介绍,3天内无条件退款

07c9e3a995851c2ea308b13b4b12ed4d.png

 圈里有高质量教程资料、可答疑解惑、助你高效解决问题

觉得有用,麻烦给个赞和在看~

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值