C/C++ 語言限制 (C/C++ Language Restrictions)

重點總覽

CUDA C++ 在 device 端不是完整的 ISO C++:RTTI、例外、long double、recursion(__global__/__tile_global__/__tile__)皆不支援,且跨 execution space 的指標解參考、取址、變數宣告都有嚴格限制。除了通用限制外,每個 C++ 標準版本(C++11/14/17/20/23)還疊加各自的特殊規則。核心心法是:host 與 device 是兩個獨立執行空間,凡是「跨空間引用記憶體/位址/型別」幾乎都是 undefined behavior(UB)

項目 重點
device 不支援 typeiddynamic_casttry/catch/throwlong double、trigraphs
保留 namespace cuda::nv::cooperative_groups:: 加定義是 UB
跨空間解參考/取址 host 解 device 指標、device 解 host 指標 = UB
recursion __global__/__tile_global__/__tile__ 不支援;__device__
__global__ 參數 經 constant memory 傳遞,總大小上限 32,764 bytes,不可 variadic/std::initializer_list
host→device 傳參 用 raw memcpy(跳過 copy constructor),destructor 可能在 kernel 完成前執行
各標準限制 C++11 constexpr 跨空間、C++14 deduced return、C++17 inline 變數、C++20 <=>、C++23 無新增

通用不支援功能與 namespace 保留

Unsupported Features

限制 說明/條件
RTTI 與例外 device code 不支援 typeiddynamic_casttry/catch/throw
long double device code 不支援
Trigraphs 所有平台皆不支援
Digraphs Windows 上不支援
自訂 operator new/new[]/delete/delete[] 不能用來取代編譯器內建版本;host 與 device 上皆為 UB

Namespace Reservations

namespace cuda {
    struct foo;        // ERROR:在 cuda namespace 內宣告 class
    void bar();        // ERROR:在 cuda namespace 內宣告 function
    namespace utils {} // ERROR:在 cuda namespace 內宣告 namespace
}
namespace utils {
    namespace cuda {   // CORRECT:cuda 巢狀於非保留 namespace utils 內
        void bar();
    }
}
using namespace utils; // ERROR:等同把 symbol 加進 global scope 的 cuda namespace

指標、記憶體位址與變數限制

Pointers and Memory Addresses

