Các luồng được tổ chức để được thực thi bởi GPU như thế nào?
Các luồng được tổ chức để được thực thi bởi GPU như thế nào?
Câu trả lời:
Ví dụ, nếu một thiết bị GPU có 4 đơn vị đa xử lý và chúng có thể chạy 768 luồng mỗi luồng: thì tại một thời điểm nhất định, không quá 4 * 768 luồng sẽ thực sự chạy song song (nếu bạn lên kế hoạch cho nhiều luồng hơn, chúng sẽ chờ khởi đầu của họ).
chủ đề được tổ chức trong các khối. Một khối được thực thi bởi một đơn vị đa xử lý. Các luồng của một khối có thể được xác định (lập chỉ mục) bằng cách sử dụng các chỉ mục 1Dimension (x), 2Dimensions (x, y) hoặc 3Dim (x, y, z) nhưng trong mọi trường hợp x y z <= 768 cho ví dụ của chúng tôi (áp dụng các hạn chế khác đến x, y, z, xem hướng dẫn và khả năng thiết bị của bạn).
Rõ ràng, nếu bạn cần nhiều hơn 4 * 768 luồng đó, bạn cần nhiều hơn 4 khối. Các khối cũng có thể được lập chỉ mục 1D, 2D hoặc 3D. Có một hàng các khối đang chờ để vào GPU (bởi vì, trong ví dụ của chúng tôi, GPU có 4 bộ xử lý và chỉ có 4 khối được thực thi đồng thời).
Giả sử chúng ta muốn một luồng xử lý một pixel (i, j).
Chúng tôi có thể sử dụng khối 64 chủ đề mỗi. Sau đó, chúng ta cần 512 * 512/64 = 4096 khối (để có 512x512 chủ đề = 4096 * 64)
Việc tổ chức (để lập chỉ mục hình ảnh dễ dàng hơn) các luồng trong các khối 2D có blockDim = 8 x 8 (64 luồng trên mỗi khối). Tôi thích gọi nó là threadPerBlock.
dim3 threadsPerBlock(8, 8); // 64 threads
và 2D GridDim = 64 x 64 khối (cần 4096 khối). Tôi thích gọi nó là numBlocks.
dim3 numBlocks(imageWidth/threadsPerBlock.x, /* for instance 512/8 = 64*/
imageHeight/threadsPerBlock.y);
Nhân được khởi chạy như thế này:
myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );
Cuối cùng: sẽ có một cái gì đó giống như "một hàng gồm 4096 khối", trong đó một khối đang chờ để được chỉ định một trong nhiều bộ xử lý của GPU để thực hiện 64 luồng của nó.
Trong nhân, pixel (i, j) được xử lý bởi một luồng được tính theo cách này:
uint i = (blockIdx.x * blockDim.x) + threadIdx.x;
uint j = (blockIdx.y * blockDim.y) + threadIdx.y;
Giả sử GPU 9800GT:
https://www.tutorialspoint.com/cuda/cuda_threads.htm
Một khối không thể có nhiều luồng hoạt động hơn 512 do đó __syncthreads
chỉ có thể đồng bộ hóa số lượng luồng hạn chế. tức là nếu bạn thực hiện như sau với 600 luồng:
func1();
__syncthreads();
func2();
__syncthreads();
sau đó kernel phải chạy hai lần và thứ tự thực hiện sẽ là:
Ghi chú:
Điểm chính __syncthreads
là một hoạt động toàn khối và nó không đồng bộ hóa tất cả các luồng.
Tôi không chắc chắn về số lượng chính xác của các luồng __syncthreads
có thể đồng bộ hóa, vì bạn có thể tạo một khối có hơn 512 luồng và để cho sợi dọc xử lý việc lập lịch. Theo hiểu biết của tôi, chính xác hơn để nói: func1 được thực thi ít nhất cho 512 luồng đầu tiên.
Trước khi tôi chỉnh sửa câu trả lời này (trở lại năm 2010) tôi đã đo các luồng 14x8x32 được đồng bộ hóa bằng cách sử dụng __syncthreads
.
Tôi sẽ đánh giá rất cao nếu ai đó kiểm tra lại điều này để có thông tin chính xác hơn.
__syncthreads
là một hoạt động toàn khối và thực tế là nó không thực sự đồng bộ hóa tất cả các chủ đề là một phiền toái cho người học CUDA. Vì vậy, tôi đã cập nhật câu trả lời của mình dựa trên thông tin bạn đã cho tôi. Tôi rất trân trọng điều này.