AMD Radeon HD 7800 serisi GPU'm ile kullanmak için bir OpenCL programı yazıyorum. AMD'nin OpenCL programlama kılavuzuna göre , bu GPU nesli zaman uyumsuz olarak çalışabilen iki donanım kuyruğuna sahiptir.
5.5.6 Komut Sırası
Güney Adaları ve sonrası için, cihazlar en az iki donanım bilgi işlem kuyruğunu destekler. Bu, bir uygulamanın, eşzamansız gönderim ve muhtemelen yürütme için iki komut kuyruğuyla küçük gönderilerin verimini artırmasına olanak tanır. Donanım hesaplama kuyrukları aşağıdaki sırayla seçilir: ilk kuyruk = çift OCL komut kuyrukları, ikinci kuyruk = tek OCL kuyrukları.
Bunu yapmak için, verileri GPU'ya beslemek için iki ayrı OpenCL komut kuyruğu oluşturdum. Kabaca, ana bilgisayar iş parçacığı üzerinde çalışan program şöyle görünür:
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);
Ile kNumQueues = 1
, bu uygulama hemen hemen amaçlandığı gibi çalışır: daha sonra GPU tüm zaman oldukça meşgul olmak ile tamamlanması için çalışan tek bir komut kuyruğu tüm çalışmaları toplar. CodeXL profil oluşturucusunun çıktısına bakarak bunu görebiliyorum:
Ancak, ayarladığımda kNumQueues = 2
, aynı şeyin olmasını bekliyorum, ancak çalışma iki kuyruğa eşit olarak bölündü. Bir şey varsa, her kuyruğun tek kuyrukla aynı özelliklere sahip olmasını beklerim: her şey bitene kadar sıralı olarak çalışmaya başlar. Ancak, iki kuyruk kullanırken, tüm çalışmaların iki donanım kuyruğuna bölünmediğini görebiliyorum:
GPU'nun çalışmasının başlangıcında, kuyruklar bazı çekirdekleri eşzamansız olarak çalıştırmayı başarır, ancak her ne kadar donanım kuyruklarını tam olarak kaplamıyor gibi görünüyor (benim anlayışım yanlış değilse). GPU çalışmasının sonuna doğru, sıralar donanım sıralarından yalnızca birine sıralı olarak iş ekliyor gibi görünüyor, ancak çekirdeklerin çalışmadığı zamanlar bile var. Ne oluyor? Çalışma zamanının nasıl davranması gerektiği konusunda bazı temel yanlış anlamaları var mı?
Bunun neden olduğuna dair birkaç teorim var:
Serpiştirilmiş
clCreateBuffer
çağrılar GPU'yu, ayrı ayrı çekirdeklerin yürütülmesini durduran, paylaşılan bir bellek havuzundan aygıt kaynaklarını eşzamanlı olarak ayırmaya zorluyor.Temel OpenCL uygulaması, mantıksal kuyrukları fiziksel kuyruklarla eşlemez ve yalnızca nesnelerin çalışma zamanında nereye yerleştirileceğine karar verir.
GL nesneleri kullandığım için, GPU'nun yazma sırasında özel olarak ayrılmış belleğe erişimi senkronize etmesi gerekir.
Bu varsayımlardan herhangi biri doğru mu? GPU'nun iki kuyruk senaryosunda beklemesine neyin neden olabileceğini bilen var mı? Herhangi bir anlayış takdir edilecektir!