Opencl “的目的是什么?”;clRetainKernel“;功能?

Opencl “的目的是什么?”;clRetainKernel“;功能?,opencl,Opencl,不可能使用同一内核对象并行执行同一内核的两个实例 要并行执行同一内核的多个实例,需要从同一个程序对象创建多个内核对象,并将其排入不同的命令队列 即使主机代码是并行的,保留相同内核对象的两个CPU线程也没有用。那么,“clRetainKernel”API的目的是什么 那么“clRetainKernel”API的目的是什么呢? 来源 第18页: 引用计数:OpenCL对象的寿命由 它的引用计数对的引用数的内部计数 物体。在OpenCL中创建对象时,其引用 count设置为1。对相应retainAPI

不可能使用同一内核对象并行执行同一内核的两个实例

要并行执行同一内核的多个实例,需要从同一个程序对象创建多个内核对象,并将其排入不同的命令队列

即使主机代码是并行的,保留相同内核对象的两个CPU线程也没有用。那么,“clRetainKernel”API的目的是什么

那么“clRetainKernel”API的目的是什么呢?

来源

第18页:

引用计数:OpenCL对象的寿命由 它的引用计数对的引用数的内部计数 物体。在OpenCL中创建对象时,其引用 count设置为1。对相应retainAPI的后续调用 (例如clRetainContext、clretainCommand和queue)增加 参考计数。调用适当的releaseAPI(例如 clReleaseContext、CLRELEASECOMANDQUEUE)递减引用 计数引用计数达到零后,对象的资源 由OpenCL解除分配

它增加其相关opencl对象的内部计数器,并可在某些RAII块之外使用。我没有用它,因为我已经足够了。但如果存在“共享”问题,这将有助于在it范围之外使用它。因此,如果每个人都在共享其范围之外的内容(特别是如果使用C api),那么他们应该自己保留和发布。在C++绑定中,可以看到构造函数

explicit Kernel(const cl_kernel& kernel, bool retainObject = false) :  ...
取得所有权,而不是增加引用计数器。(retain=false)。然后,在几行代码之后

(保留)

确保队列之间有事件同步,以便在执行时能够看到内核中具有不同偏移量的“最新位”数据

第二个队列与第一个队列的数据复制命令(evt参数)同步。复制数据后,它的事件向另一个队列(queue2)发送信号,以便它现在可以计算。但在第一个队列上,同步是隐式的,因此在数据副本之后立即将计算排队,无事件排队是可以的,因为此处使用的队列是有序队列。queue2完成计算后,发出readBuffer信号(通过evt2)

这是来自单个GPU示例,对于多个GPU,还需要复制数据

即使主机代码是并行的,两个CPU线程也没有用

如果同步是通过事件轮询自旋等待循环完成的,那么它将完全占用其线程。如果有多个命令队列具有相同的自旋等待循环,则需要这两个线程;您也可以在同一个循环中逐个轮询两个事件,但这需要您在动态更改命令队列数量的情况下处理事件。通过每线程轮询,更容易管理代码行的可伸缩性

要并行执行同一内核的多个实例,需要创建多个内核对象

如果内核要同时在多个GPU上使用,或者在同一个GPU上使用不同的缓冲区,那么就必须有不同的内核对象。因为设置内核参数不是排队操作。它在内核运行时返回,并且不应该在内核运行时返回,并且在内核运行完成后,如果不获取事件,则无法知道内核运行的确切时间。但是,您可以在内核执行之前添加一个闩锁,并在那里进行回调以及时设置参数。这一定很慢,所以拥有多个对象既快又简单

 2447         // We must retain things we obtain from the API to avoid releasing
 2448         // API-owned objects.
 2449         if (devices) {
 2450             devices->resize(ids.size());
 2451 
 2452             // Assign to param, constructing with retain behaviour
 2453             // to correctly capture each underlying CL object
 2454             for (size_type i = 0; i < ids.size(); i++) {
 2455                 (*devices)[i] = Device(ids[i], true); // true: retain
 2456             }
 2457         }
 6457             kernels->resize(value.size());
 6458 
 6459             // Assign to param, constructing with retain behaviour
 6460             // to correctly capture each underlying CL object
 6461             for (size_type i = 0; i < value.size(); i++) {
 6462                 // We do not need to retain because this kernel is being created 
 6463                 // by the runtime
 6464                 (*kernels)[i] = Kernel(value[i], false); // false: no retain
 6465             }
 6466         }
cl_event evt;
clEnqueueWriteBuffer(queue,buffer,CL_FALSE,0,100,myCharArray.data(),0,NULL,&evt);

size_t global_work_size = 50;
clEnqueueNDRangeKernel(queue,kernel,1,NULL,&global_work_size,NULL,0, NULL, NULL);

size_t global_work_size_2 = 50;
size_t global_offset_2 = 50;
cl_event evt2;  clEnqueueNDRangeKernel(queue2,kernel,1,&global_offset_2,&global_work_size_2,NULL,1, &evt, &evt2);
clEnqueueReadBuffer(queue,buffer,CL_FALSE,0,100,myCharArray.data(),1,&evt2,NULL);

clFlush(queue);
clFlush(queue2);
clFinish(queue2);
clFinish(queue);