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