cudaStreamDestroy()不同步/阻止?

时间:2012-06-11 14:21:19

标签: cuda gpgpu

我在Win7 x64机器上的Quadro NVS 295上使用CUDA 4.2。从CUDA C编程手册我读到:

“...通过调用cudaStreamDestroy()释放流。

for (int i = 0; i < 2; ++i)
cudaStreamDestroy(stream[i]);

cudaStreamDestroy()等待给定流中的所有前面的命令 在销毁流并将控制权返回给主机线程之前完成。“

这是真的吗?我写了一个小代码,我或多或少地执行以下操作(我只会使用伪代码):

//transfer input buffer to device
cudaMemcpyToArrayAsync( ... , stream[1]);

//launch kernel
my_kernel <<<dimGrid, dimBlock, 0, stream[1]>>> (...);

//transfer from device to host
cudaMemcpyAsync(.., cudaMemcpyDeviceToHost, stream[1]);

//Destroy stream. In theory this should block the host until everything on the stream is completed!
ret = cudaStreamDestroy(stream[1]); 

通过这个例子,似乎cudaStreamDestroy()调用立即返回到主机,即不等待cudaMemcpyAsync()调用和其他strem指令完成。如果我输入“cudaStreamSynchronize(stream [1]);”叫做befor破坏流,一切顺利但速度慢。那么,我做错了什么?

非常感谢您的回复!

2 个答案:

答案 0 :(得分:3)

我不确定您正在查看的文档版本,但它与我的版本不同。我的CUDA 4.2文档说明了这一点:

  

销毁并清理stream指定的异步流。

     

如果设备仍然在流媒体流中工作   调用cudaStreamDestroy()后,该函数将立即返回   并且将释放与流相关的资源   设备完成流中的所有工作后自动生成。

而且,根据我的经验,这正是它的作用。驱动程序等待直到流为空并将其销毁。但是cudaStreamDestroy 不会阻止调用线程。

您可以通过运行此示例来确认:

#include <stdio.h>
#include <assert.h>
#include <unistd.h>

__global__ void kernel(int * inout, const int N)
{
    int gid = threadIdx.x + blockIdx.x * blockDim.x;
    int gstride = gridDim.x * blockDim.x;

   for (; gid < N; gid+= gstride) inout[gid] *= 2;
}

#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
   if (code != cudaSuccess) 
   {
      fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
      if (abort) exit(code);
   }
}

int main(void)
{
    const int N = 2<<20, sz = N * sizeof(int);

    int * inputs, * outputs, * _inout;

    gpuErrchk( cudaMallocHost((void **)&inputs, sz) );
    gpuErrchk( cudaMallocHost((void **)&outputs, sz) );
    gpuErrchk( cudaMalloc((void **)&_inout, sz) );

    for(int i=0; i<N; i++) { inputs[i] = i; outputs[i] = 0; }

    cudaStream_t stream[2];
    for (int i = 0; i < 2; i++)
        gpuErrchk( cudaStreamCreate(&stream[i]) );

    gpuErrchk( cudaMemcpyAsync(_inout, inputs, sz, cudaMemcpyHostToDevice, stream[1]) );

    kernel<<<128, 128, 0, stream[1]>>>(_inout, N);
    gpuErrchk(cudaPeekAtLastError());

    gpuErrchk( cudaMemcpyAsync(outputs, _inout, sz, cudaMemcpyDeviceToHost, stream[1]) );

    for(int i = 0; i < 2; i++)
        gpuErrchk( cudaStreamDestroy(stream[i]) );

    sleep(1); // remove the sleep and see what happens....

    for(int i = 0; i < N; i++)
        assert( (2 * inputs[i]) == outputs[i] );

    cudaDeviceReset();

    return 0;
}

如果没有sleep()代码将失败,因为GPU尚未完成,但有了它,assert将通过。请注意,sleepcudaStreamDestroy调用之前与使用显式流同步原语略有不同,即使结果相同。如果流在销毁时不是空的,则结果检查永远不会通过。

答案 1 :(得分:2)

CUDA流只是设备任务的执行队列。接受流的所有函数仅将新任务添加到队列而不等待执行结果。 cudaStreamDestroy是一项特殊任务,这意味着需要销毁流,然后完成所有先前的设备任务。 单词

  

“cudaStreamDestroy()在销毁流并将控制权返回给主机线程之前等待给定流中的所有前面的命令完成。”

表示在设备代码完成之前无法销毁流。