Warp 函式、CUDA 巨集與裝置函式 (Warp Functions, CUDA Macros and Device Functions)
重點總覽
Warp 函式讓「同一 warp 內的 thread」彼此通訊與協作。其中 vote / match / reduce / shuffle 等同步原語皆以 *_sync 結尾並帶 mask 參數(連同 __syncwarp 統稱 warp __sync intrinsic),mask 用來在硬體執行前確保收斂;__activemask() 為例外,不帶 mask、也非同步原語。本篇另含 CUDA 專屬巨集(__CUDA_ARCH__ 等)與裝置端工具函式(位址空間判斷/轉換、低階快取載入/儲存、__trap、__nanosleep、DPX)。
| 項目 | 重點 |
|---|---|
__activemask() |
回傳 32-bit「當下 active lane」快照;不等同「會走某分支的 lane」,僅供 opportunistic 用 |
| Warp vote | __all_sync / __any_sync / __ballot_sync:對 predicate 做 reduction-and-broadcast |
| Warp match | __match_any_sync / __match_all_sync:broadcast-and-compare,找出值相同的 lane |
| Warp reduce | __reduce_<op>_sync:CC 8.x+ 才支援;add/min/max + and/or/xor |
| Warp shuffle | __shfl_sync / _up / _down / _xor:不經 shared memory 交換值,width 必為 2 的次方 |
| mask 鐵則 | 每個呼叫 thread 的 bit 必須=1、非呼叫 thread=0;參與者須用「相同 mask」(disjoint 例外) |
| 為何 Volta+ 要 mask | thread 可獨立排程,mask 明示參與集合並強制收斂;舊式無 mask intrinsic 已不安全 |
| 記憶體序 | 所有 warp *_sync intrinsic 不提供任何 memory ordering / barrier |
__CUDA_ARCH__ |
僅 device code 有定義;值 = compute_<version> * 10(compute_80 → 800) |
| 位址空間 intrinsic | __isGlobal/__isShared/... 判斷;__cvta_* 在 generic 與特定空間間轉換 |
__nanosleep(ns) |
約睡 ns 奈秒,上限約 1 ms;常用於 exponential back-off mutex |
| DPX | 3 路 min/max + fused add-min/max,含 ReLU;用於動態規劃演算法 |
原文反覆建議:warp 級操作盡量用 CUB Warp-Wide "Collective" Primitives(cub::WarpScan / cub::WarpReduce)或 libcu++ 的 cuda::device::warp_shuffle() / warp_match_all(),以兼顧效率、安全與可攜性。
Warp Active Mask:__activemask()
unsigned __activemask();
回傳 32-bit 整數遮罩,第 N bit=1 表示呼叫當下第 N lane 為 active;inactive(含已退出程式)的 lane 為 0。
__activemask() 只是瞬間快照,僅供 opportunistic warp 程式設計。下例的結果是 non-deterministic、跨次執行可能不同:
if (pred) {
// Invalid:at_least_one 的值不確定
at_least_one = __activemask() > 0;
}
在 __activemask() 收斂的 thread,不保證在後續指令仍收斂——除非後續是 warp 同步 intrinsic(*_sync)。編譯器可能重排指令,使 active 集合改變。
關鍵反模式:即使下一個指令就是 *_sync,把 __activemask() 的回傳值拿去當它的 mask 仍不安全——因為 active 集合可能在「取 mask」與「呼叫 *_sync」這兩步之間改變。正確做法是直接用已知的靜態 mask 呼叫 *_sync。
unsigned mask = __activemask();
// 假設 mask == 0xFFFFFFFF(全部 bit 設立、所有 thread active)
int predicate = threadIdx.x % 2 == 0; // 偶數 lane 為 1、奇數為 0
int result = __any_sync(mask, predicate); // active 集合可能已不被保留
Warp Vote、Match 與 Reduce
Warp Vote(reduction-and-broadcast)
每個非退出 thread 輸入一個整數 predicate,與 0 比較後做歸約並把單一結果廣播回所有參與 thread。
| 函式 | 回傳語意 |
|---|---|
int __all_sync(mask, predicate) |
mask 內所有非退出 thread 的 predicate 皆非零 → 回非零 |
int __any_sync(mask, predicate) |
mask 內至少一個 thread 的 predicate 非零 → 回非零 |
unsigned __ballot_sync(mask, predicate) |
第 N bit=1 ⟺ 第 N thread predicate 非零且 active;否則為 0 |
Warp Match(broadcast-and-compare)
unsigned __match_any_sync(unsigned mask, T value);
unsigned __match_all_sync(unsigned mask, T value, int* pred);
__match_any_sync:回傳 mask 內「與自己 bitwise 值相同」的非退出 thread 之遮罩。__match_all_sync:若 mask 內所有非退出 thread 的值皆相同 → 回 mask 並把*pred設 true;否則回 0、*pred設 false。T可為int、unsigned、long、unsigned long、long long、unsigned long long、float、double。
Warp Reduce(CC 8.x 以上)
T __reduce_add_sync(unsigned mask, T value); // T: 有/無號整數
T __reduce_min_sync(unsigned mask, T value);
T __reduce_max_sync(unsigned mask, T value);
unsigned __reduce_and_sync(unsigned mask, unsigned value);
unsigned __reduce_or_sync (unsigned mask, unsigned value);
unsigned __reduce_xor_sync(unsigned mask, unsigned value);
- add/min/max:對 mask 內各 thread 的
value做算術歸約,T為有號或無號整數。 - and/or/xor:對
value做 bitwise 歸約。
Vote / Match / Reduce 全部受 Warp __sync Intrinsic Constraints 約束,且不提供任何 memory ordering。
Warp Shuffle 函式
T __shfl_sync (unsigned mask, T value, int srcLane, int width = warpSize);
T __shfl_up_sync (unsigned mask, T value, unsigned delta, int width = warpSize);
T __shfl_down_sync(unsigned mask, T value, unsigned delta, int width = warpSize);
T __shfl_xor_sync (unsigned mask, T value, int laneMask, int width = warpSize);
不經 shared memory,在 warp 內非退出 thread 間交換 value。
| 函式 | 來源 lane 計算 | 邊界行為 |
|---|---|---|
__shfl_sync |
直接複製 srcLane 的值 |
srcLane 超出 [0, width-1] → 取 srcLane % width(同 subsection 內) |
__shfl_up_sync |
caller - delta(值往上移) |
不跨 width wrap;最低 delta 個 lane 值不變 |
__shfl_down_sync |
caller + delta(值往下移) |
不跨 width wrap;最高 delta 個 lane 值不變 |
__shfl_xor_sync |
caller XOR laneMask(butterfly) |
width<warpSize 時可讀「較早」群組;讀「較晚」群組會取回自己的值 |
width 與 subsection
width 小於 warpSize 時,warp 被切成多個 subsection,各自獨立、邏輯 lane ID 從 0 起算。width 必為 2 的次方且在 [1, warpSize]:1、2、4、8、16、32;其他值結果未定義。
T 可為:
int、unsigned、long、unsigned long、long long、unsigned long long、float、double。- 含
cuda_fp16.h時可用__half、__half2。 - 含
cuda_bf16.h時可用__nv_bfloat16、__nv_bfloat162。
若目標 thread 為 inactive(未在本次呼叫中參與),取回的值未定義。下列皆為未定義行為:lane 0 未參與卻被當 srcLane、目的 lane 不在 active 集合、width 非 2 的次方。Shuffle 不隱含 memory barrier。
典型用法範例
// 廣播:全 warp 從 lane 0 取 value
value = __shfl_sync(0xFFFFFFFF, value, 0);
// 蝴蝶式 full-warp 歸約:log2(32) = 5 步
for (int i = 1; i <= 16; i *= 2)
value += __shfl_xor_sync(0xFFFFFFFF, value, i);
// 8-thread sub-partition 的 inclusive scan:log2(8) = 3 步
for (int delta = 1; delta <= 4; delta *= 2) {
int tmp = __shfl_up_sync(0xFFFFFFFF, value, delta, /*width=*/8);
if ((laneId % 8 - delta) >= 0) value += tmp; // source_lane < 0 的不變
}
Warp __sync Intrinsic Constraints(mask 規則)
所有 __shfl_*_sync、__match_*_sync、__reduce_*_sync、__syncwarp 都靠 mask 指明哪些 lane 參與,並在硬體執行前確保收斂。mask 每個 bit 對應一個 lane ID(threadIdx.x % warpSize),intrinsic 會等到 mask 內所有非退出 thread 都抵達呼叫點。
正確執行需滿足:
- 每個呼叫 thread,其對應 bit 在 mask 中必須=1。
- 每個非呼叫 thread,其對應 bit 必須=0(已退出 thread 被忽略)。
- mask 內所有非退出 thread 必須以相同 mask 值執行該 intrinsic。
- 不同 thread 可用不同 mask 並發呼叫,只要這些 mask 彼此 disjoint(不重疊)——即使在 divergent 控制流中亦合法。
正確:if (threadIdx.x < 4) __all_sync(0b1111, pred); // 0,1,2,3 都參與
正確:disjoint __syncwarp(tid<16 ? 0xFFFF : 0xFFFF0000);
錯誤:active 卻沒設 __all_sync(0b0000011, pred); // 2,3 active 但漏設
錯誤:設了卻非 active __all_sync(0b1111111, pred); // 4,5,6 非 active
錯誤:參與者 mask 重疊不一致 __all_sync(tid==0 ? 1 : 0xFFFFFFFF, pred);
呼叫 thread 不在 mask 中;mask 內某非退出 thread 既不退出也不在同一程式點以同一 mask 呼叫;或條件式中各非退出 thread 的條件未一致 evaluate——皆可能造成 kernel hang 或未定義行為。
本節來源只陳述:mask 用來在硬體執行前確保收斂,且效率最佳是全員參與(mask = 0xFFFFFFFF)。以下因果屬補充背景(來自 Independent Thread Scheduling / CUDA 9 棄用脈絡,非本節原文):Volta 起 thread 可獨立排程、不再保證 lock-step 收斂,mask 明確列出「應參與的 lane 集合」並強制收斂;這也是舊式無 mask 的 __shfl/__any/__ballot 被 *_sync 版取代的原因。
CUDA-Specific Macros
__CUDA_ARCH__
代表「正在編譯的目標虛擬架構」,其值可能與實際裝置 compute capability 不同。只在 device code(__device__、__host__ __device__、__global__)中有定義,可用來分流架構特化路徑、或區分 host/device 程式碼。
- 數值關係:
__CUDA_ARCH__ = <version> * 10。例如nvcc --generate-code arch=compute_80,code=sm_90→__CUDA_ARCH__為 800。
__CUDA_ARCH__ 四大約束
- 下列實體的型別簽章不得依賴
__CUDA_ARCH__是否定義或其值:__global__函式/模板、__device__與__constant__變數、texture/surface。 __global__函式模板的具現化不得依賴__CUDA_ARCH__;相同 template 引數的具現化必須在所有 device 程式與 host 程式中都存在。- 分離編譯(separate compilation)下,具外部連結的函式/變數「是否定義」不得依賴
__CUDA_ARCH__。 - 分離編譯下,header 中的 weak/template 函式不得依賴
__CUDA_ARCH__,否則不同 object 為不同架構編譯時可能在 link 時衝突(只取其中一版)。編譯器不保證會對這些誤用發出診斷。
__CUDA_ARCH_SPECIFIC__ 與 __CUDA_ARCH_FAMILY_SPECIFIC__
用來識別具「架構特定」與「家族特定」特性的 GPU,同樣只在 device code 定義,分別對應 nvcc 的 compute_<version>a 與 compute_<version>f。
| nvcc 旗標 | __CUDA_ARCH__ |
_SPECIFIC__ |
_FAMILY_SPECIFIC__ |
|---|---|---|---|
compute_100a / sm_100a |
1000 | 1000 | 1000 |
compute_100f / sm_103f |
1000 | 未定義 | 1000 |
-arch=sm_100 |
1000 | 未定義 | 未定義 |
-arch=sm_100a |
1000 | 1000 與未定義並存 | 未定義 |
-arch=sm_100a 為何 _SPECIFIC__ 會有兩種結果
-arch=sm_100a 等同 --generate-code arch=sm_100a,compute_100,compute_100a,會做多趟(multi-pass)編譯:對 compute_100a 那趟 __CUDA_ARCH_SPECIFIC__ == 1000,對 compute_100 那趟則未定義,兩種路徑並存。
CUDA Feature Testing Macros
當 CUDA 前端編譯器支援某特性時定義對應巨集:
| 巨集 | 意義 / 啟用方式 |
|---|---|
__CUDACC_DEVICE_ATOMIC_BUILTINS__ |
支援 device atomic 編譯器 builtins |
__NVCC_DIAG_PRAGMA_SUPPORT__ |
支援診斷控制 pragma |
__CUDACC_EXTENDED_LAMBDA__ |
支援 extended lambda(--expt-extended-lambda / --extended-lambda) |
__CUDACC_RELAXED_CONSTEXPR__ |
支援 relaxed constexpr(--expt-relaxed-constexpr) |
__nv_pure__ 屬性
標記 pure 函式(對參數無 side effect、可讀但不改全域變數),host/device 皆可用。編譯器將其轉成 GNU pure 屬性或 MSVC noalias 屬性。
__device__ __nv_pure__ int add(int a, int b) { return a + b; }
CUDA-Specific Functions
位址空間判斷(Address Space Predicate)
__device__ unsigned __isGlobal(const void* ptr);
__device__ unsigned __isShared(const void* ptr);
__device__ unsigned __isConstant(const void* ptr);
__device__ unsigned __isGridConstant(const void* ptr); // __grid_constant__ kernel 參數
__device__ unsigned __isLocal(const void* ptr);
若 ptr 為指向該位址空間物件的 generic 位址則回 1,否則回 0;對 NULL 指標行為未指定。
libcu++ 提供 cuda::device::is_address_from() / cuda::device::is_object_from(),可作為 __isGlobal/__isShared 等 intrinsic 的可攜且更安全替代。
位址空間轉換(Address Space Conversion)
當編譯器無法判定指標的位址空間(跨 translation unit、或與 PTX 互動)時使用:
generic → 特定(回 size_t) |
特定 → generic(回 void*) |
對應 PTX |
|---|---|---|
__cvta_generic_to_global |
__cvta_global_to_generic |
cvta.to.global / cvta.global |
__cvta_generic_to_shared |
__cvta_shared_to_generic |
cvta.to.shared / cvta.shared |
__cvta_generic_to_constant |
__cvta_constant_to_generic |
cvta.to.const / cvta.const |
__cvta_generic_to_local |
__cvta_local_to_generic |
cvta.to.local / cvta.local |
shared / local / constant 空間位址範圍小於 32-bit,可把 64-bit 位址截斷成 32-bit 整數儲存(少佔暫存器,且 32-bit 運算更快);要還原時 zero-extend 回 64-bit 再呼叫對應的 __cvta_*_to_generic。
低階載入/儲存(快取運算子)
| 函式群 | 用途 |
|---|---|
__ldg |
read-only L1/Tex 快取載入 |
__ldcg / __ldca / __ldcs / __ldlu / __ldcv |
依 PTX ISA 指定快取運算子的載入 |
__stwb / __stcg / __stcs / __stwt |
依 PTX ISA 指定快取運算子的儲存 |
皆支援所有 C++ 基本型別、CUDA 向量型別(x3 分量除外)與擴充浮點型別(__half、__half2、__nv_bfloat16、__nv_bfloat162)。
__trap()、__nanosleep()
void __trap(); // 中止 kernel、向 host 觸發中斷
__device__ void __nanosleep(unsigned nanoseconds); // 約睡 ns 奈秒,上限約 1 ms
__trap() 會毀掉 context
任何 device thread 呼叫 __trap() 會中止 kernel 執行並在 host 端觸發中斷,導致 CUDA context 損毀,後續所有 CUDA 呼叫與 kernel 啟動皆失敗。可攜替代:libcu++ cuda::std::terminate()。
// __nanosleep 經典用例:exponential back-off mutex
__device__ void mutex_lock(unsigned* mutex) {
unsigned ns = 8;
while (atomicCAS(mutex, 0, 1) == 1) {
__nanosleep(ns);
if (ns < 256) ns *= 2; // 退避時間倍增,上限 256ns
}
}
DPX(Dynamic Programming eXtension)指令
對最多三個 16-bit 或 32-bit 有/無號整數做 min/max 與 fused add-min/max,並可選 ReLU(夾到 0)。
| 類別 | 代表函式 | 語意 |
|---|---|---|
| 三路比較 | __vimax3_s32 / __vimin3_u16x2 … |
max(a,b,c) / min(a,b,c) |
| 二路 + ReLU | __vimax_s32_relu / __vimin_s16x2_relu |
max(a,b,0) / max(min(a,b),0) |
| 三路 + ReLU | __vimax3_s32_relu … |
max(a,b,c,0) / max(min(a,b,c),0) |
| 二路 + 回報誰較大/小 | __vibmax_* / __vibmin_*(bool* pred) |
回極值並把比較結果寫入 pred |
| Fused add-min/max | __viaddmax_s32 / __viaddmin_u32 … |
max(a+b,c) / min(a+b,c)(可再 +ReLU) |
__vimax3_s32_relu(-15, 8, 5); // max(-15, 8, 5, 0) = 8
__vimax3_s32_relu(-15, -2, -4); // 全負 → ReLU 後 = 0
__viaddmax_s32_relu(-5, 6, -2); // max(-5+6, -2, 0) = max(1,-2,0) = 1
__vimax3_u16x2(0x00050002, 0x00070004, 0x00020006); // 逐 16-bit → 0x00070006
DPX 依 compute capability 決定為硬體加速或軟體模擬(詳見 Arithmetic Instructions 與 CUDA Math API)。s16x2 / u16x2 後綴表示一次處理兩個 packed 的 16-bit 值。常用於 Smith-Waterman、Needleman-Wunsch(基因序列)與 Floyd-Warshall(路徑最佳化)等動態規劃演算法。
考試/測驗重點
| 題型 | 關鍵答案 |
|---|---|
__activemask() 能否判斷哪些 lane 走某分支? |
不能;只是 active lane 的瞬間快照,供 opportunistic 用 |
收斂在 __activemask() 後保證持續嗎? |
不保證,除非後續是 warp 同步 intrinsic(*_sync) |
__all_sync vs __any_sync? |
all:全部非零才回非零;any:至少一個非零就回非零 |
__ballot_sync 第 N bit 何時=1? |
第 N thread predicate 非零且 active |
__match_all_sync 何時回 0? |
mask 內非退出 thread 的值不全相同時回 0,且 *pred=false |
__match_any_sync vs __match_all_sync? |
any:回「與自己同值」的 lane 遮罩;all:全同回 mask、否則回 0 並設 *pred |
| Warp reduce 需要哪個 compute capability? | CC 8.x 以上 |
__reduce_and/or/xor_sync 接受什麼型別? |
unsigned(bitwise 歸約) |
__shfl_up_sync 來源 lane 怎麼算? |
caller - delta,不 wrap,最低 delta 個 lane 不變 |
__shfl_xor_sync 實作什麼模式? |
butterfly(蝴蝶)定址,用於 tree reduction / broadcast |
shuffle 的 width 限制? |
必為 2 的次方且 ∈ [1, 32]:1/2/4/8/16/32 |
| 讀到 inactive thread 的 shuffle 值會如何? | 未定義 |
warp *_sync 提供 memory ordering 嗎? |
不提供,也不隱含 memory barrier |
| mask 規則:呼叫/非呼叫 thread 的 bit? | 呼叫者 bit=1、非呼叫者 bit=0,退出 thread 被忽略 |
| 不同 mask 可並發呼叫嗎? | 可,只要彼此 disjoint,即使 divergent 也合法 |
| 為何 Volta+ 要帶 mask? | thread 獨立排程,mask 明示參與集合並在硬體執行前強制收斂 |
__CUDA_ARCH__ 在哪裡有定義?值怎麼算? |
只在 device code;值 = compute_<version> * 10(compute_80→800) |
compute_80,code=sm_90 下 __CUDA_ARCH__=? |
800(取 compute 的虛擬架構,非 sm_90) |
compute_100f / sm_103f 下 _SPECIFIC__ 與 _FAMILY_SPECIFIC__? |
__CUDA_ARCH_SPECIFIC__ 未定義;__CUDA_ARCH_FAMILY_SPECIFIC__ = 1000 |
__isShared 回傳什麼? |
ptr 指向 shared 物件的 generic 位址回 1,否則 0 |
__cvta_generic_to_shared 回傳型別? |
size_t(特定空間位址),對應 PTX cvta.to.shared |
__ldg 走哪條快取? |
read-only L1/Tex |
__trap() 的副作用? |
中止 kernel、觸發 host 中斷、損毀 CUDA context |
__nanosleep 最大睡眠時間? |
約 1 毫秒 |
| DPX 的 ReLU 後綴語意? | 對結果夾到 0,即 max(..., 0) |
| DPX 典型應用? | Smith-Waterman、Needleman-Wunsch、Floyd-Warshall 等動態規劃 |
Related Notes
- 05-Technical-Appendices/07-Synchronization-and-Atomic-Functions
- 05-Technical-Appendices/09-Compiler-Hints-and-Warp-Matrix
- 01-Introduction-to-CUDA/02-Execution-Model-and-SIMT
- 03-Advanced-CUDA/04-Using-PTX-and-Hardware-Model
- 05-Technical-Appendices/11-Math-Functions-and-Intrinsics
- 05-Technical-Appendices/Practice-Technical-Appendices
- 00-Dashboard/Exam-Traps