cudaStreamAddCallback不会阻止以后的cudaMemcpyAsync

时间:2018-06-18 01:50:45

标签: cuda

我试图让cudaMemcpyHost2Device使用cudaStreamAddCallback等待某些特定事件。我找到了关于cudaStreamCallback API的评论

  

回调将阻止流中的后续工作,直到它完成。

因此,预计后来的工作如cudaMemcpyAsync将被阻止。但后来的代码断言失败了。

#include <cuda_runtime.h>

#include <stdlib.h>
#include <string.h>
#include <cassert>
#include <unistd.h>
#include <stdio.h>

#define cuda_check(x) \
    assert((x) == cudaSuccess)

const size_t size = 1024 * 1024;


static void CUDART_CB cuda_callback(
        cudaStream_t, cudaError_t, void* host) {
    float* host_A = static_cast<float*>(host);
    for (size_t i = 0; i < size; ++i) {
        host_A[i] = i;
    }

    printf("hello\n");
    sleep(1);
}

int main(void) {

    float* A;
    cuda_check(cudaMalloc(&A, size * 4));
    float* host_A = static_cast<float*>(malloc(size * 4));
    float* result = static_cast<float*>(malloc(size * 4));

    memset(host_A, 0, size * 4);

    cuda_check(cudaMemcpy(A, host_A, size * 4, cudaMemcpyHostToDevice));

    cudaStream_t stream;
    cuda_check(cudaStreamCreate(&stream));

    cuda_check(cudaStreamAddCallback(stream, cuda_callback, host_A, 0));
    cuda_check(cudaMemcpyAsync(A, host_A, size * 4, cudaMemcpyHostToDevice,
                               stream));

    cuda_check(cudaStreamSynchronize(stream));
    cuda_check(cudaMemcpy(result, A, size * 4, cudaMemcpyDeviceToHost));

    for (size_t i = 0; i < size; ++i) {
        assert(result[i] == i);
    }

    return 0;
}

1 个答案:

答案 0 :(得分:1)

你对正在发生的事情的假设并不是真的正确。如果我使用分析器来收集代码的运行时API跟踪(我添加cudaDeviceReset以确保分析数据被刷新),我看到了:

124.79ms  129.57ms  cudaMalloc
255.23ms  694.20us  cudaMemcpy
255.93ms  38.881us  cudaStreamCreate
255.97ms  123.44us  cudaStreamAddCallback
256.09ms  1.00348s  cudaMemcpyAsync
1.25957s  76.899us  cudaStreamSynchronize
1.25965s  1.3067ms  cudaMemcpy
1.26187s  71.884ms  cudaDeviceReset

正如您所看到的,cudaMemcpyAsync确实被回调阻止了(完成时花了> 1.0秒)。

复制未能按照您认为的顺序发生的事实可能是由于您使用常规可分页主机分配,而不是固定内存并期望回调立即触发空队列。重要的是要注意,注册流回调和启动副本的时间少于0.1毫秒,并且回调可能不会立即触发(假设它在另一个线程中),这样就有可能复制在回调函数对空队列条件作出反应之前启动。

有趣的是,如果我将host_A更改为固定分配并运行代码,我会获得此API时间轴:

124.21ms  130.24ms  cudaMalloc
254.45ms  1.0988ms  cudaHostAlloc
255.98ms  376.14us  cudaMemcpy
256.36ms  33.841us  cudaStreamCreate
256.39ms  87.303us  cudaStreamAddCallback
256.48ms  17.208us  cudaMemcpyAsync
256.50ms  1.00331s  cudaStreamSynchronize
1.25981s  1.2880ms  cudaMemcpy
1.26205s  68.506ms  cudaDeviceReset

现在请注意,cudaStreamSynchronize是被阻止的呼叫。但是在这种情况下,程序会传递断言,这可能与调度程序正确地管理主机内存固定的流中的依赖关系有关。