NVCC:NVIDIA CUDA 編譯器 (The NVIDIA CUDA Compiler)

重點總覽

項目 重點
nvcc 定位 CUDA Toolkit 內的 toolchain,驅動 offline 編譯;含 compiler、linker、PTX/Cubin assembler,由頂層 nvcc 協調各階段
nvcc vs nvrtc nvcc 是離線編譯;nvrtc 是執行期 online / JIT runtime compiler
Source files .cu=CUDA 原始檔(含 device code),.cuh=CUDA header;.c/.cpp/.cc/.cxx=host-only
編譯流程 nvcc 把 device code 與 host code 分流:device → PTX → ptxas → Cubin;host → host compiler
Fatbin 單一 binary 可內嵌多個 PTX + Cubin target,支援多虛擬/實體 ISA
PTX/Cubin 生成 -arch-gencode-arch=native/all/all-major 控制目標架構
Host code 編譯 -ccbin 指定 host compiler、-Xcompiler 透傳參數、--cudart 選靜/動態 runtime
Separate compilation -rdc=true / -dc 啟用跨 compilation unit 的 device code linking
LTO -dlto / lto_<SM> 在 link 時跨檔最佳化,挽回 separate compilation 的效能
常用選項 language(-std)、debug(-g/-G/-lineinfo)、optimization、profiling、fatbin 壓縮、compiler 效能控制

nvcc 是什麼

nvcc(NVIDIA CUDA Compiler)是 NVIDIA 提供、用來編譯 CUDA C/C++ 與 PTX 程式碼的 toolchain,屬於 CUDA Toolkit 的一部分。它本身是一個頂層工具,會協調整個編譯流程,依各階段呼叫合適的子工具(compiler、linker、PTX assembler、Cubin assembler)。

Tip

這份指南只涵蓋建置應用程式最常用的 nvcc 用法;完整選項請查官方 nvcc documentation。

2.7.1 CUDA Source Files and Headers

用 nvcc 編譯的原始檔可同時含 host code(在 CPU 執行)與 device code(在 GPU 執行)。nvcc 依副檔名判斷內容類型。

副檔名 說明 內容
.c C source file Host-only code
.cpp, .cc, .cxx C++ source file Host-only code
.h, .hpp, .hh, .hxx C/C++ header device / host / 混合皆可
.cu CUDA source file device / host / 混合皆可
.cuh CUDA header file device / host / 混合皆可
Warning

副檔名只是慣例:原文指出一般 header(.h/.hpp/...)其實也可容納 device、host 或混合程式碼。.cuh 主要是用來「標示」這是含 device code 的 header,方便人類區分。

2.7.2 NVCC Compilation Workflow

初始階段,nvcc 會把 device code 與 host code 分離,分別交給 GPU compiler 與 host compiler。

                 example.cu
                     │ nvcc 分流
       ┌─────────────┴──────────────┐
   host code                    device code
       │                            │
  host compiler              GPU compiler (每個 virtual ISA 各跑一次)
 (gcc/clang/...)                    │  例如 compute_90
       │                          PTX assembly
       │                            │ ptxas
   host object                    Cubin (對應 SM 版本的 hardware ISA)
       │                            │
       └──────────► link ◄──────────┘
                     │
              可內嵌多個 PTX + Cubin
                的 Fatbin 容器
                     ▼
                  executable

關鍵事實:

Tip

上述工具的呼叫與協調都由 nvcc 自動完成。-v 可顯示完整編譯流程與工具呼叫;-keep 可保留中間檔(搭配 --keep-dir 指定輸出目錄)。

範例 example.cu

// ----- example.cu -----
#include <stdio.h>
__global__ void kernel() {
    printf("Hello from kernel\n");
}
void kernel_launcher() {
    kernel<<<1, 1>>>();
    cudaDeviceSynchronize();
}
int main() {
    kernel_launcher();
    return 0;
}

此檔同時含 device(__global__ kernel)與 host(main/kernel_launcher)程式碼,正是 nvcc 分流編譯的典型輸入。

2.7.3 NVCC Basic Usage

基本編譯指令:

nvcc <source_file>.cu -o <output_file>

nvcc 接受常見 compiler flag:

Flag 用途
-I <path> 指定 include 目錄
-L <path> 指定 library 路徑
-l<library> 連結某個 library(如 -lcublas
-D<macro>=<value> 定義巨集
nvcc example.cu -I path_to_include/ -L path_to_library/ -lcublas -o <output_file>

2.7.3.1 PTX 與 Cubin 生成

預設下,nvcc 會為 CUDA Toolkit 支援的最早(最低 compute_XY / sm_XY)GPU 架構產生 PTX 與 Cubin,以最大化相容性

nvcc example.cu -arch=compute_<XY>  # 例 compute_80:PTX-only,GPU 向前相容
nvcc example.cu -arch=sm_<XY>       # 例 sm_80:PTX + Cubin,GPU 向前相容
nvcc example.cu -arch=native        # 自動偵測並為當前 GPU 產生 Cubin(無 PTX,無向前相容)
nvcc example.cu -arch=all           # 為所有支援架構產生 Cubin,另含最新 PTX 供向前相容
nvcc example.cu -arch=all-major     # 為所有主要架構產生 Cubin(如 sm_80, sm_90),另含最新 PTX
-arch PTX Cubin GPU 向前相容
compute_XY 是(靠 JIT PTX)
sm_XY
native 僅當前 GPU
all / all-major 含最新 PTX 多架構

進階:可個別指定 PTX 與 Cubin target。

# 為 virtual arch compute_80 產生 PTX,並編成 sm_86 的 Cubin,且保留 compute_80 PTX
nvcc example.cu -arch=compute_80 -gpu-code=sm_86,compute_80   # PTX + Cubin
# 編成 sm_86、sm_89 的 Cubin(不保留 PTX)
nvcc example.cu -arch=compute_80 -gpu-code=sm_86,sm_89
nvcc example.cu -gencode=arch=compute_80,code=sm_86,sm_89     # 同上
# 多個 gencode:compute_80→sm_86,sm_89 與 compute_90→sm_90
nvcc example.cu -gencode=arch=compute_80,code=sm_86,sm_89 \
                -gencode=arch=compute_90,code=sm_90
Important

compute_XYvirtual architecture(產生可 JIT 的 PTX,向前相容);sm_XYreal hardware architecture(直接產生 Cubin)。sm_XY 同時也內嵌 PTX 以保留向前相容性,但 native 不含 PTX 因而無向前相容

2.7.3.2 Host Code Compilation Notes

不含 device code 或 symbol 的 compilation unit(原始檔 + 其 header)可直接用 host compiler 編譯。

nvcc example.cu -ccbin=clang++
export NVCC_CCBIN='gcc'
nvcc example.cu -Xcompiler=-O3   # -O3 被透傳給 host compiler

2.7.3.3 Separate Compilation of GPU Code

nvcc 預設為 whole-program compilation:要求用到的所有 GPU code 與 symbol 都在同一 compilation unit 內。

要讓 device function 呼叫其他 compilation unit 定義的 device function 或存取其 device variable,必須加 -rdc=true(或別名 -dc),啟用跨單元的 device code linking,這稱為 separate compilation

比較 Whole-program(預設) Separate compilation
跨檔 device 連結 不允許 允許(需 -dc/-rdc=true
程式碼組織 較受限 較彈性
編譯時間 可改善
二進位大小 可更小
效能 較佳 可能因 device link 受影響(可用 LTO 緩解)
建置複雜度 較低 較高

separate compilation 的條件:

Warning

CUDA 13 起,__global__ 函式與 __managed__ / __device__ / __constant__ 變數預設為 internal linkage。這會影響跨單元引用,需特別注意。

// ----- definition.cu -----
extern __device__ int device_variable = 5;
__device__
int device_function() { return 10; }
// ----- example.cu -----
extern __device__ int device_variable;   // 以 extern 引用
__device__
int device_function();                    // 宣告,定義在另一單元
__global__ void kernel(int* ptr) {
    device_variable = 0;
    *ptr = device_function();
}
nvcc -dc definition.cu -o definition.o
nvcc -dc example.cu    -o example.o
nvcc definition.o example.o -o program   # device link + 最終連結

每個 .cu 都以 -dc 各自編成 object,最後再由 nvcc 連結;這就是 separate compilation 的標準流程。

2.7.4 Common Compiler Options

2.7.4.1 Language Features

nvcc 支援從 C++03 到 C++23 的核心語言特性。

2.7.4.2 Debugging Options

Flag 作用
-g 產生 host code 的 debug 資訊(gdb/lldb 用)
-G 產生 device code 的 debug 資訊(cuda-gdb 用),並定義 __CUDACC_DEBUG__ 巨集
-lineinfo 產生 device code 的行號資訊,不影響執行效能,搭配 compute-sanitizer 追蹤 kernel 執行
Warning

nvcc 對 GPU code 預設用最高最佳化等級 -O3-G阻止部分編譯器最佳化,因此 debug code 效能預期較低。可用 -DNDEBUG 關閉 runtime assertion 以免拖慢執行。

2.7.4.3 Optimization Options

Warning

使用 advanced controls file(ACF)可能造成編譯失敗或執行結果錯誤,原文明言「Use at your own risk」。

可從編譯器取得、利於進階最佳化的輸出旗標:

Flag 輸出
-res-usage 列出資源使用:每個 kernel 的 register、shared memory、constant memory、local memory
-opt-info=inline 列出被 inline 的函式資訊
-Xptxas=-warn-lmem-usage 使用到 local memory 時警告
-Xptxas=-warn-spills register 被 spill 到 local memory 時警告
Tip

更細緻的浮點行為控制請見 Floating-Point Computation 章節與 nvcc 文件。

separate compilation 因跨檔最佳化機會有限,效能可能低於 whole-program。LTOlink 時跨各別編譯的檔案做最佳化,代價是編譯時間增加,但可挽回大部分 whole-program 的效能,同時保留 separate compilation 的彈性。

啟用 LTO 需 -dlto 旗標或 lto_<SM version> link-time 最佳化 target:

# 方式一:-dlto
nvcc -dc -dlto -arch=sm_100 definition.cu -o definition.o
nvcc -dc -dlto -arch=sm_100 example.cu    -o example.o
nvcc -dlto definition.o example.o -o program

# 方式二:lto_<SM> target
nvcc -dc -arch=lto_100 definition.cu -o definition.o
nvcc -dc -arch=lto_100 example.cu    -o example.o
nvcc -dlto definition.o example.o -o program

注意:device 編譯(-dc)與最終 link 兩步驟都要帶上 LTO 旗標。

2.7.4.5 Profiling Options

Nsight ComputeNsight Systems直接 profile,編譯時不必加額外旗標。但下列旗標能讓 profiling 工具把原始碼與生成碼對應起來:

2.7.4.6 Fatbin Compression

nvcc 預設會壓縮 binary 內的 fatbin。

模式 取向
speed預設 著重快速解壓縮時間
size 著重縮小 fatbin 大小
balance 在 speed 與 size 間取折衷
none 停用壓縮

2.7.4.7 Compiler Performance Controls

加速與分析「編譯過程本身」的選項:

Flag 作用
-t <N> 用 N 個 CPU thread 平行化單一 compilation unit、多 GPU 架構的編譯
-split-compile <N> 用 N 個 CPU thread 平行化最佳化階段
-split-compile-extended <N> 更積極的 split compilation;需 LTO
-Ofc <N> device code 編譯速度等級
-time <filename> 產生 CSV 表,記錄各編譯階段耗時
-fdevice-time-trace 產生 device code 編譯的 time trace

考試/測驗重點

情境/關鍵字 答案
nvcc vs nvrtc nvcc=offline 編譯;nvrtc=online/JIT runtime compiler
.cu vs .cuh .cu=CUDA 原始檔;.cuh=含 device code 的 header(慣例)
device code 編譯鏈順序 C/C++ device code → PTX → ptxas → Cubin
compute_XY vs sm_XY compute_XY=virtual ISA(產生 PTX,可 JIT);sm_XY=real hardware ISA(產生 Cubin),以 SM version 識別
-arch=native 的代價 只為當前 GPU 產 Cubin、無 PTX、無向前相容
預設為哪個架構生成 支援的最早/最低架構,為了最大相容性
顯示完整編譯流程 -v;保留中間檔 -keep(搭 --keep-dir
跨檔呼叫 device function 需要 -rdc=true 或別名 -dc(separate compilation)
為何 separate compilation 非預設 效能可能因 device code linking 受影響
CUDA 13 linkage 陷阱 __global____managed__/__device__/__constant__ 變數預設 internal linkage
const __device__ 變數 必須以 extern 定義並引用
預設連哪種 runtime 靜態 libcudart_static;改動態用 --cudart=shared
指定 host compiler -ccbin 或環境變數 NVCC_CCBIN;透傳參數用 -Xcompiler
-g vs -G vs -lineinfo -g=host debug;-G=device debug(定義 __CUDACC_DEBUG__、抑制最佳化);-lineinfo=行號、不影響效能
GPU code 預設最佳化等級 -O3(最高)
限制每 thread register 數 -Xptxas=-maxrregcount=N
register spill / local memory 警告 -Xptxas=-warn-spills / -Xptxas=-warn-lmem-usage
列印 kernel 資源使用 -res-usage(register/shared/constant/local memory)
LTO 啟用方式 -dltolto_<SM> target;compile 與 link 都要帶
-src-in-ptx 前提 需搭配 -lineinfo
fatbin 預設壓縮模式 speed--compress-mode 另有 size/balance/none;-no-compress 全關)
平行編譯 vs 平行最佳化 -t <N>=多架構編譯平行;-split-compile <N>=最佳化階段平行
--apply-controls 風險 ACF 可能造成編譯失敗或執行錯誤(自負風險)