指標解參考(*pp->mp[0]只允許在記憶體所屬的 execution space 內,否則為 UB(多半 segfault)。

操作 結果
host 解參考 global/shared/constant memory 指標 UB
device 解參考 host memory 指標 UB
host code 取 __device__ function 位址 不允許
host 取得的 __global__ function 位址用於 device(反之亦然) 不允許
cudaGetSymbolAddress() 取得 __device__/__constant__ 變數位址 只能用於 host code

Variables:memory space specifier 規則

情境 不允許的 specifier 例外
host function 內 non-extern 區域變數 __device__/__tile__/__shared__/__managed__/__constant__ extern __device__ OK
device function 內 non-extern 且 non-static 變數 __device__/__tile__/__constant__/__managed__ extern __device__ OK
class/struct/union data member 上述全部 僅編譯期求值的 static member(const/constexpr)
formal parameter(形參) 上述全部

const-qualified 變數

Tip

C++14 起建議改用 constexprinline constexpr(C++17)變數,不受上述型別限制,可直接用於 device code。__managed__ 變數不支援 const-qualified 型別。

volatile-qualified 變數

Warning

CUDA C++ volatile 不適合

  • 執行緒間同步 → 改用 cuda::atomic_refcuda::atomic 或 Atomic Functions(atomicAdd/atomicExch + __threadfence()
  • Memory Mapped IO (MMIO) → 改用 inline PTX 的 PTX MMIO(如 ld.relaxed.mmio.sys.u32 / st.relaxed.mmio.sys.u32),它嚴格保留存取次數

static 變數

extern 變數

函式限制

主題 限制
Recursion __global__/__tile_global__/__tile__ 不支援__device____host__ __device__
External Linkage 帶 external linkage 的 device 變數/函式需 separate compilation;ODR-use 時參數與回傳型別須在該 TU 內為完整型別
Formal Parameters memory space specifier 不可用於形參

__global__ Function Parameters

__global__ Function Arguments Passing

啟動來源 規則
device code 啟動 kernel 每個 argument 必須 trivially copyable 且 trivially destructible
host code 啟動 kernel 允許 non-trivial copy/destructor,但處理流程偏離標準 C++(見下)
host→device 傳參的兩個陷阱

  1. Raw memory copy 取代 copy constructor:CUDA Runtime 用 memcpy 複製 raw 記憶體把參數送到 device,user-defined copy constructor 被跳過,其副作用不會發生(例如指向自身成員的指標在 device 端會失效)。
  2. Destructor 可能在 kernel 完成前執行:kernel 啟動與 host 非同步,若參數有 non-trivial destructor,destructor 可能在 host 端早於 kernel 結束就執行,破壞有副作用的程式邏輯。

類別與模板限制

Class-type Variables / Function Members

Implicitly-Declared / Explicitly-Defaulted Functions

Polymorphic Classes 與 Windows Class Layout

限制 說明
跨空間複製 polymorphic 物件 device↔host(含 __global__ 參數)複製 polymorphic 物件為 UB
overridden virtual function execution space 必須與 base class 中的版本一致(否則 ERROR)
Windows class layout CUDA 遵循 IA64 ABI,MSVC 不遵循;對 pointer-to-member、polymorphic class、含多重空 base 等型別 T,host/device 的 layout 與 size 可能不同,跨空間 bitwise 複製為 UB

Templates

下列型別不可當作 __global__ 函式或 __device__/__constant__ 變數(C++14)的 template argument:

Tile code 限制(__tile__ / __tile_global__

__tile____tile_global__ 函式有額外限制。下列語言構造在 tile code 不支援

不支援構造 不支援構造
do/while/for 迴圈內的 return virtual function 呼叫
goto 述句 switch 述句
產生 function pointer/reference、pointer-to-member 的運算式 function pointer 與 pointer-to-member function 呼叫
pointer-to-member variable 存取 128-bit 整數或浮點型別
含 bitfield 的型別 大小超過 16 MB 的型別
含 virtual base class 或 virtual function 的型別 用 non-placement operator new/delete 的動態配置/釋放

其他限制:

C++11/14/17/20/23 各標準版本限制

C++11 Restrictions

限制 說明
inline namespace 當 enclosing namespace 已有同名同簽章實體時,不可在 inline namespace 內定義 __global__/__tile_global__ 函式、device 類變數、surface/texture 變數(cudaSurfaceObject_t/cudaTextureObject_t
inline unnamed namespace 上述實體不可宣告於 inline unnamed namespace 的 namespace scope
constexpr 函式 __global__ 不可宣告為 constexpr;預設不可跨 execution space 呼叫 constexpr(跨空間呼叫為 UB);標 constexpr 的 template function 其特化(specialization)不保證為 constexpr 函式
constexpr 變數 預設不可跨空間使用;可直接用於 device 的型別見下
__global__ variadic template 只能有單一 pack parameter,且必須列在最後
= default 函式 execution space 自動推導;顯式 specifier 被忽略,除非 out-of-line 定義或 virtual
std::initializer_list 成員函式預設為 __host__ __device__ __tile__,可從 device 呼叫;__global__/__tile_global__ 不可有此型別參數
std::move/std::forward 預設為 __host__ __device__ __tile__,可從 device 呼叫
相關 nvcc flag

  • --expt-relaxed-constexpr:放寬 constexpr 跨空間呼叫(在需常數求值的 context 中),並定義 __CUDACC_RELAXED_CONSTEXPR__
  • --no-host-device-initializer-list:把 std::initializer_list 成員函式改視為 __host__,device 不可呼叫。
  • --no-host-device-move-forward:把 std::move/std::forward 改視為 __host__
  • cuda::std::move / cuda::std::forward永遠__host__ __device__

可直接用於 device code 的 constexpr 變數型別:

Warning

由於 relaxed-constexpr 下缺乏編譯期診斷,建議勿從 device code 呼叫 Standard C++ header 的 std:: 函式(實作隨 host 平台而異);改用 libcu++ 的 cuda::std:: 等價功能。

C++14 Restrictions

限制 說明
deduced return type __global__/__tile_global__ 不可有 auto 推導回傳型別;host code 不可 introspect __device__ 函式的推導回傳型別(CUDA frontend 會先把其回傳型別改為 void
variable template 使用 Microsoft compiler 時,__device__/__tile__/__constant__ variable template 不可 const-qualified(non-portable)

C++17 Restrictions

限制 說明
inline 變數 nvcc 僅在 Separate Compilation 模式或 internal linkage 時,允許帶 __device__/__tile__/__constant__/__managed__ 的 inline 變數(whole program 模式下為 ERROR)
structured binding 不可帶 memory space specifier(__device__/__tile__/__shared__/__constant__/__managed__
Note

用 gcc/g++ host compiler 時,宣告為 __managed__ 的 inline 變數可能對 debugger 不可見。

C++20 Restrictions

限制 說明
three-way comparison <=> __device__/__global__ 函式支援,但部分用法隱含依賴 host 提供的 C++ Standard Library(如 std::strong_ordering),可能需 --expt-relaxed-constexpr 並要求 host 實作滿足 device code 需求
consteval 函式 可從 host 與 device 雙方呼叫,與其 execution space 無關

C++23 Restrictions

考試/測驗重點

題型 關鍵答案
device code 支援 try/catch/throw 嗎? 不支援;RTTI(typeid/dynamic_cast)也不支援
long double 在 device 可用嗎? 不可
哪些 namespace 加定義是 UB? cuda::nv::cooperative_groups:: 及其巢狀 namespace
host 解參考 device 指標會怎樣? UB(多半 segfault);反向(device 解 host 指標)亦同
cudaGetSymbolAddress() 取得的位址用在哪? 只能用於 host code
哪些 function 不支援 recursion? __global____tile_global____tile____device__/__host__ __device__
__global__ 參數總大小上限? 32,764 bytes,經 constant memory 傳遞
__global__ 參數可否是 variadic / std::initializer_list 皆不可;但 C++11 variadic template 可(單一 pack 且置最後)
host 啟動 kernel 時參數如何複製? raw memcpy,跳過 copy constructor,且 destructor 可能在 kernel 完成前執行
無 memory space 標註的 const(host)變數,何時可在 device code 直接以值使用? 用 constant expression 初始化、非 volatile、且為 built-in 整數/浮點(MSVC 浮點除外)
volatile 適合做執行緒同步嗎? 不適合;用 cuda::atomic_ref/cuda::atomic/atomic functions;MMIO 用 PTX MMIO
static 變數在 __host__ __device__ 函式有幾份? 兩份(host 一、device 一);dynamic initialization 不允許
whole program 模式下 extern __device__ 變數合法嗎? 不合法(ERROR);例外是 dynamic __shared__
overridden virtual function 的 execution space 要求? 必須與 base class 版本一致
polymorphic 物件跨 host/device 複製? UB(含 __global__ 參數)
__global__ 函式可當 class member 嗎? 不可;可在 friend 宣告但不可定義
implicitly-declared / explicitly-defaulted 函式的 execution space 怎麼定? 取所有呼叫它的函式之 execution space 聯集;分析時 __global__ caller 視為 __device__ caller
= default 函式上的顯式 execution space specifier 何時生效? 預設被忽略(execution space 自動推導),除非 out-of-line 定義或 virtual
std::move/std::forward/std::initializer_list 成員預設 execution space? 預設 __host__ __device__ __tile__,可從 device 呼叫;但 cuda::std::move/cuda::std::forward 恆為 __host__ __device__
tile code 支援 switch/goto/virtual 呼叫嗎? 皆不支援;型別 >16 MB、含 bitfield、128-bit 型別亦不支援
__global__ 可宣告為 constexpr 嗎? 不可;__global__ 也不可有 auto 推導回傳型別(C++14)
--expt-relaxed-constexpr 的作用? 放寬 constexpr 跨 execution space 呼叫,定義 __CUDACC_RELAXED_CONSTEXPR__
structured binding 可帶 memory space specifier 嗎? 不可
consteval 函式可從哪呼叫? host 與 device 皆可,與 execution space 無關
Windows 上為何 bitwise 跨空間複製某些物件是 UB? CUDA 用 IA64 ABI、MSVC 不用,layout/size 可能不同