Các câu trả lời ở trên chỉ ra kích thước khối có thể tác động đến hiệu suất như thế nào và đề xuất phương pháp kinh nghiệm chung cho sự lựa chọn của nó dựa trên tối đa hóa công suất thuê. Nếu không muốn để cung cấp các tiêu chí để lựa chọn kích thước khối, nó sẽ là đáng nói đến là CUDA 6.5 (nay trong phiên bản Release Candidate) bao gồm một số chức năng thời gian chạy mới để hỗ trợ trong việc tính toán công suất và cấu hình khởi động, thấy
Mẹo chuyên nghiệp CUDA: API chiếm giữ đơn giản hóa cấu hình khởi chạy
Một trong những chức năng hữu ích là cudaOccupancyMaxPotentialBlockSize
tính toán theo kinh nghiệm kích thước khối để đạt được công suất tối đa. Sau đó, các giá trị được cung cấp bởi chức năng đó có thể được sử dụng làm điểm bắt đầu của quá trình tối ưu hóa thủ công các thông số khởi chạy. Dưới đây là một ví dụ nhỏ.
#include <stdio.h>
/************************/
/* TEST KERNEL FUNCTION */
/************************/
__global__ void MyKernel(int *a, int *b, int *c, int N)
{
int idx = threadIdx.x + blockIdx.x * blockDim.x;
if (idx < N) { c[idx] = a[idx] + b[idx]; }
}
/********/
/* MAIN */
/********/
void main()
{
const int N = 1000000;
int blockSize; // The launch configurator returned block size
int minGridSize; // The minimum grid size needed to achieve the maximum occupancy for a full device launch
int gridSize; // The actual grid size needed, based on input size
int* h_vec1 = (int*) malloc(N*sizeof(int));
int* h_vec2 = (int*) malloc(N*sizeof(int));
int* h_vec3 = (int*) malloc(N*sizeof(int));
int* h_vec4 = (int*) malloc(N*sizeof(int));
int* d_vec1; cudaMalloc((void**)&d_vec1, N*sizeof(int));
int* d_vec2; cudaMalloc((void**)&d_vec2, N*sizeof(int));
int* d_vec3; cudaMalloc((void**)&d_vec3, N*sizeof(int));
for (int i=0; i<N; i++) {
h_vec1[i] = 10;
h_vec2[i] = 20;
h_vec4[i] = h_vec1[i] + h_vec2[i];
}
cudaMemcpy(d_vec1, h_vec1, N*sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(d_vec2, h_vec2, N*sizeof(int), cudaMemcpyHostToDevice);
float time;
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0);
cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, MyKernel, 0, N);
// Round up according to array size
gridSize = (N + blockSize - 1) / blockSize;
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
printf("Occupancy calculator elapsed time: %3.3f ms \n", time);
cudaEventRecord(start, 0);
MyKernel<<<gridSize, blockSize>>>(d_vec1, d_vec2, d_vec3, N);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime(&time, start, stop);
printf("Kernel elapsed time: %3.3f ms \n", time);
printf("Blocksize %i\n", blockSize);
cudaMemcpy(h_vec3, d_vec3, N*sizeof(int), cudaMemcpyDeviceToHost);
for (int i=0; i<N; i++) {
if (h_vec3[i] != h_vec4[i]) { printf("Error at i = %i! Host = %i; Device = %i\n", i, h_vec4[i], h_vec3[i]); return; };
}
printf("Test passed\n");
}
BIÊN TẬP
Giá trị cudaOccupancyMaxPotentialBlockSize
được định nghĩa trong cuda_runtime.h
tệp và được định nghĩa như sau:
template<class T>
__inline__ __host__ CUDART_DEVICE cudaError_t cudaOccupancyMaxPotentialBlockSize(
int *minGridSize,
int *blockSize,
T func,
size_t dynamicSMemSize = 0,
int blockSizeLimit = 0)
{
return cudaOccupancyMaxPotentialBlockSizeVariableSMem(minGridSize, blockSize, func, __cudaOccupancyB2DHelper(dynamicSMemSize), blockSizeLimit);
}
Ý nghĩa của các tham số như sau
minGridSize = Suggested min grid size to achieve a full machine launch.
blockSize = Suggested block size to achieve maximum occupancy.
func = Kernel function.
dynamicSMemSize = Size of dynamically allocated shared memory. Of course, it is known at runtime before any kernel launch. The size of the statically allocated shared memory is not needed as it is inferred by the properties of func.
blockSizeLimit = Maximum size for each block. In the case of 1D kernels, it can coincide with the number of input elements.
Lưu ý rằng, kể từ CUDA 6.5, người ta cần tính toán kích thước khối 2D / 3D của riêng mình từ kích thước khối 1D do API đề xuất.
Cũng lưu ý rằng API trình điều khiển CUDA chứa các API tương đương về chức năng để tính toán tỷ lệ sử dụng, do đó, có thể sử dụng cuOccupancyMaxPotentialBlockSize
trong mã API trình điều khiển theo cách tương tự được hiển thị cho API thời gian chạy trong ví dụ trên.