執行緒束(warp)
在 GPU 上,執行緒不是真的一個一個跑——它們以小排為單位一起行進,全部踩同一個節拍。一個 warp 就是這樣一個小排:一組固定大小的執行緒(NVIDIA GPU 上是 32 個;AMD 對應的 64 個稱為 wavefront),硬體把它當作單一單位來排程與執行。想像一艘八人賽艇,每個人必須在同一槳上出力:一個 warp 是一隊一起提取與執行同一條指令的執行緒,各自拉自己那一片資料。
為什麼要把執行緒捆起來?因為共用讓硬體便宜。一個 warp 的 32 個執行緒共用一個指令提取與一個解碼單元;每週期提取一條指令,全部 32 條車道用各自的暫存器執行它。這正是 SIMT 機器如何把舒適的執行緒程式模型在底下變成有效率的 SIMD 執行。warp 也是排程的單位:當一個 warp 因等待緩慢的記憶體而停頓時,硬體立刻換上另一個就緒的 warp,好讓算術單元保持忙碌——GPU 靠保持許多 warp 在飛行中、而非靠大快取來隱藏記憶體延遲,這就是為什麼 GPU 想要有數千個執行緒可用,而不只是少數幾個。
對初學者有兩個誠實的後果。第一,因為一個 warp 共用一條指令流,一個把 warp 拆開的分支(有些執行緒為真、有些為假)會迫使硬體一前一後跑兩邊、每次有一半執行緒閒置——分支發散,而它在一個 warp 內最痛。第二,warp 大小是個你該尊重的真實固定數字:啟動 33 個執行緒仍佔兩個 warp(一個滿、一個只有單一活躍執行緒與 31 條浪費的車道),所以好的 GPU 程式把工作組織成 warp 大小的倍數。warp 是「友善的執行緒抽象」與「無情的 SIMD 硬體」相接的縫。
在 NVIDIA GPU 上以 100 個執行緒啟動一個核心程式:硬體形成四個 warp——三個各 32 個的滿 warp(96 個執行緒),外加第四個裝著剩下 4 個活躍執行緒與 28 條閒置車道的 warp。那 28 條浪費的車道,就是為什麼程式設計師把執行緒數無條件進位到 32 的倍數。
warp 是 GPU 真正的執行單位:一捆固定的執行緒,一起提取與執行一條指令。
warp 裡的執行緒不是各跑各的。它們共用一條指令流,所以只有在意見一致時才快;半滿的 warp 浪費車道,而發散的分支會把 warp 序列化。把執行緒數規劃成 warp 大小的倍數。