问题描述
我的目标是设置通过引用传递到cuda内核的主机变量:
// nvcc test_cudamemcpyAsync.cu -rdc=true
#include <iostream>
__global__ void setHostvar(double& host_var) {
double const var = 2.0;
cudamemcpyAsync(&host_var,&var,sizeof(double),cudamemcpyDevicetoHost);
// identifier "cudamemcpy" is undefined in device code
// cudamemcpy(&host_var,cudamemcpyDevicetoHost);
}
int main() {
double host_var = 1.0;
setHostvar<<<1,1>>>(host_var);
cudaDeviceSynchronize();
std::cout << "host_var = " << host_var << std::endl;
setHostvar<<<1,1>>>(host_var);
cudaDeviceSynchronize();
std::cout << "host_var = " << host_var << std::endl;
return 0;
}
编译并运行:
$ nvcc test_cudamemcpyAsync.cu -rdc=true
$ ./a.out
输出:
host_var = 1
host_var = 1
除了对host_var = 1
的异步调用之外,我还可以理解给定的异步内核调用的第一条输出线cudamemcpyAsync()
。但是,我本以为第二个内核调用是在先前的异步调用完成之后执行的,但是host_var
保持不变。
问题
- 我的期望有什么不对?
- 将引用/指针传递的主机变量设置到内核中的最佳/更好方法是什么?
版本
$ nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
copyright (c) 2005-2020 NVIDIA Corporation
Built on Wed_Jul_22_19:09:09_PDT_2020
Cuda compilation tools,release 11.0,V11.0.221
Build cuda_11.0_bu.TC445_37.28845127_0
解决方法
我的期望有什么不对?
如果我们忽略托管内存和主机固定内存(即,如果我们专注于典型的主机内存,例如您在此处使用的内存),则CUDA中的一项基本原则是设备代码无法触摸/修改/访问主机内存(在Power9处理器平台上除外)。对此的直接扩展是,您(不能附带这些附带条件)不能传递对CUDA内核的引用,而希望对它进行任何有用的操作。
如果您真的想通过引用传递变量,则必须使用托管内存或主机固定的内存。这些需要特定的分配器,因此取决于指针的使用方式。
无论如何,除非您在Power9平台上,否则无法将对基于主机的堆栈内存的引用传递给CUDA内核并明智地使用它。
如果您想查看主机与设备之间的内存合理使用情况,请研究任何CUDA示例代码。
将引用/指针传递的主机变量设置到内核中的最佳/更好方法是什么?
我建议您与此处显示的内容最接近的内容是这样的(使用主机固定的分配器):
$ cat t14.cu
#include <iostream>
__global__ void setHostVar(double *host_var) {
double const var = 2.0;
*host_var = var;
}
int main() {
double *host_var_ptr;
cudaHostAlloc(&host_var_ptr,sizeof(double),cudaHostAllocDefault);
*host_var_ptr = 1.0;
setHostVar<<<1,1>>>(host_var_ptr);
cudaDeviceSynchronize();
std::cout << "host_var = " << *host_var_ptr << std::endl;
setHostVar<<<1,1>>>(host_var_ptr);
cudaDeviceSynchronize();
std::cout << "host_var = " << *host_var_ptr << std::endl;
return 0;
}
$ nvcc -o t14 t14.cu
$ cuda-memcheck ./t14
========= CUDA-MEMCHECK
host_var = 2
host_var = 2
========= ERROR SUMMARY: 0 errors
$
尽管这可能并不完全符合您的要求。
您可能还对CUDA中如何使用异步感到困惑。在不试图涵盖该主题的各个方面的情况下,CUDA内核是异步启动的,这意味着CPU线程在继续操作之前不会等待CUDA内核完成。但是cudaDeviceSynchronize()
强制在允许CPU线程继续执行之前,对该设备的所有先前发布的工作必须完成。其中包括内核以及与内核相关的任何事物,例如从内核/设备代码发出的数据复制(无论如何)。因此,我们希望在调用之后内核活动将是完整/一致的。