CUDA动态Parallelizm;从设备流同步

时间:2013-12-13 20:50:09

标签: cuda cublas

我基本上是在寻找一种从设备中同步流的方法。我想避免使用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()可以正常工作,但正如我所提到的,它会跨流来序列化我的内核执行。

谢谢, 马尔钦

1 个答案:

答案 0 :(得分:1)

如果您仔细阅读the programming guide dynamic parallelism section(特别是流,事件和同步),您可能会得到一些想法。这就是我想出的:

在调用_cDdot函数的执行序列中有一个隐含的NULL流(在设备上)(奇怪的是,恕我直言,因为在这种情况下你使用float数量,即使用Sgemv)。因此,在函数调用cublasSgemv_v2之后发出的任何cuda内核或API调用应该等到与cublasSgemv_v2函数关联的任何cuda活动完成。如果在调用cublasSgemv_v2之后插入一个无害的cuda API调用,或者是一个虚拟内核调用,它应该等待它完成。这应该为您提供您所追求的线程级同步。您也可以使用cudaEventRecord来电,然后进行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;
}

编译:

nvcc -arch=sm_35 -rdc=true -o t302 t302.cu -lcudadevrt -lcublas -lcublas_device

结果:

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

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