我基本上是在寻找一种从设备中同步流的方法。我想避免使用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()可以正常工作,但正如我所提到的,它会跨流来序列化我的内核执行。
谢谢, 马尔钦
答案 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
编译。因此,编译器可能足够聪明,可以优化完整的空子内核调用。