GPGPU 程式優化的前提是「知己知彼」。不同 GPU 的計算單元 (CU) 數量、處理元素 (PE) 數量、本地記憶體大小、最大工作群組尺寸等參數差異極大。盲目編寫 Kernel 而不針對硬體特性調優,效能往往差強人意。clGetDeviceInfo 正是獲取這些關鍵參數的標準介面。
cl_int clGetDeviceInfo(
cl_device_id device, // 目標設備 ID
cl_device_info param_name, // 查詢參數枚舉
size_t param_value_size, // 緩衝區大小
void* param_value, // 緩衝區指標
size_t* param_value_size_ret // 實際返回大小
);
核心機制:必須調用兩次:
param_value = NULL,param_value_size = 0,獲取 param_value_size_ret 確定所需緩衝區大小| 參數枚舉 | 資料類型 | 用途說明 |
|---|---|---|
CL_DEVICE_NAME | char[] | 設備名稱字串 |
CL_DEVICE_VENDOR | char[] | 廠商名稱 |
CL_DEVICE_MAX_COMPUTE_UNITS | cl_uint | 計算單元 (CU) 數量,決定並行度上限 |
CL_DEVICE_MAX_WORK_GROUP_SIZE | size_t | 單工作群組最大 work-item 數 |
CL_DEVICE_LOCAL_MEM_SIZE | cl_ulong | 本地記憶體大小 (bytes),關鍵優化參考 |
CL_DEVICE_GLOBAL_MEM_SIZE | cl_ulong | 全域記憶體總大小 |
CL_DEVICE_MAX_MEM_ALLOC_SIZE | cl_ulong | 單次分配最大記憶體 |
CL_DEVICE_EXTENSIONS | char[] | 支援的擴展列表,空格分隔 |
#include <CL/cl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void print_device_info(cl_device_id device) {
cl_int err;
size_t size;
// 1. 獲取設備名稱
err = clGetDeviceInfo(device, CL_DEVICE_NAME, 0, NULL, &size);
char* name = (char*)malloc(size);
clGetDeviceInfo(device, CL_DEVICE_NAME, size, name, NULL);
printf("Device: %s\n", name);
free(name);
// 2. 獲取計算單元數量
cl_uint cu_count;
clGetDeviceInfo(device, CL_DEVICE_MAX_COMPUTE_UNITS,
sizeof(cl_uint), &cu_count, NULL);
printf("Compute Units: %u\n", cu_count);
// 3. 獲取本地記憶體大小
cl_ulong local_mem;
clGetDeviceInfo(device, CL_DEVICE_LOCAL_MEM_SIZE,
sizeof(cl_ulong), &local_mem, NULL);
printf("Local Memory: %llu KB\n", local_mem / 1024);
// 4. 獲取最大工作群組尺寸
size_t max_wg_size;
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE,
sizeof(size_t), &max_wg_size, NULL);
printf("Max Work Group Size: %zu\n", max_wg_size);
// 5. 獲取支援的擴展
err = clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, 0, NULL, &size);
char* exts = (char*)malloc(size);
clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, size, exts, NULL);
printf("Extensions: %s\n", exts);
free(exts);
}
int main() {
cl_platform_id platform;
clGetPlatformIDs(1, &platform, NULL);
cl_device_id device;
clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
print_device_info(device);
return 0;
}
// 根據查詢結果動態設定 local work size
size_t local_size;
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE,
sizeof(size_t), &local_size, NULL);
// 確保不超過硬體限制,並嘗試 2 的冪次方以獲得最佳效能
size_t optimal_local = 1;
while (optimal_local * 2 <= local_size && optimal_local * 2 <= 256) {
optimal_local *= 2;
}
// 啟動 Kernel
size_t global_size = (data_size + optimal_local - 1) / optimal_local * optimal_local;
clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size, &optimal_local, 0, NULL, NULL);
CL_INVALID_DEVICE、CL_INVALID_VALUE 都會導致查詢失敗cl_khr_fp16、cl_khr_int64_base_atomics 等擴展前必須先查詢確認