Saya menulis program OpenCL untuk digunakan dengan GPU AMD Radeon HD 7800 series saya. Menurut panduan pemrograman OpenCL AMD , generasi GPU ini memiliki dua antrian perangkat keras yang dapat beroperasi secara tidak sinkron.
5.5.6 Perintah Antrian
Untuk Kepulauan Selatan dan yang lebih baru, perangkat mendukung setidaknya dua antrian penghitungan perangkat keras. Itu memungkinkan aplikasi untuk meningkatkan throughput kiriman kecil dengan dua antrian perintah untuk pengiriman asinkron dan kemungkinan eksekusi. Antrian penghitungan perangkat keras dipilih dalam urutan berikut: antrian pertama = bahkan antrian perintah OCL, antrian kedua = antrian OCL ganjil.
Untuk melakukan ini, saya telah membuat dua antrian perintah OpenCL terpisah untuk memasukkan data ke GPU. Secara kasar, program yang berjalan di utas host terlihat seperti ini:
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);
Dengan kNumQueues = 1
, aplikasi ini cukup berfungsi sebagaimana mestinya: aplikasi ini mengumpulkan semua pekerjaan menjadi satu antrian perintah yang kemudian berjalan hingga selesai dengan GPU yang cukup sibuk sepanjang waktu. Saya dapat melihat ini dengan melihat output dari profiler CodeXL:
Namun, ketika saya mengatur kNumQueues = 2
, saya berharap hal yang sama terjadi tetapi dengan pekerjaan terbagi rata dalam dua antrian. Jika ada, saya berharap setiap antrian memiliki karakteristik yang sama secara individual dengan antrian satu: bahwa antrian mulai bekerja secara berurutan sampai semuanya selesai. Namun, ketika menggunakan dua antrian, saya dapat melihat bahwa tidak semua pekerjaan dibagi di antara dua antrian perangkat keras:
Pada awal pekerjaan GPU, antrian berhasil menjalankan beberapa kernel secara serempak, meskipun sepertinya tidak pernah sepenuhnya menempati antrian perangkat keras (kecuali pemahaman saya salah). Menjelang akhir kerja GPU, sepertinya antrian menambahkan pekerjaan secara berurutan hanya ke salah satu antrian perangkat keras, tetapi bahkan ada saat-saat ketika kernel tidak berjalan. Apa yang menyebabkannya? Apakah saya memiliki beberapa kesalahpahaman mendasar tentang bagaimana seharusnya runtime berperilaku?
Saya punya beberapa teori mengapa ini terjadi:
clCreateBuffer
Panggilan yang diselingi memaksa GPU untuk mengalokasikan sumber daya perangkat dari kumpulan memori bersama secara sinkron yang menghentikan eksekusi kernel individual.Implementasi OpenCL yang mendasarinya tidak memetakan antrian logis ke antrian fisik, dan hanya memutuskan tempat untuk meletakkan objek saat runtime.
Karena saya menggunakan objek GL, GPU perlu menyinkronkan akses ke memori yang dialokasikan secara khusus selama penulisan.
Adakah asumsi-asumsi ini yang benar? Adakah yang tahu apa yang menyebabkan GPU menunggu dalam skenario dua-antrian? Setiap dan semua wawasan akan sangat dihargai!