2016-11-12 80 views
0

我目前正在通過計算兩個向量之間的點積來學習CUDA流。這些成分是一個核函數,它接受向量xy,並返回一個大小等於塊數的向量,結果,其中每個塊貢獻自己的減少的總和。CUDA流的性能

我也有一臺主機功能dot_gpu調用內核,並降低了矢量結果到最終點積值。

同步版本不只是這一點:

// copy to device 
copy_to_device<double>(x_h, x_d, n); 
copy_to_device<double>(y_h, y_d, n); 

// kernel   
double result = dot_gpu(x_d, y_d, n, blockNum, blockSize); 

而異步一個是這樣:

double result[numChunks]; 
for (int i = 0; i < numChunks; i++) { 
    int offset = i * chunkSize; 

    // copy to device 
    copy_to_device_async<double>(x_h+offset, x_d+offset, chunkSize, stream[i]); 
    copy_to_device_async<double>(y_h+offset, y_d+offset, chunkSize, stream[i]); 

    // kernel 
    result[i] = dot_gpu(x_d+offset, y_d+offset, chunkSize, blockNum, blockSize, stream[i]); 
} 
for (int i = 0; i < numChunks; i++) { 
    finalResult += result[i]; 
    cudaStreamDestroy(stream[i]); 
} 

我使用的數據流,並試圖探討的原因時變得更差的性能。我試圖管理下載,內核調用和上傳,但沒有結果。

// accumulate the result of each block into a single value 
double dot_gpu(const double *x, const double* y, int n, int blockNum, int blockSize, cudaStream_t stream=NULL) 
{ 
double* result = malloc_device<double>(blockNum); 
dot_gpu_kernel<<<blockNum, blockSize, blockSize * sizeof(double), stream>>>(x, y, result, n); 

#if ASYNC 
    double* r = malloc_host_pinned<double>(blockNum); 
    copy_to_host_async<double>(result, r, blockNum, stream); 

    CudaEvent copyResult; 
    copyResult.record(stream); 
    copyResult.wait(); 
#else 
    double* r = malloc_host<double>(blockNum); 
    copy_to_host<double>(result, r, blockNum); 
#endif 

double dotProduct = 0.0; 
for (int i = 0; i < blockNum; i ++) { 
    dotProduct += r[i]; 
} 

cudaFree(result); 
#if ASYNC 
    cudaFreeHost(r); 
#else 
    free(r); 
#endif 

return dotProduct; 
} 

我的猜測是,這個問題是dot_gpu()功能,它不僅調用內核裏面。告訴我,如果我理解正確以下流的執行

foreach stream { 
    cudaMemcpyAsync(device[stream], host[stream], ... stream); 
    LaunchKernel<<<...stream>>>(...); 
    cudaMemcpyAsync(host[stream], device[stream], ... stream); 
} 

主機執行,因爲馬上(cudaMemcpyAsync和內核回報所有三個指令不會被阻擋,但他們會按順序執行的GPU,因爲他們被分配到相同的流)。因此,主機繼續下一個流(即使stream1誰知道它在哪個階段,但是誰在乎......它在GPU上完成他的工作,對吧?)並且再次執行三條指令而不被阻塞......等等等等。但是,我的代碼會阻止主機,然後才能處理下一個流,位於dot_gpu()函數內。是否因爲我分配了空閒內容,並將內核返回的數組減少爲單個值?

回答

1

假設你的客觀CUDA接口做什麼函數和方法的名稱所暗示的,有三個原因,從後續調用dot_gpu()的工作可能不會重疊:

  1. 您的代碼明確塊通過記錄一個事件,並等待爲了它。

  2. 若不是阻斷1.已經,您的代碼將block on the pinned host side allocation and deallocation,你嫌。

  3. 如果您的代碼沒有被阻止2,那麼根據計算能力,從後續調用到dot_gpu()的工作可能仍不會重疊。即使它們入隊到不同的流,也可以使用Devices of compute capability 3.0 or lower do not reorder operations

    即使對於計算能力的裝置3.5和更高the number of streams whose operations can be reordered is limited by the CUDA_​DEVICE_​MAX_​CONNECTIONS environment variable,默認爲8,並且可以被設置爲值一樣大32.