2016-03-03 3 views
1

У меня есть только процессор Core i3 с двумя ядрами, поэтому я могу работать только с процессором, а не с графическим процессором. Я хочу протестировать простой пример с помощью OpenCL с простым ядром add. Но вот моя проблема:Неполная задача до того, как HOST заканчивается процессором

После выделения платформы, устройство процессора и т.д., я следующее:

1) clEnqueueNDRange() ставит в очереди задачи ядра и назначает событие для выполнения этой задачи с помощью последнего параметр.

2) clSetEventCallback() с использованием CL_COMPLETE связывает функцию обратного вызова с вышеупомянутым событием.

Как правило, функция callback должна вызываться, когда задача завершается. Но это не так. В самом деле, задача INCOMPLETE в конце события, если хост как много материала, чтобы сделать до окончания. Может ли кто-нибудь сказать мне, почему?

Вот мой минимальный код:

/** 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"); 
} 

ответ

1

Enqueue не обеспечивает выполнение этой задачи. Он просто помещает его в очередь.

Задачи выполняются только, если вы:

  • Принудительно немедленного исполнения с помощью clFlush().
  • Сделать блокирующий вызов, который прямо или косвенно зависит от этой задачи.

Некоторые драйверы также могут решить, что они приступят к выполнению задания, даже если вы не сбросили его. Но это зависит от реализации. Если вы хотите быть уверены, используйте clFlush(commandQueue);

Extra: Такое поведение так, потому что накладные расходы очередями данных на устройство может быть большим, и делать это каждый вызов Enqueue не может быть эффективной, если она называется множественным раз в петле. Вместо этого он откладывается на флеш или блокирующий вызов, поэтому его можно отбирать.

+0

Хорошо, спасибо за ответ, он отлично работал. Кстати, я заметил на другом компьютере, что для устройства NVIDIA GPU этот вызов clFlush() не нужен, поэтому это связано с тем, что вы сказали о драйверах, зависящих от реализации. Поэтому я думаю, что какое бы устройство мы ни использовали, хорошо подумать, что всегда вызывайте функцию clFlush(), чтобы убедиться, что задача выполняется, правильно? Большое спасибо в любом случае DarkZeros – Algernon2

+0

Зависит, например, в случае только процессора. Выполнение всей обработки при блокирующем вызове экономит некоторое время, поскольку оно минимизирует количество контекстных переключателей вашего приложения. Хотя в устройствах GPU полезно начать работать как можно быстрее. – DarkZeros

+0

Итак, в случае, когда несколько командных очередей принадлежат нескольким различным контекстам, удобнее выполнять все вызовы clEnqueueNDRange(), а затем вызывать только один раз clFlush(), если я правильно понял? – Algernon2