Sto scrivendo un programma OpenCL da utilizzare con la mia GPU AMD Radeon HD serie 7800. Secondo la guida alla programmazione OpenCL di AMD , questa generazione di GPU ha due code hardware che possono funzionare in modo asincrono.
5.5.6 Coda di comando
Per le isole meridionali e successive, i dispositivi supportano almeno due code di calcolo hardware. Ciò consente a un'applicazione di aumentare il throughput di piccoli invii con due code di comando per l'invio asincrono e possibilmente l'esecuzione. Le code di calcolo dell'hardware sono selezionate nel seguente ordine: prima coda = anche code dei comandi OCL, seconda coda = code OCL dispari.
Per fare ciò, ho creato due code di comando OpenCL separate per inviare dati alla GPU. All'incirca, il programma in esecuzione sul thread host è simile al seguente:
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
, questa applicazione funziona praticamente come previsto: raccoglie tutto il lavoro in una singola coda di comandi che viene quindi eseguita fino al completamento con la GPU che è abbastanza occupata per tutto il tempo. Sono in grado di vedere questo guardando l'output del profiler CodeXL:
Tuttavia, quando ho impostato kNumQueues = 2
, mi aspetto che accada la stessa cosa, ma con il lavoro suddiviso uniformemente su due code. Semmai, mi aspetto che ogni coda abbia le stesse caratteristiche singolarmente dell'unica coda: che inizi a lavorare in sequenza fino a quando non viene fatto tutto. Tuttavia, quando si utilizzano due code, posso vedere che non tutto il lavoro è suddiviso tra le due code hardware:
All'inizio del lavoro della GPU, le code riescono a far funzionare alcuni kernel in modo asincrono, anche se sembra che nessuno dei due occupi completamente le code hardware (a meno che la mia comprensione non sia errata). Verso la fine del lavoro della GPU, sembra che le code aggiungano lavoro in sequenza solo a una delle code hardware, ma ci sono anche volte in cui nessun kernel è in esecuzione. Cosa dà? Ho dei malintesi fondamentali su come dovrebbe comportarsi il runtime?
Ho alcune teorie sul perché questo sta accadendo:
Le
clCreateBuffer
chiamate intervallate stanno costringendo la GPU ad allocare le risorse del dispositivo da un pool di memoria condiviso in modo sincrono che blocca l'esecuzione dei singoli kernel.L'implementazione OpenCL sottostante non associa le code logiche alle code fisiche e decide solo dove posizionare gli oggetti in fase di esecuzione.
Poiché sto usando oggetti GL, la GPU deve sincronizzare l'accesso alla memoria appositamente allocata durante le scritture.
Qualcuno di questi presupposti è vero? Qualcuno sa cosa potrebbe causare l'attesa della GPU nello scenario a due code? Qualsiasi intuizione sarebbe apprezzata!