iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
Software Development

渲染與GPU編程系列 第 24

# Day 23|第一個 CUDA 專案:環境與 Hello World

  • 分享至 

  • xImage
  •  

這一篇只做兩件事:

  1. CUDA 開發環境裝好、驗證顯示卡可用;
  2. 寫出你人生第一支 「Hello from GPU」 程式,看到 GPU 真的在幫你工作。
    我會用超直白的比喻帶你走,碰到不同作業系統也有對應的做法。

0)先釐清:我需要哪些條件?

  • NVIDIA 顯示卡(近 10 年的卡幾乎都行)。

  • 作業系統:Windows 或 Linux(含 WSL2)。

    • macOS:Apple Silicon 沒原生 CUDA;舊款 NVIDIA Mac 也太老,不建議當入門環境。
  • 管理權限:能裝驅動與 CUDA Toolkit。


1)三步檢查:我的電腦能跑 CUDA 嗎?

  1. 看見顯示卡

    • Windows:按下 Win + X →「裝置管理員」→「顯示卡」應該看到 NVIDIA
    • Linux:lspci | grep -i nvidia(能看到列出 NVIDIA 代表有裝)。
  2. 驅動正常

    • Windows / Linux:打開終端機輸入 nvidia-smi
    • 你應該會看到類似表格,包含 Driver 版本、GPU 名稱、記憶體使用量。若顯示「找不到指令」代表驅動或 PATH 尚未設定好。
  3. 編譯器存在

    • 裝完 CUDA Toolkit 後,在終端機輸入 nvcc --version,應該會印出 NVCC 與 CUDA 版本。

心法:nvidia-smi 管驅動、nvcc 管開發工具。兩者都 OK,你就準備好了。


2)安裝 CUDA Toolkit(簡述重點)

Windows(建議)

  1. 先確保 NVIDIA 顯示卡驅動是新版本(Game Ready 或 Studio 都可)。
  2. 下載並安裝 CUDA Toolkit(預設選項即可)。
  3. 建議一起安裝 Visual Studio(Community 版),因為 NVCC 會用它的 C++ 工具鍊。
  4. 安裝完成之後,開新的 Developer Command Prompt,輸入 nvcc --version 應可看到版本。

Linux(Ubuntu 範例)

  1. 安裝 NVIDIA 驅動(用官方套件或 Ubuntu 內建驅動管理工具)。
  2. 安裝 CUDA Toolkit(可用官方存放庫或 .run 安裝器)。
  3. 確認 /usr/local/cuda/bin/usr/local/cuda/lib64 已加到 PATH/LD_LIBRARY_PATH。
  4. 新開一個 shell,測試 nvcc --versionnvidia-smi

WSL2(Windows 10/11)

  1. 主系統先裝好 NVIDIA 驅動(支援 WSL)
  2. WSL2(Ubuntu)內依照 Linux 流程裝 CUDA Toolkit。
  3. 在 WSL shell 測試 nvidia-sminvcc --version

如果你對「版本相容」有疑慮:驅動要不低於 CUDA Toolkit 要求。通常裝最新驅動再裝對應的 CUDA最省心。


3)你的第一個 CUDA 專案:檔案與目錄

建立一個資料夾 cuda-hello/,裡面只有一個檔案:

cuda-hello/
└─ hello.cu

副檔名 .cu 代表「這是 CUDA C/C++ 原始碼」。


4)Hello World 的核心概念(先懂再寫)

  • __global__:把一個函式標記成「GPU Kernel」。

  • <<<grid, block>>>:啟動 Kernel 的「人力配置」。

    • block 是「每隊幾人」
    • grid 是「要幾隊」
  • printf in GPU:可以在 Kernel 裡印字,但要在結束前 cudaDeviceSynchronize() 讓輸出刷到主機端。

  • 錯誤檢查cudaGetLastError()cudaDeviceSynchronize() 是入門最實用的兩個。


5)把程式寫進 hello.cu

這份程式會先由 CPU 印一次「Hello from CPU!」,再請 GPU 開 1 個 block × 4 個 thread,由第 0 線程代表印出「Hello from GPU!」。這樣不會一次印太多行,初學好觀察。

// hello.cu
#include <cstdio>
#include <cuda_runtime.h>

__global__ void helloFromGPU() {
    // 只讓每個 block 的第 0 條 thread 印,避免刷一堆行
    if (threadIdx.x == 0) {
        printf("Hello from GPU! (block %d, thread %d)\n", blockIdx.x, threadIdx.x);
    }
}

