คำตอบข้างต้นชี้ให้เห็นว่าขนาดบล็อกสามารถส่งผลกระทบต่อประสิทธิภาพการทำงานได้อย่างไรและแนะนำวิธีการฮิวริสติกทั่วไปสำหรับทางเลือกตามการเพิ่มจำนวนผู้เข้าพัก โดยไม่ต้องการที่จะให้เกณฑ์การเลือกขนาดบล็อกมันจะคุ้มค่าการกล่าวขวัญว่า CUDA 6.5 (ตอนนี้ในรุ่นสมัครอิสระ) รวมถึงหลายฟังก์ชั่นรันไทม์ใหม่ที่จะช่วยในการคำนวณอัตราการเข้าพักและการกำหนดค่าการเปิดดู
เคล็ดลับ CUDA Pro: Occupancy API ช่วยลดความซับซ้อนของการกำหนดค่าการเปิดตัว
หนึ่งในฟังก์ชันที่มีประโยชน์คือการcudaOccupancyMaxPotentialBlockSize
คำนวณขนาดบล็อกแบบฮิวริสติกเพื่อให้ได้จำนวนผู้เข้าพักสูงสุด จากนั้นค่าที่จัดเตรียมโดยฟังก์ชันนั้นสามารถใช้เป็นจุดเริ่มต้นของการปรับพารามิเตอร์การเรียกใช้ให้เหมาะสมด้วยตนเอง ด้านล่างนี้เป็นตัวอย่างเล็กน้อย
#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");
}
แก้ไข
cudaOccupancyMaxPotentialBlockSize
ถูกกำหนดไว้ในcuda_runtime.h
แฟ้มและมีกำหนดดังนี้
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);
}
ความหมายของพารามิเตอร์มีดังต่อไปนี้
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.
โปรดทราบว่าใน CUDA 6.5 เราต้องคำนวณขนาดบล็อก 2D / 3D ของตนเองจากขนาดบล็อก 1D ที่ API แนะนำ
โปรดทราบด้วยว่า API ไดรเวอร์ CUDA มี API ที่เทียบเท่ากับฟังก์ชันสำหรับการคำนวณการเข้าพักดังนั้นจึงเป็นไปได้ที่จะใช้cuOccupancyMaxPotentialBlockSize
ในโค้ด API ไดรเวอร์ในลักษณะเดียวกับที่แสดงสำหรับรันไทม์ API ในตัวอย่างด้านบน