与barrier()
(我认为我理解)不同,mem_fence()
不会影响工作组中的所有项目。 OpenCL规范说明(第6.11.10节),mem_fence()
:
对执行内核的工作项的加载和存储进行排序。
(因此它适用于单个工作项)。
但是,与此同时,在3.3.1节中,它说:
在工作项内存中具有加载/存储一致性。
所以在工作项中内存是一致的。
那么mem_fence()
对什么有用呢?它不适用于项目,但在项目中不需要......
请注意,我没有使用过原子操作(第9.5节等)。 mem_fence()
与这些一起使用的想法是什么?如果是这样,我很乐意看到一个例子。
感谢。
更新:我可以看到与 barrier()
一起使用时有用(隐式地,因为屏障调用mem_fence()
) - 但肯定会必须有更多,因为它是分开存在的?
答案 0 :(得分:6)
试图更明确地(希望如此),
mem_fence()
等待,直到mem_fence()之前的调用工作项对本地和/或全局内存的所有读/写对工作组中的所有线程都可见。
来自:http://developer.download.nvidia.com/presentations/2009/SIGGRAPH/asia/3_OpenCL_Programming.pdf
可以重新排序内存操作以适应它们运行的设备。该规范(基本上)指出任何内存操作的重新排序必须确保内存在单个工作项中处于一致状态。但是,如果您(例如)执行商店操作并且值决定现在直播工作项特定缓存,直到更好的时间呈现给本地/全局内存?如果您尝试从该内存加载,则写入该值的工作项将其包含在其缓存中,因此没有问题。但是工作组中的其他工作项没有,因此他们可能会读错了值。放置内存栅栏可确保在内存栅栏调用时,本地/全局内存(根据参数)将保持一致(任何缓存都将被刷新,任何重新排序都会考虑到您希望其他线程可能在此之后需要访问此数据。)
我承认它仍然令人困惑,我不会发誓我的理解是100%正确的,但我认为这至少是一般的想法。
跟进:
我发现这个链接讨论了CUDA内存防护,但同样的一般想法适用于OpenCL:
查看 B.5记忆围栏功能部分。
他们有一个代码示例,用于计算一次调用中数字数组的总和。设置代码以计算每个工作组中的部分和。然后,如果要做更多的求和,代码会让最后一个工作组完成工作。
因此,每个工作组基本上完成了两件事:一个部分和,它更新一个全局变量,然后是一个计数器全局变量的原子增量。
之后,如果还有其他工作要做,将计数器增加到(“工作组大小” - 1)值的工作组将被视为最后一个工作组。那个工作组继续完成。
现在,问题(正如他们解释的那样)是,由于内存重新排序和/或缓存,计数器可能会增加,最后一个工作组可能会在该部分总和全局变量之前开始工作已将其最新价值写入全球记忆。
内存围栏将确保在移过篱笆之前,该部分和变量的值对于所有线程都是一致的。
我希望这有一定道理。这很令人困惑。
答案 1 :(得分:0)
栅栏确保在栅栏完成之前在栅栏之后发出的任何载荷和/或商店之前发出的载荷和/或商店。单独的围栏没有暗示sinc。屏障操作支持一个或两个存储空间中的读/写栅栏以及阻塞,直到给予者工作组中的所有工作项到达它为止。
答案 2 :(得分:0)
这就是我理解的方式(我还在尝试验证它)
JOIN
只会确保内存对于组中的所有线程都是一致且可见的,即执行不会停止,直到有另一个内存事务(本地或全局)。这意味着如果在SELECT
a.id,
CONCAT(a.firstname, ' ', a.lastname) AS name,
b.cnt
FROM table a
JOIN (
SELECT
CONCAT(firstname, ' ', lastname) AS bname,
COUNT(*) AS cnt
FROM table b
GROUP BY firstname, lastname
) b
ON CONCAT(a.firstname, ' ', a.lastname) = b.bname
之后存在移动指令或添加指令,则设备将继续执行这些“非存储器事务”指令。
memory_fence
将停止执行,期间。并且只有在所有线程到达该点并且所有内存事务都已清除后才会继续。
换句话说,memory_fence
是barrier
的超集。 barrier
在性能方面可能比mem_fence
更昂贵。