有序队列的内存可见性

时间:2017-03-01 11:16:28

标签: multithreading parallel-processing opencl

在阅读OpenCL 1.1标准后,我仍然无法掌握有序命令队列是否确保任何一对命令的内存可见性不仅仅是内核根据他们的排队顺序。

OpenCL标准1.1部分 5.11 状态:

  

如果是CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE属性   命令队列未设置,命令排入命令队列   按顺序执行。例如,如果应用程序调用clEnqueueNDRangeKernel来执行内核A后跟a   clEnqueueNDRangeKernel执行内核B,应用程序即可   假设内核A先完成,然后执行内核B 。如果   然后,内核A输出的内存对象是内核B的输入   内核B将在执行内核A时看到内存对象中的正确数据

使用该缓冲区内容后clEnqueueWriteBuffer(非阻塞)和clEnqueueNDRangeKernel排队怎么样?

AFAIK,'完成执行'并不意味着相应的写入是可见的(由于宽松的一致性)。例如, 5.10 部分具体说明:

  

clEnqueueBarrier命令确保所有排队的命令   command_queue已在下一批之前完成执行   命令可以开始执行。 clEnqueueBarrier命令是一个   同步点

换句话说,我应该依赖其他同步点和相关规则(事件等),还是为所有命令开箱即用的内存同步在有序队列中?

2 个答案:

答案 0 :(得分:3)

  

clEnqueueWriteBuffer(非阻塞)和   clEnqueueNDRangeKernel入队后,使用该缓冲区   内容?

因为它是有序队列,所以它会先写,然后在完成后运行内核,即使写是非阻塞的。

clEnqueueBarrier 是设备端同步命令,用于处理无序队列。当您使用clFinish()时,您可以使api等待主机和设备之间的通信。入队屏障同步速度要快得多,但仅限于设备端。当您需要将队列与另一个队列同步并且仍需要类似的同步点时,您应该在屏障之后(或之前)使用 clEnqueueWaitForEvents ,或者仅使用偶数等待(对于有序队列) )。

对于opencl 1.2, clEnqueueWaitForEvents clEnqueueBarrier 被合并到 clEnqueueBarrierWithWaitList 中,这样可以阻止无序队列并将其与其他队列甚至主持人提出的事件。

如果只有单个有序队列,则不需要屏障,当需要与主机同步时,可以使用clFinish或基于事件的同步命令。

  

或者我为所有命令提供了开箱即用的内存同步功能   有序队列?

仅用于 enqueue 类型命令,是的。在有序队列中排队(1次写入+ 1次计算+ 1次读取)操作128次,它们将一个接一个地工作并完成128步模拟(在它们由刷新/完成命令发出之后)。命令不必具有此隐式同步的特定顺序。像1次写入+ 2次读取+ 2次内核+5次写入+1读取+ 1次内核+15读取一个接一个地工作(2个内核= 1个内核+ 1个内核)。

对于非排队类型命令,例如clSetKernelArg,您必须使用同步点或在所有命令排队之前执行此操作。

您还可以使用排队命令本身作为其事件列表参数的队列间同步点,并使用下一个参数将其完成事件用于另一个队列(信令),但它仍然不是一个障碍。 -order队列。

如果缓冲区用于两个不同队列的内核并且要在该缓冲区上写入数据,则队列之间必须有同步,除非它们在不同的位置写入。因此,您可以使用20个内核在每个1/20的缓冲区上工作,并使用多个队列并行工作,最后使用等待列表最终同步所有队列。如果内核同时使用或更改另一个内核区域,则它是未定义的行为。也可以对map / unmap进行类似的处理。

按顺序与无序示例:

r: read, w: write, c: compute


                    <------------clFinish----------------------->
in-order queue....: rwrwrwcwccwccwrcwccccccwrcwcwrwcwrwccccwrwcrw

out-of-order queue: <--r--><-r-><-c-><-----c-----><-----r-----><w>
                     <---w-------><-------r-----><------c----->
                      <---r---><-----c--------------------><--c->
                       <---w---> 
                     <---c-----> <----w------>   

和另一个在中间有障碍的无序队列:

                    <---r---><--w---> | <-----w----> 
                    <---c------>      | <----c---> <----w--->
                       <---c--------> | <------r------->
                     <----w------>    | <----c------->

在屏障之前的读/写操作被强制等待,直到所有命令都达到相同的屏障。然后剩下的所有剩余的同时继续。

最后一个例子显示,来自&#34;主机端的内存可见性&#34;可以通过障碍或clfinish获得。但是屏障没有告知主机它已经完成,所以你需要查询有关队列的事件。 ClFinish阻止所有命令完成,因此您不需要查询任何内容。两者都会让主机看到最新的内存。

您的问题是关于有序队列命令的内存可见性,因此您不需要同步点来查看彼此最新的值。

每个内核执行也是其工作组之间的同步点,因此工作组无法了解其他组&#39;数据直到内核完成并且所有数据都准备好并在内核执行结束时变得可见。所以下一个内核可以立即使用它。

我没有尝试从设备读取数据并发而没有任何同步点,但可能适用于某些未缓存任何数据的设备任何缓存内存。即使是集成的gpus也有它们专用的L3缓存,所以它偶尔需要至少一个屏障命令,让主机读取一些更新的(但可能部分重新更新的飞行中)数据。基于事件的同步比clFinish更快,并为主机提供正确的内存数据。屏障也比clFinish快,但只能用于设备端同步点。

如果我理解正确,

 Sync Point   -------------------------    Memory visibility

 in-kernel fence                           in same workitem(and wavefront?)
 in-kernel local memory barrier            local memory in same workgroup
 in-kernel global memory barrier           global memory in same workgroup
 in-kernel atomics                         only other atomics in same kernel
 enqueued kernel/command                   next kernel/command in same queue
 enqueued barrier                          following commands in same device
 enqueued event wait                       host
 clFinish                                  host 

https://www.khronos.org/registry/OpenCL/sdk/1.1/docs/man/xhtml/clEnqueueMapBuffer.html

  

如果在设置CL_MEM_USE_HOST_PTR的情况下创建缓冲区对象   mem_flags,clCreateBuffer中指定的host_ptr为保证   包含当前映射区域中的最新位   clEnqueueMapBuffer命令已完成;和指针值   由clEnqueueMapBuffer返回的将从host_ptr派生   在创建缓冲区对象时指定。

https://www.khronos.org/registry/OpenCL/sdk/1.1/docs/man/xhtml/clEnqueueWriteBuffer.html

  

使用此缓冲区对象或内存对象的所有命令(缓冲区或   从此缓冲区对象创建的图像)之前有已完成执行   读取命令开始执行。

所以它没有说像屏障或同步这样的东西。完成就够了。

答案 1 :(得分:3)

来自规范:

  

按顺序执行:命令按照它们在命令队列中出现的顺序启动并按顺序完成。换句话说,先前   队列上的命令在以下命令开始之前完成。   这序列化了队列中命令的执行顺序。

如果按顺序排队队列中的所有命令按顺序执行,则不需要额外的同步。