CUDA Stream流并发性
目錄
1、CUDA 中的異步命令
2、默認流
3、Multistream多流示例
異構(gòu)計算是指高效地使用系統(tǒng)中的所有處理器,包括 CPU 和 GPU 。為此,應用程序必須在多個處理器上并發(fā)執(zhí)行函數(shù)。 CUDA 應用程序通過在 streams 中執(zhí)行異步命令來管理并發(fā)性,這些命令是按順序執(zhí)行的。不同的流可以并發(fā)地執(zhí)行它們的命令,也可以彼此無序地執(zhí)行它們的命令。在不指定流的情況下執(zhí)行異步 CUDA 命令時,運行時使用默認流。在 CUDA 7 之前,默認流是一個特殊流,它隱式地與設備上的所有其他流同步。CUDA7引入了大量強大的新功能,包括一個新的選項,可以為每個主機線程使用獨立的默認流,這避免了傳統(tǒng)默認流的序列化。本文將展示如何在 CUDA 程序中簡化實現(xiàn)內(nèi)核和數(shù)據(jù)副本之間的并發(fā)。
1、CUDA 中的異步命令
如 CUDA C 編程指南所述,異步命令在設備完成請求的任務之前將控制權(quán)返回給調(diào)用主機線程(它們是非阻塞的)。這些命令是:
- 內(nèi)核啟動;
- 存儲器在兩個地址之間復制到同一設備存儲器;
- 從主機到設備的 64kb 或更少內(nèi)存塊的內(nèi)存拷貝;
- 由后綴為?Async?的函數(shù)執(zhí)行的內(nèi)存復制;
- 內(nèi)存設置函數(shù)調(diào)用。
為內(nèi)核啟動或主機設備內(nèi)存復制指定流是可選的;可以調(diào)用 CUDA 命令而不指定流(或通過將 stream 參數(shù)設置為零)。下面兩行代碼都在默認流上啟動內(nèi)核。
kernel<<< blocks, threads, bytes >>>(); // default streamkernel<<< blocks, threads, bytes, 0 >>>(); // stream 02、默認流
在并發(fā)性對性能不重要的情況下,默認流很有用。在 CUDA 7 之前,每個設備都有一個用于所有主機線程的默認流,這會導致隱式同步。正如 CUDA C 編程指南中的“隱式同步”一節(jié)所述,如果主機線程向它們之間的默認流發(fā)出任何 CUDA 命令,來自不同流的兩個命令就不能并發(fā)運行。CUDA 7 引入了一個新選項,?每線程默認流?,它有兩個效果。首先,它為每個主機線程提供自己的默認流。這意味著不同主機線程向默認流發(fā)出的命令可以并發(fā)運行。其次,這些默認流是常規(guī)流。這意味著默認流中的命令可以與非默認流中的命令同時運行。要在?nvcc?7 及更高版本中啟用每線程默認流,可以在包含 CUDA 頭(?cuda.h?或?cuda_runtime.h?)之前,使用?nvcc?命令行選項 CUDA 或?#define?編譯?CUDA_API_PER_THREAD_DEFAULT_STREAM?預處理器宏。需要注意的是:當代碼由?nvcc?編譯時,不能使用?#define CUDA_API_PER_THREAD_DEFAULT_STREAM?在. cu 文件中啟用此行為,因為?nvcc?在翻譯單元的頂部隱式包含了?cuda_runtime.h?。
3、Multistream多流示例
看一個小例子。下面的代碼簡單地在八個流上啟動一個簡單內(nèi)核的八個副本。只為每個網(wǎng)格啟動一個線程塊,這樣就有足夠的資源同時運行多個線程塊。作為遺留默認流如何導致序列化的示例,在默認流上添加不起作用的虛擬內(nèi)核啟動
const int N = 1 << 20;__global__ void kernel(float *x, int n) {int tid = threadIdx.x + blockIdx.x * blockDim.x;for (int i = tid; i < n; i += blockDim.x * gridDim.x) {x[i] = sqrt(pow(3.14159,i));} }int main() {const int num_streams = 8;cudaStream_t streams[num_streams];float *data[num_streams];for (int i = 0; i < num_streams; i++) {cudaStreamCreate(&streams[i]);cudaMalloc(&data[i], N * sizeof(float));// launch one worker kernel per streamkernel<<<1, 64, 0, streams[i]>>>(data[i], N);// launch a dummy kernel on the default streamkernel<<<1, 1>>>(0, 0);}cudaDeviceReset();return 0; }首先讓檢查遺留行為,通過不帶選項的編譯:
nvcc ./stream_test.cu -o stream_legacy可以在 NVIDIA visualprofiler (?nvvp?)中運行該程序,以獲得顯示所有流和內(nèi)核啟動的時間軸。圖 1 顯示了 Macbook Pro 上生成的內(nèi)核時間線,該 Macbook Pro 帶有 NVIDIA GeForce GT 750M (一臺開普勒 GPU )。可以看到默認流上虛擬內(nèi)核的非常小的條,以及它們?nèi)绾螌е滤衅渌餍蛄谢?/span>
一個簡單的多流示例在將任何交錯內(nèi)核發(fā)送到默認流時不會實現(xiàn)并發(fā)
現(xiàn)在嘗試新的每線程默認流。
nvcc --default-stream per-thread ./stream_test.cu -o stream_per-thread圖 2 顯示了來自?nvvp?的結(jié)果。在這里可以看到九個流之間的完全并發(fā):默認流(在本例中映射到流 14 )和創(chuàng)建的其他八個流。請注意,虛擬內(nèi)核運行得如此之快,以至于很難看到在這個圖像中默認流上有八個調(diào)用。
圖 2 :使用新的每線程默認流選項的多流示例,它支持完全并發(fā)執(zhí)行
在為并發(fā)進行編程時,還需要記住以下幾點。
- 記住:對于每線程的默認流,每個線程中的默認流的行為與常規(guī)流相同,只要同步和并發(fā)就可以了。對于傳統(tǒng)的默認流,這是不正確的。
- --default-stream?選項是按編譯單元應用的,確保將其應用于所有需要它的?nvcc?命令行。
- cudaDeviceSynchronize()?繼續(xù)同步設備上的所有內(nèi)容,甚至使用新的每線程默認流選項。如果只想同步單個流,請使用?cudaStreamSynchronize(cudaStream_t stream)?,如的第二個示例所示。
- 從 CUDA 7 開始,還可以使用句柄?cudaStreamPerThread?顯式地訪問每線程的默認流,也可以使用句柄?cudaStreamLegacy?訪問舊的默認流。請注意,?cudaStreamLegacy?仍然隱式地與每個線程的默認流同步,如果碰巧在一個程序中混合使用它們。
- 可以通過將?cudaStreamCreate()?標志傳遞給?cudaStreamCreate()?來創(chuàng)建不與傳統(tǒng)默認流同步的非堵塞流?。
參考文獻:
CUDA 7 Stream流簡化并發(fā)性 - 吳建明wujianming - 博客園
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的CUDA Stream流并发性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows脚本编制引擎_说说 Win
- 下一篇: wxpython是干嘛的_你都用 Pyt