带有clSetKernelArg的OpenCL竞争条件



来自Khronos网站关于clSetKernelArg:的线程安全

除了clSetKernelArg之外,所有OpenCL API调用都是线程安全的,它可以安全地从任何主机线程调用,并且只要并发调用操作在不同的cl_kernel对象上,就可以安全地重新集中调用。但是,如果同时从同一cl_kernel对象上的多个主机线程调用clSetKernelArg,则cl_kerne对象的行为是未定义的。

我的问题是,有没有一种方法可以定义这种行为,即内核可以从多个线程中从单个内核对象读取和写入?

我认为内核修改的对象上的std::atomic会阻止这种未定义的行为,但根据我的尝试,它会导致内核的输出产生错误的值。有没有更好的方法来实现这一点/一种已知的处理案例的技术?

如果分配的对象的大小太大,以至于每次内核执行都要重新创建一个新对象,这可能会占用太多内存,并且最好使用共享/可重写的对象。

哪里的内核可以从多个线程读取和写入单个内核对象?

By;内核";你指的是在GPU上执行的代码片段;单内核对象";你的意思是主机代码中的clkernel?GPU上的内核永远看不到主机端存在的cl_kernel结构。我假设您谈论的是内核使用缓冲区对象(cl_mem(参数。

您可以将cl_kernel看作:

struct {
size_t num_args;
void* args[];
} _cl_kernel;
typedef struct _cl_kernel * cl_kernel;

如果调用clSetKernelArg((,它只是在该结构中设置一些内容。如果调用clEnqueueNDRangeKernel((,它会获取(参数的(cl_kernel结构的快照,并将其附加到某个内部设备队列中。通过";快照";我的意思不是说它创建了一个实际cl_mem缓冲区内容的隐藏快照;它只是复制对cl_mem参数的引用。由于它是一个引用,所以无论您是使用多个线程中的单个cl_kernel对象,还是使用相同的名称多次调用clCreateKernel,然后在每个线程中使用这些cl_kernel,都无关紧要;这只是一个方便的问题,最终结果是一样的。

如果您有一个有序的命令队列,那么您的内核将按队列顺序执行。如果您有多个命令队列(有序或无序,无关紧要(,则队列之间没有任何隐式排序,因此,如果您将同一内核排入所有队列,它们将以随机顺序执行。您可以使用事件强制执行显式顺序。IOW,你做:

cl_event event1, event2;
cl_kernel K;
...
clEnqueueNDRangeKernel(queue_1, K, ... , &event1);
clEnqueueNDRangeKernel(queue_2, K, ... , 1, &event1, &event2);

等等。这将强制内核执行在前一个上等待,即使它们在不同的队列中也是如此。但是一次只能有一个内核使用缓冲区。

如果您希望多个运行的内核同时使用相同的缓冲区,则这取决于该缓冲区的使用模式。如果只进行读取,则可以安全地同时使用任意数量内核的缓冲区。对于写入使用,如果您知道只写入缓冲区的一部分,则可以尝试使用子缓冲区(clCreateSubBuffer(。否则,你可能运气不佳(也许你可以尝试原子操作,但这可能会使算法变得非常慢(。

最新更新