函式與變數限定詞 (Function and Variable Annotations)

重點總覽

CUDA C/C++ 在標準 C++ 之上加了一組限定詞 (annotation),用來標示函式在哪個情境執行、變數放在哪段記憶體、以及如何啟動 kernel。核心分三類:執行空間限定詞__global__ / __device__ / __host__)、記憶體空間限定詞__device__ / __constant__ / __shared__ / __managed__),以及啟動相關限定詞__launch_bounds__ / __maxnreg__ / __cluster_dims__)。再配合內建型別(向量型別、dim3)與內建變數(threadIdx 等)構成 kernel 的基本語彙。

項目 重點
__global__ kernel 進入點;host 端可呼叫、在 device(SIMT) 執行;回傳 void、需 <<<>>>、不可遞迴、非 class member
__device__ / __host__ 分別只在 device / host 執行;__host__ __device__ 兩邊都編譯,用 __CUDA_ARCH__ 分流
__device__ 變數 放 device 全域記憶體;host 端用 cudaMemcpyToSymbol 等存取
__constant__ device 端唯讀,只能由 host 改
__shared__ block 內共享;static 尺寸或 extern __shared__ 動態尺寸
__managed__ host/device 自動共用;位址非常數運算式、不可作 decltype 非括號引數
__restrict__ 承諾指標不別名 (no aliasing),放開優化;會增加暫存器壓力
__grid_constant__ __global__ 參數共用單一位址、唯讀,省去 per-thread copy
__launch_bounds__ 提示 maxThreadsPerBlock 等,引導暫存器配置與占用率
內建變數 gridDim/blockDimdim3blockIdx/threadIdxuint3warpSize 為 int

Function Execution Space Specifiers

執行空間限定詞標示函式在 hostSIMT(傳統 device)、或 tile 情境執行與被呼叫:

限定詞 執行於 可被呼叫自
__host__(或不寫) Host Host
__device__ SIMT SIMT
__global__ SIMT Host + SIMT
__tile__ Tile Tile
__tile_global__ Tile Host
__global__ / __tile_global__ 的限制

  • 必須回傳 void
  • 不可為 class / struct / union 的成員。
  • 必須提供 execution configuration(即 <<<...>>>,見 Kernel Configuration)。
  • 不支援遞迴。
  • 另見 __global__ 函式參數的額外限制(見 __grid_constant__ 參數)。

__global__ / __tile_global__ 的呼叫是非同步的:在 device 完成執行前就先返回 host thread。

多重執行空間與 __CUDA_ARCH__

宣告多個執行空間(如 __host__ __device__)的函式會為每個情境各編譯一份。用 __CUDA_ARCH__ 巨集區分 host / device 程式路徑:

__host__ __device__ void func() {
#if defined(__CUDA_ARCH__)
  // Device 端程式路徑
#else
  // Host 端程式路徑
#endif
}
__CUDA_ARCH__ 在 host 端是未定義的,因此凡是要在 host 程式中用到的值(例如 threads per block)都不能依賴它。

Variable Memory Space Specifiers

記憶體空間限定詞標示變數在 device 上的儲存位置:

限定詞 位置 可存取者 生命週期 唯一實例
__device__ device 全域記憶體 grid 內 threads / Runtime API Program / CUDA context 每個 device 一份
__tile__ device 全域記憶體 tile blocks / Runtime API(tile code 可直接以名稱存取) Program / CUDA context 每個 device 一份
__constant__ device constant 記憶體 grid 內 threads / Runtime API Program / CUDA context 每個 device 一份
__managed__ host 與 device(自動) host/device threads Program 每個 program 一份
__shared__ device(SM 上) 同一 block 的 threads Block 每個 block 一份
無限定詞 device(暫存器) 單一 thread 單一 thread 單一 thread
__device__   float device_var   = 4.0f;  // 位於 device 記憶體
__constant__ float constant_var = 4.0f;  // 位於 constant 記憶體
int main() {
  float* device_ptr;
  cudaGetSymbolAddress((void**)&device_ptr, device_var); // 取得符號位址
  size_t symbol_size;
  cudaGetSymbolSize(&symbol_size, device_var);            // 取得大小 (4 bytes)
  float host_var;
  cudaMemcpyFromSymbol(&host_var, device_var, sizeof(host_var)); // device -> host
  host_var = 3.0f;
  cudaMemcpyToSymbol(device_var, &host_var, sizeof(host_var));   // host -> device
}

