CUDA 讓 C/C++ 開發者以熟悉語法釋放 GPU 大規模並行算力。本文系統介紹異構運算模型、Host/Device 記憶體操作、線程層級組織、Kernel 啟動語法、函數類型限制,並附帶完整向量加法範例代碼。
h_)。d_)。__global__ 的 Device 函數,從 Host 啟動,異步執行。| 函數 | 用途 | 關鍵參數 |
|---|---|---|
cudaMalloc | 分配 Device 記憶體 | void **devPtr, size_t size |
cudaFree | 釋放 Device 記憶體 | void *devPtr |
cudaMemcpy | Host↔Device 雙向複製 | dst, src, count, kind |
cudaMemcpyKind:cudaMemcpyHostToDevice、cudaMemcpyDeviceToHost、cudaMemcpyDeviceToDevice。
錯誤檢查模式:
#define CHECK(call) do { \
cudaError_t err = call; \
if (err != cudaSuccess) { \
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, cudaGetErrorString(err)); \
exit(1); \
} \
} while(0)
threadIdx.x/y/z // Block 內索引 blockIdx.x/y/z // Grid 內 Block 索引 blockDim.x/y/z // Block 尺寸 gridDim.x/y/z // Grid 尺寸
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int row = blockIdx.y * blockDim.y + threadIdx.y; int col = blockIdx.x * blockDim.x + threadIdx.x; int idx = row * N + col;
kernel_name<<>>(args...); // grid, block 為 dim3 型別 dim3 block(256); dim3 grid((N + block.x - 1) / block.x);
異步特性:啟動後控制權立即返回 Host,需 cudaDeviceSynchronize() 等待完成。
| 修飾符 | 執行位置 | 呼叫位置 | 備註 |
|---|---|---|---|
__global__ | Device | Host/Device (CC 2.0+) | Kernel 入口,回傳 void |
__device__ | Device | Device | 裝置函數,可組合 __host__ |
__host__ | Host | Host | 普通 C 函數(預設) |
Kernel 限制:僅存取 Device 記憶體、無可變參數、無靜態變數、無函數指標、異步啟動。
#include#include #include #include #include #define CHECK(call) ... // 如上 void initialData(float *ip, int size) { srand((unsigned)time(NULL)); for (int i = 0; i < size; ++i) ip[i] = (rand() & 0xFF) / 10.0f; } void sumArraysOnHost(float *A, float *B, float *C, int N) { for (int i = 0; i < N; ++i) C[i] = A[i] + B[i]; } __global__ void sumArraysOnGPU(float *A, float *B, float *C) { int i = threadIdx.x; C[i] = A[i] + B[i]; } bool checkResult(float *hostRef, float *gpuRef, int N) { const double eps = 1e-8; for (int i = 0; i < N; ++i) if (fabs(hostRef[i] - gpuRef[i]) > eps) return false; return true; } int main(int argc, char **argv) { printf("%s Starting...\n", argv[0]); CHECK(cudaSetDevice(0)); int nElem = 32; size_t nBytes = nElem * sizeof(float); float *h_A = (float*)malloc(nBytes); float *h_B = (float*)malloc(nBytes); float *hostRef = (float*)malloc(nBytes); float *gpuRef = (float*)malloc(nBytes); initialData(h_A, nElem); initialData(h_B, nElem); memset(hostRef, 0, nBytes); memset(gpuRef, 0, nBytes); float *d_A, *d_B, *d_C; CHECK(cudaMalloc((void**)&d_A, nBytes)); CHECK(cudaMalloc((void**)&d_B, nBytes)); CHECK(cudaMalloc((void**)&d_C, nBytes)); CHECK(cudaMemcpy(d_A, h_A, nBytes, cudaMemcpyHostToDevice)); CHECK(cudaMemcpy(d_B, h_B, nBytes, cudaMemcpyHostToDevice)); dim3 block(nElem); dim3 grid(1); sumArraysOnGPU<< >>(d_A, d_B, d_C); printf("Execution config <<<%d, %d>>>\n", grid.x, block.x); CHECK(cudaMemcpy(gpuRef, d_C, nBytes, cudaMemcpyDeviceToHost)); sumArraysOnHost(h_A, h_B, hostRef, nElem); if (checkResult(hostRef, gpuRef, nElem)) printf("Arrays match.\n\n"); else printf("Arrays mismatch!\n"); CHECK(cudaFree(d_A)); CHECK(cudaFree(d_B)); CHECK(cudaFree(d_C)); free(h_A); free(h_B); free(hostRef); free(gpuRef); CHECK(cudaDeviceReset()); return 0; }
nvcc -o vecAdd vecAdd.cu ./vecAdd # 輸出: # vecAdd Starting... # Vector size 32 # Execution config <<<1, 32>>> # Arrays match.
sumMatrixOnGPU2D)。nvprof、Nsight Compute、Nsight Systems。掌握 Host/Device 記憶體邊界、線程索引映射、Kernel 異步啟動模型,是 CUDA 程式設計的「三大支柱」。建議從向量加法起步,逐步遷移至矩陣乘法、卷積、減運算等實際算法,並養成「先在 Host 寫參考實現 → 再移植 Device → 對數驗證」的工程習慣。