smu*_*kes 4 memory cuda nvidia opencl
const char programSource[] =
"__kernel void vecAdd(__global int *a, __global int *b, __global int *c)"
"{"
" int gid = get_global_id(0);"
"for(int i=0; i<10; i++){"
" a[gid] = b[gid] + c[gid];}"
"}";
Run Code Online (Sandbox Code Playgroud)
上面的内核是每个循环执行十次的向量加法.我已经使用编程指南和堆栈溢出来弄清楚全局内存是如何工作的,但是如果我以一种好的方式访问全局内存,我仍然无法通过查看我的代码来弄清楚.我以连续的方式访问它,我正在以一种统一的方式猜测.该卡是否为阵列a,b和c加载128kb的全局内存块?然后是否为每个处理的32个gid索引加载一次128kb的每个数组块?(4*32 = 128)好像那时我没有浪费任何全局内存带宽吗?
顺便说一句,计算分析器显示gld和gst效率为1.00003,这看起来很奇怪,我认为如果所有的商店和负载都合并,那只会是1.0.它是如何高于1.0的?
Gri*_*zly 12
是的,您的内存访问模式非常优秀.每个半衰期都访问16个连续的32位字.此外,访问是64字节对齐的,因为缓冲区本身是对齐的,并且每个半整数的startindex是16的倍数.因此每个halfwarp将生成一个64Byte事务.所以你不应该通过非合并访问浪费内存带宽.
由于您在上一个问题中要求提供示例,请将此代码修改为其他(不太理想的访问模式)(因为循环实际上没有做任何事情我会忽略它):
kernel void vecAdd(global int* a, global int* b, global int* c)
{
int gid = get_global_id(0);
a[gid+1] = b[gid * 2] + c[gid * 32];
}
Run Code Online (Sandbox Code Playgroud)
首先介绍它如何在计算1.3(GT200)硬件上运行
对于写入,这将生成一个稍微不理想的模式(遵循由其id范围和相应的访问模式标识的半衰期):
gid | addr. offset | accesses | reasoning
0- 15 | 4- 67 | 1x128B | in aligned 128byte block
16- 31 | 68-131 | 1x64B, 1x32B | crosses 128B boundary, so no 128B access
32- 47 | 132-195 | 1x128B | in aligned 128byte block
48- 63 | 196-256 | 1x64B, 1x32B | crosses 128B boundary, so no 128B access
Run Code Online (Sandbox Code Playgroud)
所以基本上我们浪费了大约一半的带宽(奇数半圈的访问宽度减少了一倍并没有多大帮助,因为它会产生更多的访问,这不会比浪费更多的字节更快).
对于来自b的读取,线程只访问数组的偶数元素,因此对于每个半变形,所有访问都位于128字节对齐的块中(第一个元素位于128B边界,因为对于该元素,gid是16的倍数=>对于4字节元素,索引是32的倍数,这意味着地址偏移量是128B的倍数.访问模式在整个128B块上延伸,因此每次半衰期都会进行128B传输,再次将带宽减半.
从c读取生成最糟糕的情况之一,其中每个线程在其自己的128B块中进行索引,因此每个线程都需要自己的传输,这一方面是一个序列化方案的一点(尽管不像normaly那么糟糕,因为硬件应该能够重叠转移).更糟糕的是,这将为每个线程传输32B块,浪费7/8的带宽(我们访问4B /线程,32B/4B = 8,因此仅使用1/8带宽).由于这是天真矩阵转换的访问模式,因此建议使用本地内存进行访问(从经验来讲).
计算1.0(G80)
这里唯一可以创建良好访问的模式是原始模式,示例中的所有模式都将创建完全未合并的访问,浪费7/8的带宽(32B传输/线程,见上文).对于G80硬件,每半个变形中第n个线程不访问第n个元素的访问都会创建这样的非合并访问
计算2.0(费米)
在这里,每次访问内存都会创建128B事务(尽可能多地收集所有数据,因此在最坏的情况下为16x128B),但是这些事务被缓存,使数据传输的位置变得不那么明显.暂时假设缓存足够大以容纳所有数据并且没有冲突,因此每个128B缓存线最多只能传输一次.让我们假设一半的序列化执行,所以我们有一个确定的缓存占用.
对b的访问仍将始终传输128B块(相应的内存区域中没有其他线程索引).访问c将为每个线程生成128B传输(最差的访问模式).
对于访问它是以下(暂时将它们视为读取):
gid | offset | accesses | reasoning
0- 15 | 4- 67 | 1x128B | bringing 128B block to cache
16- 31 | 68-131 | 1x128B | offsets 68-127 already in cache, bring 128B for 128-131 to cache
32- 47 | 132-195 | - | block already in cache from last halfwarp
48- 63 | 196-259 | 1x128B | offsets 196-255 already in cache, bringing in 256-383
Run Code Online (Sandbox Code Playgroud)
因此,对于大型阵列,访问a将在理论上几乎不浪费带宽.对于这个例子,现实当然不是那么好,因为对c的访问会很好地废弃缓存
对于剖析器,我认为超过1.0的效率只是浮点不精确的结果.
希望有所帮助
| 归档时间: |
|
| 查看次数: |
592 次 |
| 最近记录: |