__shared__ 記憶體

extern __shared__ char dynamic_smem[];      // 動態 shared memory
__global__ void kernel() {
  __shared__ int smem_var1[4];              // static 尺寸
  auto smem_var2 = (int*)dynamic_smem;      // dynamic 尺寸
}
int main() {
  size_t shared_memory_size = 16;
  kernel<<<1, 1, shared_memory_size>>>();   // 第三個參數指定動態 shared bytes
  cudaDeviceSynchronize();
}

__managed__ 記憶體

__managed__ 變數的主要限制

  • 位址不是常數運算式(不能當 template 非型別參數)。
  • 不可為 reference 型別 T&
  • 當 CUDA runtime 可能尚未在有效狀態時(static/dynamic 初始化或解構、exit() 後、__attribute__((constructor)) 等),不可使用其位址或值。
  • 不可作為 decltype()非括號 id-expression 引數(decltype(global_var) 為錯誤,decltype((global_var)) 為合法)。
  • 一致性 (coherence/consistency) 行為與動態配置的 managed memory 相同。

__tile__ 記憶體

Inlining、__restrict____grid_constant__

Inlining Specifiers(限 __host__ / __device__

限定詞 作用
__noinline__ 指示 nvcc 不要 inline
__forceinline__ 強制在單一 translation unit 內 inline
__inline_hint__ 啟用 LTO(Link-Time Optimization)時跨 TU 的積極 inline
三者互斥;套在 __tile__ 函式上會被忽略。

__restrict__ 指標

__device__ void f(const float* __restrict__ a,
                  const float* __restrict__ b,
                  float* __restrict__ c);
副作用:因為要把載入與共同子運算式快取到暫存器,會增加暫存器壓力,可能降低 occupancy 反而傷效能。

另外,標了 __restrict____global__ const 指標會編成唯讀快取載入(PTX ld.global.nc,等同 __ldg())。

__grid_constant__ 參數

__global__ 函式參數加上 __grid_constant__,可阻止編譯器建立per-thread copy,改讓 grid 內所有 thread 透過單一位址存取,提升效能。

Annotation Summary(Table 41)

Annotation 用於 __host__ / __device__ 用於 __global__
__noinline__ / __forceinline__ / __inline_hint__ Function ×
__restrict__ Pointer Parameter Pointer Parameter
__grid_constant__ × Parameter
__launch_bounds__ × Function
__maxnreg__ × Function
__cluster_dims__ × Function

Built-in Types and Variables

Host Compiler 型別擴充

Built-in Variables(device-only)

變數 型別 內容
gridDim dim3 grid 維度(block 數量)
blockDim dim3 block 維度(thread 數量)
blockIdx uint3 block 在 grid 內的索引
threadIdx uint3 thread 在 block 內的索引
warpSize int warp 內的 thread 數(執行期值,常見為 32)

Built-in Vector Types

CUDA 提供從基本整數/浮點衍生的向量型別(host 與 device 皆可用),命名規則為 <base>1<base>4,例如 char1..char4int1..int4float1..float4double1..double4 等。

int sum(int4 v) { return v.x + v.y + v.z + v.w; }
int4 add_one(int x, int y, int z, int w) {
  return make_int4(x + 1, y + 1, z + 1, w + 1);
}

對齊重點(Table 43 摘要):

型別 Size Alignment
char1 1 1
int4 / uint4 16 16
float4 16 16
double2 16 16
longlong2 16 16
int3 / float3 12 4
long4ulong4longlong4ulonglong4double4CUDA 13 已 deprecated,未來版本可能移除;改用對齊明確的 ..._16a / ..._32a 變體。

另外 long 在 Windows 64-bit(LLP64)為 4 bytes,在 Linux 64-bit(LP64)為 8 bytes,故相關型別大小/對齊會隨平台不同。

Kernel Configuration

每次呼叫 __global__ / __tile_global__ 函式都必須提供 execution configuration,語法為在函式名與引數列之間插入 <<<grid_dim, block_dim, dynamic_smem_bytes, stream>>>

欄位 型別 說明
grid_dim dim3 grid 維度;x*y*z = 啟動的 block 數
block_dim dim3 block 維度;x*y*z = 每 block thread 數(__tile_global__ 時須為 1
dynamic_smem_bytes size_t 選用,預設 0;per-block 動態配置的 shared bytes(供 extern __shared__
stream cudaStream_t 選用,預設 NULL;關聯的 stream
__global__ void kernel(float* parameter);
kernel<<<grid_dim, block_dim, dynamic_smem_bytes>>>(parameter);

Thread Block Cluster(__cluster_dims__

__global__ void __cluster_dims__(2, 1, 1) kernel(float* parameter); // X=2, Y=Z=1

Launch Bounds(__launch_bounds__

__global__ void
__launch_bounds__(maxThreadsPerBlock, minBlocksPerMultiprocessor, maxBlocksPerCluster)
MyKernel(...) { ... }
參數 必要性 PTX directive 意義
maxThreadsPerBlock 必要 .maxntid 應用程式啟動 kernel 時的最大 threads per block
minBlocksPerMultiprocessor 選用 .minnctapersm 期望每個 SM 的最少 resident block 數
maxBlocksPerCluster 選用 .maxclusterrank 每個 cluster 的最大 block 數
為了向前相容並確保至少一個 block 能在 SM 上跑,建議至少加上單引數版 __launch_bounds__(maxThreadsPerBlock);否則可能出現「too many resources requested for launch」錯誤。雙引數版可在分析後改善效能。
不同主要架構的最佳 launch bounds 通常不同,可用 __CUDA_ARCH__ 在 device 端切換;但因 host 端 __CUDA_ARCH__ 未定義,host 設定 threads/block 要用不依賴它的常數,或執行期依 cudaGetDeviceProperties 的 compute capability 決定。--resource-usage 可回報暫存器使用量。

Maximum Registers per Thread(__maxnreg__

__global__ void __maxnreg__(maxNumberRegistersPerThread) MyKernel(...) { ... }

考試/測驗重點

題型 關鍵答案
__global__ 函式回傳型別? 必須是 void
__global__ 可否為 class member / 遞迴? 都不行;也必須有 <<<>>>
__global__ 呼叫是同步還是非同步? 非同步,device 完成前就返回 host
如何在 __host__ __device__ 內分流 host/device? #if defined(__CUDA_ARCH__)
__CUDA_ARCH__ 在 host 程式中? 未定義,故 host 端不可依賴它設值
__constant__ 在 device 端的特性? 唯讀,只能由 host 經 Runtime API 修改
從 host 存取 __device__/__constant__ 用哪些 API? cudaGetSymbolAddress/SizecudaMemcpyTo/FromSymbol
動態 __shared__ 怎麼宣告與配置? extern __shared__ 陣列/指標;大小由 <<<,,bytes>>>
static __shared__ 變數可否在宣告處初始化? 不行
__managed__ 位址是常數運算式嗎? 不是;也不能當 template 非型別參數
decltype(managed_var) vs decltype((managed_var)) 前者錯誤(非括號 id-expr),後者合法
__restrict__ 承諾什麼?副作用? 指標不別名;增加暫存器壓力、可能降 occupancy
__restrict__ const __global__ 指標被編成什麼? 唯讀快取載入(ld.global.nc / __ldg()
__grid_constant__ 的三大特性? 全 thread 共用單一位址、唯讀、kernel 生命週期
gridDim/blockDimblockIdx/threadIdx 的型別? 前兩者 dim3,後兩者 uint3
warpSize 型別與常見值? int,執行期值,常見 32
dim3 各分量預設值? 1(C++11 起)
向量型別第四分量用哪個欄位?工廠函式? .wmake_<type>()
CUDA 13 哪些向量型別被 deprecated? long4/ulong4/longlong4/ulonglong4/double4
__launch_bounds__ 第一個參數編成哪個 PTX directive? .maxntidmaxThreadsPerBlock
__launch_bounds____maxnreg__ 能否並用? 不能
__cluster_dims__ 需要哪個 compute capability? 9.0 以上;不可用於 __tile_global__
execution config 的四個欄位順序? <<<grid_dim, block_dim, dynamic_smem_bytes, stream>>>
int3/float3 的 size 與 alignment? size 12、alignment 4(非 16);char3 為 size 3、alignment 1
__tile_global__ kernel 的 block_dim 必須是? 1(由編譯器決定排程多少 thread)
execution config 引數與函式引數誰先求值? execution config 引數於實際函式引數求值
__maxnreg__ 的 PTX directive?--maxrregcount 影響? .maxnreg;含 __maxnreg__ 的 kernel 會忽略 --maxrregcount