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];}"
"}";
上面的内核是每个循环执行十次的向量加法。我已经使用编程指南和堆栈溢出来弄清楚全局内存是如何工作的,但是如果我以一种好的方式访问全局内存,我仍然无法通过查看我的代码来弄清楚。我以连续的方式访问它,我正在以一种统一的方式猜测。该卡是否为阵列a,b和c加载128kb的全局内存块?然后是否为每个处理的32个gid索引加载一次128kb的每个数组块? (4 * 32 = 128)好像那时我没有浪费任何全局内存带宽吗?
BTW,计算分析器显示gld和gst效率为1.00003,这看起来很奇怪,我认为如果我的所有商店和负载都合并,那只会是1.0。它是如何高于1.0的?答案 0 :(得分: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];
}
首先介绍计算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
所以基本上我们浪费了大约一半的带宽(奇数半圈的访问宽度减少了一倍,因为它会产生更多的访问,这不会比浪费更多的字节更快)。
对于来自b的读取,线程只访问数组的偶数元素,因此对于每个半变形,所有访问都位于128字节对齐的块中(第一个元素位于128B边界,因为对于该元素,gid是16 =&gt;索引是32的倍数,对于4字节元素,这意味着地址偏移是128B的倍数。 accesspattern在整个128B块上延伸,因此每次半衰期都会进行128B传输,再次将带宽减半。
从c读取生成最糟糕的情况之一,其中每个线程都在其自己的128B块中进行索引,因此每个线程都需要自己的传输,这一方面是一个序列化方案的一点(尽管不是很糟糕)正常,因为硬件应该能够重叠转移)。更糟糕的是,这将为每个线程传输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
因此,对于大型阵列,对a的访问在理论上几乎不会浪费带宽。 对于这个例子,现实当然不是那么好,因为访问c会很好地废弃缓存
对于剖析器,我认为效率超过1.0只是浮点不准确的结果。
希望有所帮助