Estoy escribiendo un programa OpenCL para usar con mi GPU AMD Radeon HD 7800 series. Según la guía de programación OpenCL de AMD , esta generación de GPU tiene dos colas de hardware que pueden funcionar de forma asíncrona.
5.5.6 Cola de comandos
Para las Islas del Sur y posteriores, los dispositivos admiten al menos dos colas de cómputo de hardware. Eso permite que una aplicación aumente el rendimiento de pequeños despachos con dos colas de comandos para el envío asíncrono y posiblemente la ejecución. Las colas de cálculo de hardware se seleccionan en el siguiente orden: primera cola = colas de comandos OCL pares, segunda cola = colas OCL impares.
Para hacer esto, he creado dos colas de comandos OpenCL separadas para alimentar datos a la GPU. Aproximadamente, el programa que se ejecuta en el subproceso host se parece a esto:
static const int kNumQueues = 2;
cl_command_queue default_queue;
cl_command_queue work_queue[kNumQueues];
static const int N = 256;
cl_mem gl_buffers[N];
cl_event finish_events[N];
clEnqueueAcquireGLObjects(default_queue, gl_buffers, N);
int queue_idx = 0;
for (int i = 0; i < N; ++i) {
cl_command_queue queue = work_queue[queue_idx];
cl_mem src = clCreateBuffer(CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ...);
// Enqueue a few kernels
cl_mem tmp1 = clCreateBuffer(CL_READ_WRITE);
clEnqueueNDRangeKernel(kernel1, queue, src, tmp1);
clEnqueueNDRangeKernel(kernel2, queue, tmp1, tmp1);
cl_mem tmp2 = clCreateBuffer(CL_READ_WRITE);
clEnqueueNDRangeKernel(kernel2, queue, tmp1, tmp2);
clEnqueueNDRangeKernel(kernel3, queue, tmp2, gl_buffer[i], finish_events + i);
queue_idx = (queue_idx + 1) % kNumQueues;
}
clEnqueueReleaseGLObjects(default_queue, gl_buffers, N);
clWaitForEvents(N, finish_events);
Con kNumQueues = 1
, esta aplicación funciona más o menos según lo previsto: recopila todo el trabajo en una sola cola de comandos que luego se ejecuta hasta su finalización con la GPU bastante ocupada todo el tiempo. Puedo ver esto mirando la salida del generador de perfiles CodeXL:
Sin embargo, cuando configuro kNumQueues = 2
, espero que suceda lo mismo pero con el trabajo dividido en dos colas. En todo caso, espero que cada cola tenga las mismas características individualmente que la cola única: que comience a funcionar secuencialmente hasta que todo esté hecho. Sin embargo, cuando uso dos colas, puedo ver que no todo el trabajo se divide en las dos colas de hardware:
Al comienzo del trabajo de la GPU, las colas logran ejecutar algunos núcleos de forma asincrónica, aunque parece que ninguno de ellos ocupa completamente las colas de hardware (a menos que mi comprensión esté equivocada). Cerca del final del trabajo de GPU, parece que las colas están agregando trabajo secuencialmente a solo una de las colas de hardware, pero incluso hay veces que no se están ejecutando núcleos. ¿Lo que da? ¿Tengo algún malentendido fundamental sobre cómo se supone que debe comportarse el tiempo de ejecución?
Tengo algunas teorías sobre por qué sucede esto:
Las
clCreateBuffer
llamadas intercaladas obligan a la GPU a asignar recursos del dispositivo desde un conjunto de memoria compartida de forma síncrona que detiene la ejecución de núcleos individuales.La implementación subyacente de OpenCL no asigna las colas lógicas a las colas físicas, y solo decide dónde colocar los objetos en tiempo de ejecución.
Como estoy usando objetos GL, la GPU necesita sincronizar el acceso a la memoria especialmente asignada durante las escrituras.
¿Alguno de estos supuestos es cierto? ¿Alguien sabe qué podría estar causando que la GPU espere en el escenario de dos colas? Cualquier apreciación será apreciada!