[英]Synchronising multiple Cuda streams
對於我目前正在開發的應用程序,我希望有一個長內核(即,相對於其他內核需要很長時間才能完成的內核)與一系列同時運行的較短內核同時執行。 然而,更復雜的是,四個較短的內核在完成后需要進行同步,以便執行另一個短內核,該內核收集並處理其他短內核的數據輸出。
以下是我的想法的示意圖,編號的綠色條代表不同的內核:
為了實現這一點,我編寫了類似於以下內容的代碼:
// definitions of kernels 1-6
class Calc
{
Calc()
{
// ...
cudaStream_t stream[5];
for(int i=0; i<5; i++) cudaStreamCreate(&stream[i]);
// ...
}
~Calc()
{
// ...
for(int i=0; i<5; i++) cudaStreamDestroy(stream[i]);
// ...
}
void compute()
{
kernel1<<<32, 32, 0, stream[0]>>>(...);
for(int i=0; i<20; i++) // this 20 is a constant throughout the program
{
kernel2<<<1, 32, 0, stream[1]>>>(...);
kernel3<<<1, 32, 0, stream[2]>>>(...);
kernel4<<<1, 32, 0, stream[3]>>>(...);
kernel5<<<1, 32, 0, stream[4]>>>(...);
// ?? synchronisation ??
kernel6<<<1, 32, 0, stream[1]>>>(...);
}
}
}
int main()
{
// preparation
Calc C;
// run compute-heavy function as many times as needed
for(int i=0; i<100; i++)
{
C.compute();
}
// ...
return 0;
}
注意:塊,線程和共享內存的數量只是任意數。
現在,我將如何在每次迭代中正確同步內核2-5? 首先,我不知道哪個內核需要花費最長的時間才能完成,因為這可能取決於用戶輸入。 此外,我已經嘗試過使用cudaDeviceSynchronize()
和cudaStreamSynchronize()
,但是那些超過總執行時間的三倍。
Cuda事件可能是要走的路嗎? 如果是這樣,我該如何應用它們? 如果沒有,那么這樣做的正確方法是什么?
非常感謝你。
首先需要提出兩條評論。
啟動小內核(一個塊)通常不是從GPU中獲得良好性能的方法。 同樣,每個塊具有少量線程的內核(32)通常會施加占用限制,這將阻止GPU的完全性能。 啟動多個並發內核並不能減輕這種第二個考慮因素。 我不會在這里花費更多的時間,因為你已經說過這些數字是隨意的(但請看下面的下一條評論)。
目睹實際的內核並發很難。 我們需要內核具有相對較長的執行時間,但對GPU資源的需求相對較低。 <<<32,32>>>
內核可能會填充您正在運行的GPU,從而阻止並發內核的塊運行能力。
您的問題似乎歸結為“如何在kernel2-5
完成之前阻止kernel6
啟動。
可以為此使用事件。 基本上,你會記錄一個事件到每個流的kernel2-5啟動后,你會放一個cudaStreamWaitEvent
電話,一個對4個事件, 之前推出的kernel6
。
像這樣:
kernel2<<<1, 32, 0, stream[1]>>>(...);
cudaEventRecord(event1, stream[1]);
kernel3<<<1, 32, 0, stream[2]>>>(...);
cudaEventRecord(event2, stream[2]);
kernel4<<<1, 32, 0, stream[3]>>>(...);
cudaEventRecord(event3, stream[3]);
kernel5<<<1, 32, 0, stream[4]>>>(...);
cudaEventRecord(event4, stream[4]);
// ?? synchronisation ??
cudaStreamWaitEvent(stream[1], event1);
cudaStreamWaitEvent(stream[1], event2);
cudaStreamWaitEvent(stream[1], event3);
cudaStreamWaitEvent(stream[1], event4);
kernel6<<<1, 32, 0, stream[1]>>>(...);
請注意,以上所有調用都是異步的 。 它們都不需要花費超過幾微秒來處理,並且它們都不會阻止CPU線程繼續運行,這與使用cudaDeviceSynchronize()
或cudaStreamSynchronize()
,后者通常會阻塞CPU線程。
因此,您可能需要在上述序列之后進行某種同步(例如cudaStreamSynchronize(stream[1]);
)在循環中執行,否則所有這些的異步性質將會變得毛茸茸( ,基於你的原理圖,似乎你可能不希望迭代i + 1的kernel2-5開始直到迭代i的內核6完成?)注意我已經省略了事件創建和其他可能的樣板,我假設您可以解決這個問題或參考任何使用事件的示例代碼,或參考文檔。
即使你實現了所有這些基礎設施,你見證(或不見)實際內核並發的能力將由你的內核本身決定, 而不是我在這個答案中建議的任何東西。 所以,如果你回來說“我做了那個,但我的內核並沒有同時運行”這實際上是一個與你所提出的問題不同的問題,在這里,我會把你的初學者推薦給我的評論#2。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.