NVIDIA CUDA统一计算设备架构编程手册 (6)

ZDNet软件频道时间2008-07-03作者:huawenguang | PhysDev论坛
本文关键词:NVIDIA CUDA 统一计算 GPU

第6章 矩阵乘法的示例


6.1 概述


计算两个维度分别为(wA, hA)和 (wB, wA) 的矩阵A和B的乘积C的任务以下列方式分为多个线程:
 每个线程负责计算C 的一个平方子矩阵Csub ;
 块内的每个线程负责计算Csub的一个元素。
选择Csub的维度block_size等于16,以便每块的线程数是warp大小的倍数(参见5.2),并且保持低于每块的最大线程数(参见附录A)。
如图6-1所示,Csub 等于两个矩形矩阵的乘积:维度为(wA, block_size)的子矩阵A,与Csub具有相同的行索引,维度为(block_size, wA)的B的子矩阵,与Csub具有相同的列索引。为了适应设备的资源,这两个矩形矩阵可根据需要划分为许多维度为block_size的平方矩阵,并且Csub计算为这些平方矩阵的乘积之和。其中每个乘积的执行过程是:首先使用一个加载每个矩阵的一个元素的线程,将两个相应的平方矩阵从全局内存加载到共享内存,然后让每个线程计算乘积的一个元素。每一线程将其中每个乘积的结果累计到寄存器中,执行完毕后,将结果写入全局内存。
通过以这种方式分块计算,我们可以有效利用快速的共享内存,并节省许多全局内存带宽,因为A和B仅从全局内存读取(wA / block_size)次。
尽管如此,编写此示例是为了清楚地说明各种CUDA编程原则,并非是为了为一般的矩阵乘法提供高性能的内核,所以不应如此构造。

每一线程块计算C的一个子矩阵Csub。块内的每一线程计算Csub的一个元素。
图6-1. 矩阵乘法


6.2 源码清单

6.3 源码攻略


源码包含下列两个函数:
 Mul(),作为Muld()的包装器的主机函数。
 Muld(),在设备上执行矩阵乘法的内核。


6.3.1 Mul()


Mul()接受下列输入:
 指向A和B的元素的主机内存的两个指针,
 A的高度和宽度,B的宽度,
 指向应该写入C的主机内存的指针。
Mul()执行下列操作:
 使用cudaMalloc()将足够的全局内存分配到库A、B和C中;
 使用cudaMemcpy()将A和B从主机内存复制到全局内存;
 调用Muld()在设备上计算C;
 使用cudaMemcpy()将C从全局内存复制到主机内存;
 使用cudaFree()释放为A、B和C分配的全局内存。


6.3.2 Muld()


除了指针指向设备内存而非主机内存之外,Muld()与Mul()具有相同的输入。
对于每个块,Muld()迭代处理所有需要计算Csub的A和B的子矩阵。在每次迭代中,此函数:
 将A的一个子集和B的一个子集从全局内存加载到共享内存中;
 同步以确保两个子矩阵都由块内的所有线程完全加载;
 计算两个子集的乘积并将其加到上一次迭代期间获得的乘积中;


 再次同步以确保在开始下一次迭代之前两个子集的乘积已经完成。
按照5.1.2.1和5.1.2.4所述,编写Muld()是为了最大化内存性能。
的确,假设wA和wB是16的倍数(如5.1.2.1所建议的),则确保了全局内存合并,因为a、b和c都是BLOCK_SIZE的倍数,BLOCK_SIZE等于6。
对于每个半warp,也没有任何共享内存库冲突,所有线程的ty和k都是相同的,tx在0到15之间变化,因此对于内存访问As[ty][tx]、Bs[ty][tx]和Bs[k][tx],每个线程都访问一个不同的库,对于内存访问As[ty][k],每个线程都访问同一个库。

您看到此篇文章时的感受是:
支持
愤怒
无聊
暴汗
养眼
炒作
不解
标题党
搞笑
用户评论
用户名
评论内容
发表时间
- 发表评论 -
匿名
注册用户

百度大联盟认证黄金会员Copyright© 1997- CNET Networks 版权所有。 ZDNet 是CNET Networks公司注册服务商标。
中华人民共和国电信与信息服务业务经营许可证编号:京ICP证010391号 京ICP备09041801号-159
京公网安备:1101082134