Tôi đang viết chương trình OpenCL để sử dụng với GPU AMD Radeon HD 7800 series. Theo hướng dẫn lập trình OpenCL của AMD , thế hệ GPU này có hai hàng đợi phần cứng có thể hoạt động không đồng bộ.
5.5.6 Hàng đợi lệnh
Đối với Quần đảo phía Nam và sau đó, các thiết bị hỗ trợ ít nhất hai hàng đợi tính toán phần cứng. Điều đó cho phép một ứng dụng tăng thông lượng của các công văn nhỏ với hai hàng đợi lệnh để gửi không đồng bộ và có thể thực thi. Các hàng đợi tính toán phần cứng được chọn theo thứ tự sau: hàng đợi thứ nhất = hàng đợi lệnh OCL, hàng đợi thứ hai = hàng đợi OCL lẻ.
Để làm điều này, tôi đã tạo hai hàng đợi lệnh OpenCL riêng để cung cấp dữ liệu cho GPU. Một cách thô bạo, chương trình chạy trên luồng máy chủ trông giống như thế này:
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);
Với kNumQueues = 1
, ứng dụng này hoạt động khá nhiều như dự định: nó tập hợp tất cả các công việc vào một hàng lệnh duy nhất sau đó chạy đến khi hoàn thành với GPU khá bận rộn suốt thời gian. Tôi có thể thấy điều này bằng cách nhìn vào đầu ra của trình lược tả CodeXL:
Tuy nhiên, khi tôi đặt kNumQueues = 2
, tôi hy vọng điều tương tự sẽ xảy ra nhưng với công việc chia đều cho hai hàng đợi. Nếu có bất cứ điều gì, tôi hy vọng mỗi hàng đợi có các đặc điểm riêng giống như một hàng đợi: nó bắt đầu hoạt động tuần tự cho đến khi mọi thứ được thực hiện. Tuy nhiên, khi sử dụng hai hàng đợi, tôi có thể thấy rằng không phải tất cả các công việc được chia thành hai hàng đợi phần cứng:
Khi bắt đầu công việc của GPU, các hàng đợi quản lý để chạy một số hạt nhân không đồng bộ, mặc dù có vẻ như không bao giờ chiếm hết các hàng đợi phần cứng (trừ khi hiểu lầm của tôi). Gần cuối GPU hoạt động, có vẻ như các hàng đợi đang thêm công việc liên tục vào một trong các hàng đợi phần cứng, nhưng thậm chí có những lúc không có hạt nhân nào đang chạy. Đưa cái gì? Tôi có một số hiểu lầm cơ bản về cách chạy thời gian được cho là hành xử?
Tôi có một vài lý thuyết về lý do tại sao điều này xảy ra:
Các
clCreateBuffer
cuộc gọi xen kẽ đang buộc GPU phân bổ tài nguyên thiết bị từ nhóm bộ nhớ dùng chung một cách đồng bộ, giúp ngăn chặn việc thực thi các hạt nhân riêng lẻ.Việc triển khai OpenCL cơ bản không ánh xạ các hàng đợi logic đến các hàng đợi vật lý và chỉ quyết định nơi đặt các đối tượng khi chạy.
Vì tôi đang sử dụng các đối tượng GL, GPU cần đồng bộ hóa quyền truy cập vào bộ nhớ được phân bổ đặc biệt trong quá trình ghi.
Có bất kỳ giả định nào trong số này là đúng không? Có ai biết điều gì có thể khiến GPU phải chờ trong kịch bản hai hàng không? Bất kỳ và tất cả cái nhìn sâu sắc sẽ được đánh giá cao!