CUDA C++ 記憶體模型 (CUDA C++ Memory Model)
重點總覽
標準 C++ 假設執行緒同步的成本是均勻且低的;CUDA C++ 不同:執行緒距離越遠,同步成本越高——block 內極低,跨多 GPU/CPU 的系統範圍則很高。為了表達這種非均勻成本,CUDA C++ 在 cuda:: namespace 中以 thread scope 擴充標準 C++ 記憶體模型與並行設施,預設仍保留標準 C++ 的語法與語意。
| 項目 | 重點 |
|---|---|
| 核心擴充 | cuda:: 在標準模型上加入 thread scope |
| Scope 列舉 | thread_scope_system / _device / _block / _thread 四級 |
| 同步原語 | cuda::atomic、cuda::barrier 等,以 scope 為模板參數 |
| Atomicity 關鍵 | 操作只在它指定的 scope 內保證 atomic |
| Data race | 兩個衝突存取,至少一個在「涵蓋對方執行緒的 scope」下不是 atomic,且無 happens-before → UB |
std::/cuda::std:: |
等同於 cuda:: 型別以 thread_scope_system 實例化的行為 |
Thread Scopes
thread scope 指定「哪一類執行緒」可以透過某個同步原語(如 cuda::atomic、cuda::barrier)彼此同步。
namespace cuda {
enum thread_scope {
thread_scope_system,
thread_scope_device,
thread_scope_block,
thread_scope_thread
};
} // namespace cuda
Scope Relationships(涵蓋範圍)
每個程式執行緒透過一或多個 scope 關係彼此關聯,由大到小(外層涵蓋內層):
| Scope | 關聯的執行緒範圍 |
|---|---|
cuda::thread_scope_system |
系統中的每個執行緒,與系統中其他每個執行緒(含多 GPU/CPU) |
cuda::thread_scope_device |
同一 CUDA device 且同一 memory synchronization domain 內的每個 GPU 執行緒 |
cuda::thread_scope_block |
同一 CUDA thread block 內的每個 GPU 執行緒 |
cuda::thread_scope_thread |
每個執行緒只與自己關聯 |
system ┌──────────────────────────────────────────┐ 多 GPU + CPU,成本最高
│ device ┌───────────────────────────────┐ │ 單一 device(同 sync domain)
│ │ block ┌──────────────────────┐ │ │ 單一 thread block,成本最低
│ │ │ thread (僅自身) │ │ │
│ │ └──────────────────────┘ │ │
│ └───────────────────────────────┘ │
└──────────────────────────────────────────┘
thread_scope_device 的涵蓋範圍除了「同一 device」外,還限定在同一 memory synchronization domain 內;跨 domain 不在此 scope 的保證範圍。
Synchronization primitives
std:: 與 cuda::std:: 中的型別,當以 cuda::thread_scope_system 實例化對應的 cuda:: 型別時,行為相同。
- 換言之,標準 C++ 的同步型別(如
std::atomic)等同於最大範圍(system scope)的cuda::版本。 - 要取得 block/device 等較窄、成本較低的 scope,需改用
cuda::atomic、cuda::atomic_ref、cuda::barrier等並以 scope 作為模板參數。
Atomicity
一個 atomic 操作只在它所指定的 scope 下保證為 atomic,條件為以下之一:
- 它指定的 scope 不是
cuda::thread_scope_system;或 - scope 是
cuda::thread_scope_system且滿足下列任一:
| 條件 | 需滿足的裝置屬性 |
|---|---|
| 影響 system allocated memory 的物件 | pageableMemoryAccess 為 1(見 [0]) |
| 影響 managed memory 的物件 | concurrentManagedAccess 為 1 |
| 影響 mapped memory 的物件 | hostNativeAtomicSupported 為 1 |
| 對 mapped memory 上自然對齊、大小 1/2/4/8/16 bytes 物件的 load 或 store | 直接成立(見 [1]) |
| 影響 GPU memory 物件且只有 GPU 執行緒存取 | 見下方兩個子條件 |
GPU memory 且僅 GPU 執行緒存取時,需再滿足任一:
- 對每個存取的
srcDev與物件所在 GPUdstDev,cudaDeviceGetP2PAttribute(&val, cudaDevP2PAttrNativeAtomicSupported, srcDev, dstDev)為 1;或 - 只有來自單一 GPU 的 GPU 執行緒並行存取它。
- [0] 若
PageableMemoryAccessUsesHostPagetables為 0,則對 memory mapped file 或 hugetlbfs 配置做的 atomic 操作不是 atomic。 - [1] 若
hostNativeAtomicSupported為 0,system scope 下對 system allocated memory 或 mapped memory 中自然對齊 16-byte 物件的 atomic load/store 需要系統支援。NVIDIA 不知道有任何系統缺乏此支援,且沒有可偵測的 CUDA API。
Data Races
CUDA C++ 對 ISO/IEC 14882(C++ 標準)intro.races 第 21 段做如下修改(加入 scope 概念):
程式執行若包含兩個可能並行的衝突動作,其中至少一個在「涵蓋了執行另一操作之執行緒」的 scope 下不是 atomic,且兩者之間沒有 happens-before 關係(signal handler 特例除外),即構成 data race。任何此類 data race 都導致未定義行為(UB)。
- 關鍵:是否 atomic 要看「該操作指定的 scope 是否涵蓋對方執行緒」,而非單看操作本身是否為 atomic 指令。
barrier、latch、counting_semaphore的成員函式(解構子除外)並行呼叫不會引入 data race,如同它們是 atomic 操作。stop_token的request_stop/stop_requested/stop_possible這三個特定函式的呼叫也不會引入 data race(來源僅針對這三個函式,且未含「解構子除外」的限定)。- Fence 同步(
atomics.fences)也加上 scope 約束,分三種 synchronizes-with 配對:- release fence A 與 acquire fence B:存在 atomic operations X、Y,**四個操作(A、B、X、Y)**各自都必須指定涵蓋其他操作之執行緒的 scope。
- release fence A 與執行 acquire 的 atomic operation B:只牽涉 (A、B、X) 三個操作,各自須指定涵蓋對方執行緒的 scope。
- release operation A 與 acquire fence B:同樣只牽涉 (A、B、X) 三個操作,各自須指定涵蓋對方執行緒的 scope。
Example: Message Passing
Block 0 的執行緒把訊息寫進 x,透過旗標 f 傳給 Block 1 的執行緒。正確版:兩端都用 thread_scope_device(涵蓋跨 block 的兩個執行緒)。
// 初始
int x = 0, f = 0;
// Thread 0, Block 0
x = 42;
cuda::atomic_ref<int, cuda::thread_scope_device> flag(f);
flag.store(1, memory_order_release);
// Thread 0, Block 1
cuda::atomic_ref<int, cuda::thread_scope_device> flag(f);
while (flag.load(memory_order_acquire) != 1);
assert(x == 42); // 必成立:release/acquire 在 device scope 下成對同步
錯誤版(data race / UB):store 端誤用 thread_scope_block。
// Thread 0, Block 0
x = 42;
cuda::atomic_ref<int, cuda::thread_scope_block> flag(f);
flag.store(1, memory_order_release); // UB: data race
// Thread 0, Block 1
cuda::atomic_ref<int, cuda::thread_scope_device> flag(f);
while (flag.load(memory_order_acquire) != 1); // UB: data race
assert(x == 42);
雖然對 f 的 store 與 load 本身都是 atomic 操作,但 store 的 scope 是 block scope——它由 Block 0 的 Thread 0 執行,只涵蓋 Block 0 內的其他執行緒。做 load 的執行緒在 Block 1,不在 store 操作所涵蓋的 scope 內,於是 store 與 load 對彼此而言不是 atomic,構成 data race。更多範例見 PTX memory consistency model litmus tests。
考試/測驗重點
| 題型 | 關鍵答案 |
|---|---|
| CUDA C++ 為何擴充標準記憶體模型 | 同步成本非均勻:距離越遠成本越高,需用 thread scope 表達 |
| 四個 thread scope 由大到小 | system → device → block → thread |
| scope 擴充放在哪個 namespace | cuda::(預設保留標準 C++ 語法與語意) |
thread_scope_device 的範圍 |
同一 device 且同一 memory synchronization domain 的 GPU 執行緒 |
thread_scope_thread 涵蓋誰 |
只涵蓋執行緒自己 |
std::/cuda::std:: 型別等同哪種 scope |
等同 cuda:: 型別以 thread_scope_system 實例化 |
| atomic 操作何時必為 atomic | 指定的 scope 不是 system scope 時,自動成立 |
| system scope + system allocated memory 需要 | pageableMemoryAccess 為 1 |
| system scope + managed memory 需要 | concurrentManagedAccess 為 1 |
| system scope + mapped memory 需要 | hostNativeAtomicSupported 為 1 |
| mapped memory 上哪些 load/store 直接 atomic | 自然對齊、大小 1/2/4/8/16 bytes 的物件 |
| GPU memory 跨 GPU atomic 的查詢 API | cudaDeviceGetP2PAttribute 配 cudaDevP2PAttrNativeAtomicSupported 為 1 |
| 單一 GPU 內的 GPU 執行緒並行存取 | 對 GPU memory 物件保證 atomic |
| data race 的定義(含 scope) | 兩並行衝突動作,至少一個在涵蓋對方執行緒的 scope 下非 atomic,且無 happens-before |
| data race 的後果 | 未定義行為(UB) |
| barrier/latch/semaphore 成員函式並行呼叫 | 除解構子外不引入 data race(視為 atomic) |
| fence 同步是否受 scope 約束 | 是,A/B/X(/Y) 各操作都須指定涵蓋對方執行緒的 scope |
| Message Passing 範例為何 UB | store 用 block scope,load 執行緒在別的 block,scope 不涵蓋對方 |
| 正確的 Message Passing 用哪個 scope | 兩端都用 thread_scope_device |
PageableMemoryAccessUsesHostPagetables 為 0 的影響 |
對 memory mapped file / hugetlbfs 的 atomic 不再是 atomic |
Related Notes
- 05-Technical-Appendices/15-CUDA-Cpp-Execution-Model
- 05-Technical-Appendices/07-Synchronization-and-Atomic-Functions
- 03-Advanced-CUDA/05-Thread-Scopes-and-Scoped-Atomics
- 02-Programming-GPUs/09-SIMT-Atomics-Cooperative-Occupancy
- 05-Technical-Appendices/Practice-Technical-Appendices
- 00-Dashboard/Exam-Traps