CUDA实践指南(十八)

简介:

矩阵乘法中的共享内存($C = AA^T$)
先前矩阵乘法的一个变体可以用来说明如何处理对全局存储器的分步访问以及共享存储器组冲突。 这个变体只是使用A的转置来代替B,所以$C = AA^T$。
$C = AA^T$的简单实现在Unoptimized处理对全局内存的跨步访问中显示
未经优化的对全局存储器的分步访问的处理:

__global__ void simpleMultiply(float *a, float *c, int M)
{
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    float sum = 0.0f;
    for (int i = 0; i < TILE_DIM; i++) {
        sum += a[row*TILE_DIM + i] * a[col*TILE_DIM + i];
    }
    c[row*M + col] = sum;
}

在未优化的对全局存储器的逐行访问的处理中,C的第row,th col元素是通过取A的第th行和第col行的点积获得的。该内核的有效带宽为3.64 GB / s在NVIDIA Tesla M2090上。 这些结果远远低于C = AB内核的相应测量结果。 不同之处在于,对于每个迭代i,在第二项中A的线程如何访问A中的元素[col TILE_DIM + i]。 对于线程变形,col表示A的转置的顺序列,因此col TILE_DIM表示跨度为w的跨步存取,导致大量浪费的带宽。
避免跨越访问的方式是像以前一样使用共享内存,除非在这种情况下,warp会将一行A读入共享内存块的列中,如使用来自全局内存的合并读取的优化处理跨步访问。
使用来自全局内存的合并读取优化处理跨度访问:

__global__ void coalescedMultiply(float *a, float *c, int M)
{
    __shared__ float aTile[TILE_DIM][TILE_DIM],
        transposedTile[TILE_DIM][TILE_DIM];
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    float sum = 0.0f;
    aTile[threadIdx.y][threadIdx.x] = a[row*TILE_DIM + threadIdx.x];
    transposedTile[threadIdx.x][threadIdx.y] =
        a[(blockIdx.x*blockDim.x + threadIdx.y)*TILE_DIM +
        threadIdx.x];
    __syncthreads();
    for (int i = 0; i < TILE_DIM; i++) {
        sum += aTile[threadIdx.y][i] * transposedTile[i][threadIdx.x];
    }
    c[row*M + col] = sum;
}

使用来自全局内存的合并读取对跨步访问进行优化处理时,使用共享transposeTile来避免点积中第二项中的未合并访问以及前一示例中共享的aTile技术,以避免第一项中的未合并访问。在NVIDIA Tesla M2090上,该内核的有效带宽为27.5 GB / s。这些结果略低于最终内核对于C = AB所获得的结果。造成差异的原因是共享内存组冲突。
for循环中transposedTile中元素的读取没有冲突,因为每个半变形的线程都会读取瓦片的行,从而导致跨单元的单元跨度。但是,从全局内存复制磁贴到共享内存时发生银行冲突。为了使来自全局存储器的加载能够合并,数据从全局存储器中顺序读取。然而,这需要以列的形式写入共享内存,并且由于在共享内存中使用了wxw磁贴(tile),这导致了w个银行的线程之间的跨度 - 每个线程都经历了同一个银行。 (回想一下,对于计算能力为2.0或更高的设备,w选为32)。这些多路银行冲突非常昂贵。简单的补救措施是填充共享内存数组,使其具有额外的列,如下面的代码行所示。

__shared__ float transposedTile[TILE_DIM][TILE_DIM+1]

这种填充消除了冲突,因为现在线程之间的跨度是w + 1个存储体(即对于当前设备是33个),由于用于计算存储体索引的模算术,这相当于单位步长。 此次更改之后,NVIDIA Tesla M2090上的有效带宽为39.2 GB / s,与上一次C = AB内核的结果相当。
表3总结了这些优化的结果。
1

应该将这些结果与表2中的结果进行比较。从这些表中可以看出,合理使用共享内存可以显着提高性能。
本节中的示例说明了使用共享内存的三个原因:

  • 为了能够对全局内存进行合并访问,特别是为了避免大步幅(对于一般矩阵,步幅远远大于32)
  • 消除(或减少)来自全局内存的冗余负载
  • 避免浪费带宽
目录
相关文章
|
Rust 算法 Go
【密码学】一文读懂MurMurHash3
本文应该是MurMurHash算法介绍的最后一篇,来一起看一下最新的MurMurHash算法的具体过程,对于最新的算法来说,整个流程和之前的其实也比较相似,这里从维基百科当中找到了伪代码,也就不贴出来Google官方给出的推荐代码了,先来看一下维基百科给出的伪代码,这里只有32位的伪代码。
3621 0
【密码学】一文读懂MurMurHash3
|
Java API Maven
pom文件的<scope>标签详细含义是什么
【4月更文挑战第13天】pom文件的<scope>标签详细含义是什么
1802 3
|
机器学习/深度学习 存储 自然语言处理
深入理解LSTM:案例和代码详解
深入理解LSTM:案例和代码详解
1402 2
|
人工智能 小程序 数据安全/隐私保护
十分钟带你彻底告别翻来覆去找ChatGPT提示词模版
十分钟带你彻底告别翻来覆去找ChatGPT提示词模版
|
存储 安全 Java
S-FTPClient(Swing,Java,Socket,FTP,加密算法)
S-FTPClient(Swing,Java,Socket,FTP,加密算法)
242 0
|
API Android开发 容器
GTK+重拾--03
版权声明:您好,转载请留下本人博客的地址,谢谢 https://blog.csdn.net/hongbochen1223/article/details/50058335 (一):写在前面 在这一个小节当中,主要是通过一个窗口实例来实现对GTK+的整体理解。
921 0
|
4天前
|
云安全 人工智能 运维
当攻击全面Agent化,你的安全防御能否跟上AI Speed?
多起客户侧AK泄露事件应急的真实观察
1222 3
|
27天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9671 32