CUDA动态并行;来自设备的流同步

CUDA动态并行;来自设备的流同步,cuda,cublas,Cuda,Cublas,我基本上是在寻找一种方法来同步设备内的流。我希望避免使用cudaDeviceSynchronize(),因为它会序列化我希望使用流并发执行的内核的执行 更详细的描述:我写了一个内核,这是一个稳定的双共轭梯度解算器。我想使用流在不同的数据上同时使用这个内核 这个内核使用cublas函数。它们是从内核内部调用的 解算器所需的操作之一是计算两个向量的点积。这可以通过cublasdot()完成。但由于此调用是同步的,不同流中的内核执行将被序列化。我没有调用点积函数,而是使用异步调用的cublasspmv

我基本上是在寻找一种方法来同步设备内的流。我希望避免使用cudaDeviceSynchronize(),因为它会序列化我希望使用流并发执行的内核的执行

更详细的描述:我写了一个内核,这是一个稳定的双共轭梯度解算器。我想使用流在不同的数据上同时使用这个内核

这个内核使用cublas函数。它们是从内核内部调用的

解算器所需的操作之一是计算两个向量的点积。这可以通过cublasdot()完成。但由于此调用是同步的,不同流中的内核执行将被序列化。我没有调用点积函数,而是使用异步调用的cublasspmv()计算点积。问题是该函数在计算结果之前返回。因此,我希望同步来自设备的流-我正在寻找与cudaStreamSynchronize()等效但可从设备调用的流

__device__ float _cDdot(cublasHandle_t & cublasHandle, const int n, real_t * x, real_t * y) {
      float *norm; norm = new float; 
      float alpha = 1.0f; float beta = 0.0f;

      cublasSgemv_v2(cublasHandle, CUBLAS_OP_N ,1 , n, &alpha, x, 1, y, 1, &beta, norm, 1);

      return *norm;
}
如何确保在函数返回之前计算结果?当然,cudaDeviceSynchronize()的插入是有效的,但正如我提到的,它跨流序列化了内核的执行。

如果您仔细阅读(特别是流、事件和同步),可能会有一些想法。以下是我的想法:

有一个隐式空流(在设备上)与调用
\u cDdot
函数的执行序列关联(名字很奇怪,IMHO,因为在这种情况下,您使用的是
float
数量,即使用
Sgemv
)。因此,在调用函数中的
cublasSgemv_v2
之后发出的任何cuda内核或API调用都应等待与
cublasSgemv_v2
函数相关联的任何cuda活动完成。如果在调用
cublasSgemv_v2
之后插入一个无害的cuda-API调用,或者一个伪内核调用,则应该等待该调用完成。这将为您提供所需的线程级同步。您还可以使用
cudaventrecord
调用,然后再使用
cudaStreamWaitEvent
调用

下面是一个显示隐式流同步方法的示例:

#include <stdio.h>
#include <cublas_v2.h>
#define SZ 16

__global__ void dummy_kernel(float *in, float *out){
  *out = *in;
}

__device__ float _cDdot(cublasHandle_t & cublasHandle, const int n, float * x, float * y, const int wait) {
      float *norm; norm = new float;
      float alpha = 1.0f; float beta = 0.0f;
      *norm = 0.0f;
      cublasSgemv_v2(cublasHandle, CUBLAS_OP_N ,1 , n, &alpha, x, 1, y, 1, &beta, norm, 1);
      if (wait){
        dummy_kernel<<<1,1>>>(norm, norm);
        }
      return *norm;
}


__global__ void compute(){
  cublasHandle_t my_h;
  cublasStatus_t status;
  status = cublasCreate(&my_h);
  if (status != CUBLAS_STATUS_SUCCESS) printf("cublasCreate fail\n");
  float *x, *y;
  x = new float[SZ];
  y = new float[SZ];
  for (int i = 0; i < SZ; i++){
    x[i] = 1.0f;
    y[i] = 1.0f;}
  float result = _cDdot(my_h, SZ, x, y, 0);
  printf("result with no wait = %f\n", result);
  result = _cDdot(my_h, SZ, x, y, 1);
  printf("result with wait = %f\n", result);
}

int main(){

  compute<<<1,1>>>();
  cudaDeviceSynchronize();
  return 0;
}
结果:

$ ./t302
result with no wait = 0.000000
result with wait = 16.000000
$

不幸的是,我尝试了一个完全空的
伪内核
;除非我使用
-G
进行编译,否则这不起作用。因此,编译器可能足够聪明,可以优化出一个完整的空子内核调用。

您说
cublasdot()
调用是同步的。什么意思?cuBLAS调用正在异步执行。我认为除了使用
cudaDeviceSynchronize()
来实现设备的主动等待之外,没有其他选择。事实上,除了一些返回标量值的1级例程之外,cuBLAS API是异步的,正如您所写的。谢谢你的回答,但也许有人有其他想法?谢谢你的回答。不幸的是,我不确定我是否同意你的想法。函数_cDdot用于双共轭梯度解算器。对于小问题,我希望通过将内核午餐分配给不同的流,同时为多个不同输入的解算器提供午餐。对于大问题,我只有一个流(比如默认流)——在这种情况下,我最好使用cublas函数来计算点积(它是同步的,因为只有一个流),对于并发午餐(对于小矩阵),您的方法仍然会导致序列化执行。我玩过CudaVentRecord和cudaStreamWaitEvent,但当从设备调用时,我无法获得所需的行为:/非常感谢您抽出时间!!
$ ./t302
result with no wait = 0.000000
result with wait = 16.000000
$