Events 主机结束CPU设备前的未完成任务

Events 主机结束CPU设备前的未完成任务,events,opencl,cpu,blocking,Events,Opencl,Cpu,Blocking,我只有一个CPU核心i3和两个核心,所以我只能使用CPU,而不是GPU。我想用一个简单的add内核测试一个使用OpenCL的简单示例。但我的问题是: 在分配平台、CPU设备等后,我执行以下操作: 1) clEnqueueNDRange()将内核任务排队,并使用最后一个参数为完成此任务分配一个事件 2) clSetEventCallback()使用CL_COMPLETE将回调函数链接到上述事件 通常,在任务完成时应调用回调函数。但事实并非如此。的确,任务在结束时不完整的事件,如果主人在结束前做了很

我只有一个CPU核心i3和两个核心,所以我只能使用CPU,而不是GPU。我想用一个简单的add内核测试一个使用OpenCL的简单示例。但我的问题是:

在分配平台、CPU设备等后,我执行以下操作:

1) clEnqueueNDRange()将内核任务排队,并使用最后一个参数为完成此任务分配一个事件

2) clSetEventCallback()使用CL_COMPLETE将回调函数链接到上述事件

通常,在任务完成时应调用回调函数。但事实并非如此。的确,任务在结束时不完整的事件,如果主人在结束前做了很多事情。有人能告诉我为什么吗

以下是我的最低代码:

/** Simple add kernel */
private static String programSource0 =
    "__kernel void vectorAdd(" +
    "     __global const float *a,"+
    "     __global const float *b, " +
    "     __global float *c)"+
    "{"+
    "    int gid = get_global_id(0);"+
    "    c[gid] = a[gid]+b[gid];"+
    "}";

/** The entry point of this sample */
public static void main(String args[])
{
    /** Callback function */
    EventCallbackFunction kernelCommandEvent = new EventCallbackFunction()
    {
        @Override
        public void function(cl_event event, int event_status, Object user_data)
        {
            System.out.println("Callback: task COMPLETED");
        }
    };

    // Initialize the input data
    int n = 1000000;
    float srcArrayA[] = new float[n];
    float srcArrayB[] = new float[n];
    float dstArray0[] = new float[n];
    Array.fill(srcArrayA, 1,0f);
    Array.fill(srcArrayB, 1,0f);

    // .
    // (hidden) Allocation of my Intel platform, CPU device, kernel, commandQueue, and memory buffer, set the argument to kernel etc...
    // .


    // Set work-item dimensions and execute the kernels
    long globalWorkSize[] = new long[]{n};

    // I pass an event on completion of the command queue.        
    cl_event[] myEventID = new cl_event[1];
    myEventID[0] = new cl_event();
    clEnqueueNDRangeKernel(commandQueue, kernel0, 1, null, globalWorkSize, null, 0, null, myEventID[0]);

    // I link the event to the callback function "kernelCommandEvent", and pass 10 as parameter
    clSetEventCallback(myEventID[0], CL_COMPLETE, kernelCommandEvent, new Integer(10));


    // host does some very long stuff !!

    // Normally, my device task should be completed
    int[] ok = new int[1];
    Arrays.fill(ok, 0);
    clGetEventInfo(myEventID[0], CL_EVENT_COMMAND_EXECUTION_STATUS, Sizeof.cl_int, Pointer.to(ok), null);
    if (ok[0] == CL_COMPLETE) System.out.println("Task COMPLETE");else System.out.println("Task INCOMPLETE");
}

排队不强制执行任务。它只是把它放在队列中

只有在以下情况下,才会执行任务:

  • 使用
    clFlush()
    强制立即执行
  • 进行直接或间接依赖于该任务的阻塞调用
一些驱动程序还可以决定,即使您没有刷新任务,他们也将开始处理该任务。但这取决于实现。如果要确保使用
clFlush(commandQueue)

额外:
这种行为是这样的,因为将数据排队到设备的开销可能很大,如果在一个循环中多次调用,那么每次排队调用都可能没有效率。相反,它被推迟到刷新或阻塞调用,因此可以批处理。

好的,谢谢您的回复,它工作得非常好。顺便说一句,我在另一台计算机上注意到,NVIDIA GPU设备不需要这个clFlush()调用来执行,这是因为您所说的依赖于实现的驱动程序。所以我认为无论我们使用哪种设备,总是调用clFlush()函数是一个好主意,以确保任务正在执行,对吗?无论如何,非常感谢您,例如在仅CPU的情况下。在阻塞调用时进行所有处理可以节省一些时间,因为这样可以最大限度地减少应用程序的上下文切换量。在GPU设备中,尽可能快地开始工作是很有用的。因此,在多个命令队列属于多个不同上下文的情况下,执行对clEnqueueNDRange()的所有调用,然后在我理解正确的情况下只调用一次clFlush()?我的建议是:应该执行clFlush()第一次排队后(以便系统尽可能快地开始工作)。然后添加所有其他排队,并执行阻塞读取/等待。后面的那些将执行另一个隐式刷新。因此,您可以充分利用两个世界,尽可能快速启动和批处理。