在OpenCL中,与barrier()相比,mem_fence()有什么作用?

16
与我理解的barrier()不同,mem_fence()不会影响工作组中的所有项。OpenCL规范在第6.11.10节中表示,对于mem_fence()

指令执行内存读写。

(因此它适用于一个单独的工作项)。

但是,在3.3.1节中同时表示:

在工作项内存中具有加载/存储一致性。

因此,在工作项内存中是一致的。

那么mem_fence()有什么用处呢?它不能跨项工作,但在项内部也不需要…

请注意,我没有使用原子操作(第9.5节等)。mem_fence()的想法是与这些操作结合使用吗?如果是这样,我很想看到一个例子。

谢谢。

供参考的规范。

更新:我可以看出当与barrier()一起使用时它是有用的(因为隐式地调用了mem_fence())- 但肯定还有更多用途,因为它存在独立使用的情况下?


3个回答

6

为了更清晰地表达(希望如此),

mem_fence() 等待调用工作项之前对本地和/或全局内存进行的所有读/写操作在 mem_fence() 之前对工作组中的所有线程可见。

这来自于:http://developer.download.nvidia.com/presentations/2009/SIGGRAPH/asia/3_OpenCL_Programming.pdf

内存操作可以根据设备进行重新排序。规范(基本上)规定任何内存操作的重新排序必须确保在单个工作项中内存处于一致状态。但是,例如您执行存储操作并且值决定现在保存在工作项特定高速缓存中,直到更好的时间提供了写入本地/全局内存的机会怎么办?如果您尝试从该内存加载,写入该值的工作项将其保存在其高速缓存中,因此没有问题。但是,工作组中的其他工作项却没有,因此它们可能读取错误的值。放置内存屏障可以确保,在内存屏障调用时,本地/全局内存(根据参数)将变得一致(任何高速缓存都将被刷新,并且任何重新排序都将考虑到您期望其他线程可能需要在此之后访问此数据)。
我承认这仍然令人困惑,而且我不保证我的理解是100%正确的,但我认为至少是一般的想法。
跟进:
我找到了这个链接,讨论CUDA内存屏障,但同样的一般想法也适用于OpenCL:

http://developer.download.nvidia.com/compute/cuda/2_3/toolkit/docs/NVIDIA_CUDA_Programming_Guide_2.3.pdf

请查看第B.5节内存栅函数
其中有一个代码示例,可以在一次调用中计算数组的总和。该代码设置为在每个工作组中计算部分总和。然后,如果还有更多求和要做,代码将让最后一个工作组完成这项工作。
因此,每个工作组基本上都会完成两件事情:更新全局变量的部分总和,然后对计数器全局变量进行原子递增。
之后,如果还有剩余的工作要做,递增计数器到("工作组大小" - 1)的工作组将被视为最后一个工作组。该工作组继续完成工作。
现在,问题(正如他们所解释的那样)是由于内存重新排序和/或缓存,计数器可能在部分总和全局变量的最新值被写入全局内存之前递增,而最后一个工作组可能开始执行其工作。
内存栅将确保在通过栅之前,该部分总和变量的值对于所有线程都是一致的。
希望这有些道理。这很令人困惑。

但在你所举的例子中,另一个线程无法确定数据是否在没有屏障的情况下存储。因此,你仍然需要屏障。我想我没有在原始问题中提到这一点 - 但我不明白mem_fence单独使用时如何有意义(而不与屏障一起使用)。如果我漏掉了什么,请谅解并感谢评论... - andrew cooke
我在上面添加了更多细节。希望这可以帮助到你。 - Jonathan DeCarlo
好的,所以CUDA示例包括原子操作,这正是我怀疑mem_fence必须要有用的东西。将来参考,OpenCL和CUDA函数之间的等价性在此处描述 - http://developer.amd.com/documentation/articles/pages/OpenCL-and-the-AMD-APP-SDK.aspx#four - andrew cooke
虽然您可能正在描述OpenCL men_fence()的意图或从类似的CUDA函数中推断,但我认为原帖的问题仍未得到回答。即使在1.2版本的OpenCL规范中,men_fence()也没有任何说明表明它可以在工作组中提供线程一致性。我仍然有同样的问题,为什么要使用men_fence()呢? - NoahR
这个AMD论坛中的帖子提供了一个解释和示例,与这个答案相矛盾。http://devgurus.amd.com/message/1242922#1242922 - NoahR

1
这是我的理解(我仍在尝试验证): memory_fence 仅确保内存一致性并对组中的所有线程可见,即执行不会停止,直到有另一个内存事务(本地或全局)。这意味着,如果在 memory_fence 之后有移动指令或加法指令,则设备将继续执行这些“非内存事务”指令。
另一方面,barrier 将停止执行。只有在所有线程到达该点且所有内存事务已被清除后,才会继续执行。
换句话说,barriermem_fence 的超集。从性能角度来看,barrier 可能更昂贵。

0
栅栏确保在栅栏之前发出的装载和/或存储会在栅栏之后发出的任何装载和/或存储之前完成。栅栏本身不涉及同步。屏障操作支持一个或两个内存空间中的读/写栅栏,并阻止直到给定工作组中的所有工作项到达为止。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接