C++ 向主机发送CUDA信号

C++ 向主机发送CUDA信号,c++,cuda,C++,Cuda,有没有办法在内核执行结束时向主机发送信号(成功/失败) 我正在研究一个迭代过程,在这个过程中,计算是在设备中进行的,在每次迭代之后,一个布尔变量被传递给主机,告诉它这个过程是否收敛。根据变量,host决定停止迭代或进行另一轮迭代 在每次迭代结束时复制单个布尔变量会使通过并行化获得的时间增益为零。因此,我想找到一种方法,让主机知道收敛状态(成功/失败),而不必每次都使用CudaMemCpy。 注意:使用固定内存传输数据后存在时间问题 我所看到的替代方案 asm(“陷阱;”;&断言() 这些将分别触

有没有办法在内核执行结束时向主机发送信号(成功/失败)

我正在研究一个迭代过程,在这个过程中,计算是在设备中进行的,在每次迭代之后,一个布尔变量被传递给主机,告诉它这个过程是否收敛。根据变量,host决定停止迭代或进行另一轮迭代

在每次迭代结束时复制单个布尔变量会使通过并行化获得的时间增益为零。因此,我想找到一种方法,让主机知道收敛状态(成功/失败),而不必每次都使用CudaMemCpy。 注意:使用固定内存传输数据后存在时间问题

我所看到的替代方案

  • asm(“陷阱;”;&断言() 这些将分别触发主机中的未知错误和cudaErrorAssert。不幸的是,它们是“粘性”的,因为无法使用CudaGetLastError重置错误。唯一的方法是使用cudaDeviceReset()重置设备

  • 使用CudaHostAllocMapped来避免CudaMemCpy这是没有用的,因为与标准固定内存分配+CudaMemCpy相比,它没有任何基于时间的优势。(第460页,多核和GPU编程,综合方法,Morgran Kruffmann 2014)


  • 我想这里真正的问题是,您的迭代内核运行时间非常短(大约为100us或更少),这意味着每次迭代的工作量非常小。最好的解决方案可能是尝试增加每次迭代的工作量(重构代码/算法,解决更大的问题,等等)

    然而,这里有一些可能性:

  • 使用映射/固定内存。依我看,你在问题第2项中的主张是不受支持的,没有比我们许多人可能看不到的一本书的一页参考资料更多的上下文

  • 使用动态并行。将内核启动进程移动到正在发布子内核的CUDA父内核。无论子内核设置了什么布尔值,都可以在父内核中立即发现,而不需要cudaMemcpy操作或映射/固定内存

  • 对于每个管道阶段,使用流水线算法,并将推测性内核启动与布尔值的设备->主机副本重叠

  • 我认为前面两个项目相当明显,所以我将提供一个项目3的例子。其基本思想是,我们将在两个流之间进行乒乓,将内核交替地发射到一个流中,然后发射到另一个流中。我们将有第三个流,这样我们就可以将设备->主机复制操作与下一次启动的执行重叠。由于D->H拷贝与内核执行重叠,拷贝操作实际上没有“成本”,它被内核执行工作隐藏

    下面是一个完整的示例,外加一个nvvp时间线:

    $ cat t267.cu
    #include <stdio.h>
    
    
    const int stop_count = 5;
    const long long tdelay = 1000000LL;
    
    __global__ void test_kernel(int *icounter, bool *istop, int *ocounter, bool *ostop){
    
      if (*istop) return;
      long long start = clock64();
      while (clock64() < tdelay+start);
      int my_count = *icounter;
      my_count++;
      if (my_count >= stop_count) *ostop = true;
      *ocounter = my_count;
    }
    
    int main(){
      volatile bool *v_stop;
      volatile int *v_counter;
      bool *h_stop, *d_stop1, *d_stop2, *d_s1, *d_s2, *d_ss;
      int *h_counter, *d_counter1, *d_counter2, *d_c1, *d_c2, *d_cs;
      cudaStream_t s1, s2, s3, *sp1, *sp2, *sps;
      cudaEvent_t e1, e2, *ep1, *ep2, *eps;
      cudaStreamCreate(&s1);
      cudaStreamCreate(&s2);
      cudaStreamCreate(&s3);
      cudaEventCreate(&e1);
      cudaEventCreate(&e2);
      cudaMalloc(&d_counter1, sizeof(int));
      cudaMalloc(&d_stop1, sizeof(bool));
      cudaMalloc(&d_counter2, sizeof(int));
      cudaMalloc(&d_stop2, sizeof(bool));
      cudaHostAlloc(&h_stop, sizeof(bool), cudaHostAllocDefault);
      cudaHostAlloc(&h_counter, sizeof(int), cudaHostAllocDefault);
      v_stop = h_stop;
      v_counter = h_counter;
      int n_counter = 1;
      h_stop[0] = false;
      h_counter[0] = 0;
      cudaMemcpy(d_stop1, h_stop, sizeof(bool), cudaMemcpyHostToDevice);
      cudaMemcpy(d_stop2, h_stop, sizeof(bool), cudaMemcpyHostToDevice);
      cudaMemcpy(d_counter1, h_counter, sizeof(int), cudaMemcpyHostToDevice);
      cudaMemcpy(d_counter2, h_counter, sizeof(int), cudaMemcpyHostToDevice);
      sp1 = &s1;
      sp2 = &s2;
      ep1 = &e1;
      ep2 = &e2;
      d_c1 = d_counter1;
      d_c2 = d_counter2;
      d_s1 = d_stop1;
      d_s2 = d_stop2;
      test_kernel<<<1,1, 0, *sp1>>>(d_c1, d_s1, d_c2, d_s2);
      cudaEventRecord(*ep1, *sp1);
      cudaStreamWaitEvent(s3, *ep1, 0);
      cudaMemcpyAsync(h_stop, d_s2, sizeof(bool), cudaMemcpyDeviceToHost, s3);
      cudaMemcpyAsync(h_counter, d_c2, sizeof(int), cudaMemcpyDeviceToHost, s3);
      while (v_stop[0] == false){
        cudaStreamWaitEvent(*sp2, *ep1, 0);
        sps = sp1; // ping-pong
        sp1 = sp2;
        sp2 = sps;
        eps = ep1;
        ep1 = ep2;
        ep2 = eps;
        d_cs = d_c1;
        d_c1 = d_c2;
        d_c2 = d_cs;
        d_ss = d_s1;
        d_s1 = d_s2;
        d_s2 = d_ss;
        test_kernel<<<1,1, 0, *sp1>>>(d_c1, d_s1, d_c2, d_s2);
        cudaEventRecord(*ep1, *sp1);
        while (n_counter > v_counter[0]);
        n_counter++;
        if(v_stop[0]  == false){
          cudaStreamWaitEvent(s3, *ep1, 0);
          cudaMemcpyAsync(h_stop, d_s2, sizeof(bool), cudaMemcpyDeviceToHost, s3);
          cudaMemcpyAsync(h_counter, d_c2, sizeof(int), cudaMemcpyDeviceToHost, s3);
        }
      }
      cudaDeviceSynchronize();  // optional
      printf("terminated at counter = %d\n", v_counter[0]);
    }
    $ nvcc -arch=sm_52 -o t267 t267.cu
    $ ./t267
    terminated at counter = 5
    $
    
    $cat t267.cu
    #包括
    常数int stop_计数=5;
    const long tdelay=10000000000ll;
    __全局无效测试内核(int*i计数器,bool*istop,int*o计数器,bool*ostop){
    如果(*istop)返回;
    长启动=时钟64();
    while(clock64()=停止计数)*ostop=true;
    *ocounter=我的计数;
    }
    int main(){
    挥发性bool*v_停止;
    易失性int*v_计数器;
    bool*h_stop、*d_stop1、*d_stop2、*d_s1、*d_s2、*d_ss;
    int*h_计数器、*d_计数器1、*d_计数器2、*d_c1、*d_c2、*d_cs;
    cudaStream_t s1、s2、s3、*sp1、*sp2、*sps;
    cudaEvent_t e1、e2、*ep1、*ep2、*eps;
    cudaStreamCreate(&s1);
    cudaStreamCreate(&s2);
    cudaStreamCreate(&s3);
    cudaEventCreate(&e1);
    cudaEventCreate(&e2);
    cudaMalloc(&d_counter1,sizeof(int));
    Cudamaloc(和d_stop1,sizeof(bool));
    Cudamaloc(&d_counter2,sizeof(int));
    Cudamaloc(和d_stop2,sizeof(bool));
    cudaHostAlloc(和h_站、sizeof(bool)、cudaHostAllocDefault);
    cudaHostAlloc(和h_计数器、sizeof(int)、cudaHostAllocDefault);
    v_停止=h_停止;
    v_计数器=h_计数器;
    int n_计数器=1;
    h_停止[0]=错误;
    h_计数器[0]=0;
    cudaMemcpy(d_stop1、h_stop、sizeof(bool)、cudamemcpyhostodevice);
    cudaMemcpy(d_stop2、h_stop、sizeof(bool)、cudaMemcpyHostToDevice);
    cudaMemcpy(d_计数器1、h_计数器、sizeof(int)、cudamemcpyhostodevice);
    cudaMemcpy(d_计数器2、h_计数器、sizeof(int)、cudamemcpyhostodevice);
    sp1=&s1;
    sp2=&s2;
    ep1=&e1;
    ep2=&e2;
    d_c1=d_计数器1;
    d_c2=d_计数器2;
    d_s1=d_stop1;
    d_s2=d_stop2;
    测试内核(d_c1、d_s1、d_c2、d_s2);
    cudaEventRecord(*ep1,*sp1);
    cudaStreamWaitEvent(s3,*ep1,0);
    cudaMemcpyAsync(h_站,d_s2,sizeof(bool),cudamemcpydevicetoost,s3);
    cudaMemcpyAsync(h_计数器,d_c2,sizeof(int),cudaMemcpyDeviceToHost,s3);
    while(v_stop[0]==false){
    cudaStreamWaitEvent(*sp2,*ep1,0);
    sps=sp1;//乒乓球
    sp1=sp2;
    sp2=sps;
    eps=ep1;
    ep1=ep2;
    ep2=每股收益;
    d_cs=d_c1;
    d_c1=d_c2;
    d_c2=d_cs;
    d_ss=d_s1;
    d_s1=d_s2;
    d_s2=d_ss;
    测试内核(d_c1、d_s1、d_c2、d_s2);
    cudaEventRecord(*ep1,*sp1);
    而(n_计数器>v_计数器[0]);
    n_计数器++;
    如果(v_stop[0]==false){
    cudaStreamWaitEvent(s3,*ep1,0);
    cudaMemcpyAsync(h_站,d_s2,sizeof(bool),cudamemcpydevicetoost,s3);
    cudaMemcpyAsync(h_计数器,d_c2,sizeof(int),cudaMemcpyDeviceToHost,s3);
    }
    }
    cudaDeviceSynchronize();//可选
    printf(“终止于计数器=%d\n”,v_计数器[0]);
    }
    $nvcc-arch=sm_52-o t267 t267.cu
    美元/t267
    在计数器处终止=5
    $
    

    在上图中,我们看到5次内核启动是明显的(实际上是6次),它们在两个流之间来回跳跃。(第六次内核启动,我们希望从代码组织和管道中看到它,这是上面stream15末尾的一段很短的一行。这个内核启动了,但立即见证了
    停止