本節書摘來自華章計算機《cuda c程式設計權威指南》一書中的第2章,第2.3節,作者 [美] 馬克斯·格羅斯曼(max grossman),譯 顔成鋼 殷建 李亮,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。
從前面的例子可以看出,如果使用了合适的網格和塊大小來正确地組織線程,那麼可以對核心性能産生很大的影響。在向量加法的例子中,為了實作最佳性能我們調整了塊的大小,并基于塊大小和向量資料大小計算出了網格大小。
現在通過一個矩陣加法的例子來進一步說明這一點。對于矩陣運算,傳統的方法是在核心中使用一個包含二維網格與二維塊的布局來組織線程。但是,這種傳統的方法無法獲得最佳性能。在矩陣加法中使用以下布局将有助于了解更多關于網格和塊的啟發性的用法:
由二維線程塊構成的二維網格
由一維線程塊構成的一維網格
由一維線程塊構成的二維網格
通常情況下,一個矩陣用行優先的方法在全局記憶體中進行線性存儲。圖2-9所示的是一個8×6矩陣的小例子。
在一個矩陣加法核函數中,一個線程通常被配置設定一個資料元素來處理。首先要完成的任務是使用塊和線程索引從全局記憶體中通路指定的資料。通常情況下,對一個二維示例來說,需要管理3種索引:

線程和塊索引
矩陣中給定點的坐标
全局線性記憶體中的偏移量
對于一個給定的線程,首先可以通過把線程和塊索引映射到矩陣坐标上來擷取線程塊和線程索引的全局記憶體偏移量,然後将這些矩陣坐标映射到全局記憶體的存儲單元中。
第一步,可以用以下公式把線程和塊索引映射到矩陣坐标上:
第二步,可以用以下公式把矩陣坐标映射到全局記憶體中的索引/存儲單元上:
圖2-10說明了塊和線程索引、矩陣坐标以及線性全局記憶體索引之間的對應關系。
printthreadinfo函數被用于輸出關于每個線程的以下資訊:
線程索引
塊索引
矩陣坐标
線性全局記憶體偏移量
相應元素的值
用以下指令編譯并運作該程式:
對于每個線程,你可以擷取以下資訊:
圖2-11說明了這三項索引之間的關系。
在本節中,我們将使用一個二維網格和二維塊來編寫一個矩陣加法核函數。首先,應編寫一個校驗主函數以驗證矩陣加法核函數是否能得出正确的結果:
然後,建立一個新的核函數,目的是采用一個二維線程塊來進行矩陣求和:
這個核函數的關鍵步驟是将每個線程從它的線程索引映射到全局線性記憶體索引中,如圖2-12所示。
接下來,每個次元下的矩陣大小可以按如下方法設定為16 384個元素:
然後,使用一個二維網格和二維塊按如下方法設定核函數的執行配置:
把所有的代碼整合到名為summatrixongpu-2d-grid-2d-block.cu的檔案中。主函數代碼如代碼清單2-7所示。
用以下指令編譯并運作該代碼:
在tesla m2070上運作的結果:
接下來,調整塊的尺寸為32×16并重新編譯和運作該代碼。核函數的執行速度幾乎快了兩倍:
你可能好奇為什麼隻是改變了執行配置,核心性能就幾乎翻了一倍。直覺地說,你可能會覺得這是因為第二次配置的線程塊數是第一次配置塊數的兩倍,是以并行性也是兩倍。你的直覺是正确的,但是,如果進一步減小塊的大小變為16×16,相比第一次配置你已經将塊的數量翻了四倍。如下所示,這種配置的結果比第一個好但是不如第二個。
表2-3總結了不同執行配置的性能。結果顯示,增加塊的數量不一定能提升核心性能。在第3章中,你将會學習到為什麼不同的執行配置會影響核函數的性能。
為了使用一維網格和一維塊,你需要寫一個新的核函數,其中每個線程處理ny個資料元素,如圖2-13所示。
由于在新的核函數中每個線程都要處理ny個元素,與使用二維網格和二維塊的矩陣求和的核函數相比,從線程和塊索引到全局線性記憶體索引的映射都将會有很大不同。由于在這個核函數啟動中使用了一個一維塊布局,是以隻有threadidx.x是有用的,并且使用核心中的一個循環來處理每個線程中的ny個元素。
一維網格和塊的配置如下:
使用以下配置調用核函數:
使用一維網格和一維塊的更改替換代碼清單2-7中的部分,并儲存到檔案summatrix-ongpu-1d-grid-1d-block.cu中,使用以下指令編譯并運作該程式:
結果顯示,與使用一個二維網格和塊(32×32)的配置結果相比,兩者的性能基本相同。
接下來,按如下所示的方法增加塊的大小:
重新編譯并運作,可以看出核函數運作得更快了。
當使用一個包含一維塊的二維網格時,每個線程都隻關注一個資料元素并且網格的第二個維數等于ny,如圖2-14所示。
這可以看作是含有一個二維塊的二維網格的特殊情況,其中塊的第二個維數是1。是以,從塊和線程索引到矩陣坐标的映射就變成:
從矩陣坐标到全局線性記憶體偏移量的映射保持不變。新的核函數如下:
注意,二維核函數summatrixongpu2d也為這個執行配置工作。編寫新核心的唯一優點是每個線程省去了一次整數乘法和一次整數加法的運算。
将塊尺寸設定為32,并在此基礎上計算網格大小:
如下所示調用核心:
對代碼清單2-7進行更改替換,并将替換後的程式儲存到名為summatrixongpu-2d-grid-1d-block.cu的檔案中,然後使用以下指令編譯并運作。
運作結果為:
如下所示,将線程塊的大小增加到256:
然後重新編譯運作,系統會表現出目前為止最佳的性能(見表2-4):
從矩陣加法的例子中可以看出:
改變執行配置對核心性能有影響
傳統的核函數實作一般不能獲得最佳性能
對于一個給定的核函數,嘗試使用不同的網格和線程塊大小可以獲得更好的性能
在第3章,将會從硬體的角度學習産生這些問題的原因。