int main() {
    printf("Hello from CPU!\n");

    // 啟動 1 個 block,每個 block 有 4 個 thread
    dim3 grid(1);
    dim3 block(4);
    helloFromGPU<<<grid, block>>>();

    // 檢查 Launch 是否成功
    cudaError_t launchErr = cudaGetLastError();
    if (launchErr != cudaSuccess) {
        fprintf(stderr, "Kernel launch error: %s\n", cudaGetErrorString(launchErr));
        return 1;
    }

    // 等 GPU 做完,並把 printf 的內容刷出來
    cudaError_t syncErr = cudaDeviceSynchronize();
    if (syncErr != cudaSuccess) {
        fprintf(stderr, "Device sync error: %s\n", cudaGetErrorString(syncErr));
        return 1;
    }

    return 0;
}

6)編譯與執行

Windows(使用 NVCC)

打開「x64 Native Tools」或「Developer Command Prompt for VS」,切到專案資料夾:

nvcc hello.cu -o hello.exe
hello.exe

Linux / WSL2

nvcc hello.cu -o hello
./hello

你應該看到類似輸出

Hello from CPU!
Hello from GPU! (block 0, thread 0)

如果你把 if (threadIdx.x == 0) 拿掉,會變成同一個 block 的 4 條 thread 都印,你會看到 4 行「Hello from GPU!」。這就是平行的最直觀證據。


7)我剛剛到底做了什麼?(把細節說人話)

  • CPU 端:跑 main(),呼叫 helloFromGPU<<<1,4>>>()
    → 意思是:「GPU 啟動 1 個 block,每個 block 派出 4 個 thread 跑 helloFromGPU()。」

  • GPU 端:每條 thread 都會執行 helloFromGPU() 的程式碼。

    • 我們用 if (threadIdx.x == 0) 限制只有隊內第 0 位代表發言。
  • cudaDeviceSynchronize():讓 CPU 等 GPU 做完,順便把 GPU printf 的輸出收齊。


8)常見錯誤與快修

  1. 'nvcc' 不是內部或外部指令 / command not found

    • 表示 PATH 沒加好或終端機沒重開。重開一個新的終端機再試;或把 CUDA bin 路徑加到 PATH。
  2. cuda_runtime.h: No such file or directory

    • 你用的是一般的 cl.exeg++;請用 nvcc 來編譯 .cu 檔。
  3. 執行時出現 no kernel image is available for execution on the device

    • 代表你編譯出的架構與 GPU 不相容。
    • 解法:先跑 deviceQuery 看你的 Compute Capability(例如 7.5、8.6)。
    • 再用 -arch=sm_75-arch=sm_86 編譯:
      nvcc -arch=sm_86 hello.cu -o hello
  4. nvidia-smi 正常但程式跑不起來

    • 多半是驅動與 Toolkit 版本不匹配,或用到 WSL2 但沒裝對應的 CUDA。
    • 建議:更新到較新的 NVIDIA 驅動,重裝對應版 CUDA Toolkit,或先用 Samples 驗證。
  5. GPU printf 沒出現

    • 確認結尾有 cudaDeviceSynchronize();沒有等到 GPU 完成,輸出可能沒刷出來。
    • 也別在大量 thread 都 printf,一次會沖太多行。

9)加碼:兩分鐘玩起「多線程」

想看到多線程真的同時跑?把 Kernel 啟動參數改一下:

dim3 grid(2);   // 2 個 block
dim3 block(8);  // 每個 block 8 條 thread

再把條件改成只讓「每個 block 的第 0 條」印:

if (threadIdx.x == 0) {
    printf("Hello from block %d, thread %d\n", blockIdx.x, threadIdx.x);
}

你會看到兩行(block 0 與 block 1 各印一行)。
if 拿掉,就會看到 2 × 8 = 16 行 印出來(每條 thread 都說話)。


一句話總結

安裝驅動 + CUDA Toolkit → 用 nvcc 編譯 .cu → 啟動第一個 Kernel → 看見 GPU 印出 Hello
你已經踏進 CUDA 的世界:知道什麼是 Kernel / Grid / Block / Thread,也嘗到「一次啟動很多執行緒」的滋味。


上一篇
# Day 22|CUDA 是什麼?GPU 平行運算基礎
系列文
渲染與GPU編程24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言