1. 程式人生 > >CUDA基礎的基礎教程:初探原子操作和CUDA流

CUDA基礎的基礎教程:初探原子操作和CUDA流

1.原子操作

熟悉作業系統的讀者對與原子操作不會陌生,原子操作代表不可被分割的操作,也就是最小的計算機可執行程式的單元。在CUDA中,也有這樣的原子操作,我們使用原子操作之後,會鎖定空間,防止其他的程序訪問。由於CUDA的超多執行緒並行運算的特性,我們可以利用CUDA中的原子操作來優化我們的程式。

1.1 CUDA原子操作

CUDA可供使用的原子操作有很多,我們只介紹最基本的一個atomicAdd,這個函式接受兩個同類型的引數,並將第二個值加到第一個值上。我們舉一個簡單的直方圖統計的例子來說明在什麼情況下使用原子操作。

1.2 直方圖統計GPU版:

我們現在有一個很長的字串,我們要統每個字元的出現次數。如果利用CPU進行計算,我們會在很長的時間來遍歷一遍字串。利用CUDA,我們可以將整個任分配給多個程序塊,或者我們可以啟動一個256個程序組成的程序塊。總之,我們發現程序塊的數量是CUDA單元的2倍時,效率是最高的。我們之後可以利用原子操作來執行對應的任務。 但是我們開率這樣一個問題,當有很多個原子操作時,記憶體是有限的,當有很多個原子操作試圖訪問比較小的全域性記憶體的時候,反而會因為訊號競爭產生大量的堵塞,這個時候我們需要用上回講到的共享記憶體了。 我們首先分配同樣大小的共享記憶體,然後先講這個程序處理的值暫存在共享記憶體。之後我們在把他們合併在一起。同樣的,我們要在全部的初步統計結束之後,讓所有的執行緒同步。當然,同步的過程同樣使用原子操作。 使用原子操作的確會加快速度,有的時候增加更多的原子操作未必是件壞事。

2.CUDA流:

CUDA的效率十分驚人,但是我們之前介紹只是在同樣的資料上進行著相同的任務,使用了CUDA流之後,我們就可以在GPU處理器上同時完成更多的任務了。

2.1 頁鎖定記憶體:

其實就是固定的記憶體,要知道這個不是GOU上的記憶體,而是主機分配的記憶體。在主機上的記憶體可能會被自動分頁或者移動到了硬碟上,所以我們固定了記憶體,相當於固定了地址,GPU在知道地址的情況下,相當於可以直接訪問(DMA),這樣就不會被匯流排限制速度了

為了使用固定記憶體,我們需要新的函式cudaHostAlloc用法和cudaMalloc是一樣的

2.2 什麼是CUDA流:

CUDA流是什麼呢?我想到輸出輸入流,但是這個和CUDA中的流是有區別的,CUDA中的流相當於是一個操作佇列,當然這個操作佇列可能就是一個程序上的任務,通過使用多個流,我們就可以更高效的完成計算任務。

使用流就不得不使用之前說過的固定記憶體,因為涉及到大量的記憶體交換,我們需要更高效的辦法,由於固定記憶體上的交換是獨立的,我們可以同時交換記憶體和執行核函式。

比如我們可以讓兩個流做同步,假設我們需要講A+B的結果存到C中,我們就可以趁第一個流執行核函式的時候,第二個流將資料複製進GPU,以此類推 在這裡插入圖片描述

偷了個圖,大致的流程就是這個樣子。 同時,複製記憶體就不能使用原來的方法了,CUDA中有cudaMemecpyAsync,這是一種非同步的方法,也就是說我們不知道他什麼之後會被執行,但是我們我們把他塞入流中,就知道這個程式碼在執行之後的操作就一定執行完畢了。好比我們不知道什麼時候會執行復制或者執行核函式,先排一個這樣的複製操作進去,等到另一個佇列執行核函式的時候,複製引擎空下來就可以執行啦。 總之合理的使用流進行資料交換和運算是一個很好的方法,具體的程式碼請參考《CUDA By Example》中的第10章的程式碼。

總結:

我們為了儘可能的讓任務並行執行,採用了流的方法、當然我們要注意要使用固定記憶體,並且非同步的執行記憶體複製操作。同時我們要理解硬體執行順序之後在進行設計。