所以我想说我有一个全局的内存数组:
|a|b|c| |e|f|g| |i|j|k| |
有四个“线程”(OpenCL中的本地工作项)访问此内存,此访问有两种可能的模式(列是时间片,行是线程):
0 -> 1 -> 2 -> 3
t1 a -> b -> c -> .
t2 e -> f -> g -> .
t3 i -> j -> k -> .
t4 . . . `> .
上述模式将数组拆分为块,每个线程迭代到每个时间片并访问块中的下一个元素。我相信这种访问适用于CPU,因为它最大化了每个线程的缓存局部性。此外,编译器可以轻松地展开利用此模式的循环。
第二种模式:
0 -> 1 -> 2 -> 3
t1 a -> e -> i -> .
t2 b -> f -> j -> .
t3 c -> g -> k -> .
t4 . . . `> .
上述模式以步幅访问内存:例如,线程1访问a,然后是e,然后是i等。这样可以最大化每单位时间的缓存局部性。考虑到你在任何给定的时间片上都有64个工作项“跨越”。这意味着,如果缓存行大小为64字节且元素为sizeof(float),则工作项1的读取将缓存工作项1的读取。必须仔细选择每个单元的数据宽度/数量(其中'a'是上面的单元)以避免错位访问。这些循环似乎不会轻易展开(或者根本不使用英特尔的内核生成器与CPU)。我相信这种模式适用于GPU。
我的目标是具有缓存层次结构的GPU。特别是AMD的最新架构(GCN)。第二种访问模式是“合并”的一个例子吗?在某个地方我的思考过程中我错了吗?
答案 0 :(得分:1)
我认为答案取决于访问是针对全局内存还是本地内存。如果要从全局内存中提取数据,则需要担心合并读取(即连续块,第二个示例)。但是,如果要从本地内存中提取数据,则需要担心银行冲突。我有一些但不是很多经验,所以我不是说这是绝对真理。
编辑:在阅读GCN之后,我不认为缓存在这里有所作为。如果你反复读/写相同的元素,你基本上可以把它们想象成加速全局内存。在旁注中,感谢您提出问题,因为阅读新架构非常有趣。
编辑2:这是针对本地和全球记忆的银行的一个很好的Stack Overflow讨论:Why aren't there bank conflicts in global memory for Cuda/OpenCL?