前言
在用 CUDA 對 GPU 進行並行編程的過程中,除了需要對線程架構要有深刻的認識外,也需要對存儲系統架構有深入的了解。
這兩個部分是 GPU 編程中最為基礎,也是最為重要的部分,需要花時間去理解吸收,加深內功。
了解 GPU 存儲系統架構的意義
CUDA 編程架構的設計思路本身也就是讓程序員去使用緩存,而不是讓緩存像 CPU 編程結構那樣對程序員透明。
通過對所使用存儲結構的優化,能夠讓程序的並行后的效果得到很大提高。
因此,這個問題是需要我們在開發全程中考慮的。
第一層:寄存器
每個流處理器中的寄存器數以千計,每個線程都能分配到其私有的寄存器,這樣做的好處是使得線程的切換幾乎是零開銷 (也許說是線程束的切換會更為准確)。
應當在硬件條件允許的情況下,盡可能地使用寄存器 (注意是硬件條件的允許之下)。
在核函數中定義的變量就是寄存器變量。
第二層:共享內存
共享內存的本質是可受用戶控制的一級緩存。每個 SM 中的一級緩存與共享內存共享一個 64 KB的內存段。在費米架構中,可以為每個塊定義 16 KB的共享內存。靈活地使用共享內存,能夠大幅度提高顯存的帶寬。此外,共享內存也是實現塊內線程間通信的有效工具。
使用時需要注意的一個地方是,只有在確定需要重復利用此空間的數據,或者明確要使塊內線程進行通信的前提下,才考慮使用共享內存。(原因不解釋)
使用時需要注意的另一個地方是應當盡可能地避免存儲體沖突。這里所謂的存儲體是指實現共享內存的硬件 - 一個費米架構的設備上有 32 個存儲體。解決此問題的關鍵在於:順序訪問存儲體。
實際開發中,常常將一個任務分解成多個部分(不論是任務分解還是數據分解),共享內存在其中扮演着任務塊工作任務匯總或者數據塊工作任務匯總的角色。
核函數中定義的變量加上__shared__聲明后就會存放在共享內存中了。
第三層:常量內存
常量內存其實只是全局內存的一種虛擬地址形式,並沒有特殊保留的常量內存塊。
使用起來非常方便,在主機端對需要放到常量內存區的變量添加 __constant__ 關鍵字聲明之即可。
唯獨需要注意的是,如果一個常量僅僅是一個字面值,那么將它聲明為宏也行,例如 PI 這樣的常數就一般定義為宏。
第四層:全局內存
全局內存,也就是顯存。
在主機端開辟的顯存空間均屬於全局內存范疇。
使用全局內存的時候,需要注意的是應當學會對顯存采取合並的訪問方式。何謂合並的訪問方式呢?請參閱下篇文章。
