在CUDA中分配设备变量时遇到问题

4
我遇到了一个问题,尝试给设备变量赋值并将其复制到主机变量中。一开始我设置d_test和h_test都为0.0。我有一个简单的内核来将设备变量d_test设置为1.0。然后我将其复制到主机变量h_test并打印。问题是当我打印时,h_test的值仍然是0.0。我做错了什么?以下是代码:
// -*- mode: C -*-
#include <stdio.h>
#include <stdlib.h>
#include <cuda_runtime.h>

// device variable and kernel
__device__ float d_test;
__global__ void kernel1(float d_test) { d_test = 1.0; }


int main() {

  // initialise variables
  float h_test = 0.0;
  cudaMemset(&d_test,0,sizeof(float));

  // invoke kernel
  kernel1 <<<1,1>>> (d_test);

  // Copy device variable to host and print
  cudaMemcpy(&h_test,&d_test,sizeof(float),cudaMemcpyDeviceToHost);
  printf("%f\n",h_test);  

}

在cudaMemcpyFromSymbol中使用引号会导致复制操作后出现0.0,因此只需删除引号即可使其正常工作。 - erogol
你使用的CUDA版本是哪一个? - BRabbit27
4个回答

6
您的代码存在几个问题。
  1. 正如pezcode正确指出的那样,kernel1的参数d_test遮盖了全局变量,因此当它分配给d_test时,实际上是更改其参数的值,而不是您想要的全局变量。在这个例子中,kernel1不需要使用参数。

  2. 在从全局__device__变量复制时,请使用cudaMemcpyFromSymbol而不是cudaMemcpy。

以下是完整的解决方案:
// -*- mode: C -*-
#include <stdio.h>
#include <stdlib.h>
#include <cuda_runtime.h>

// device variable and kernel
__device__ float d_test;
__global__ void kernel1() { d_test = 1.0; }

int main() {

  // initialise variables
  float h_test = 0.0;
  cudaMemset(&d_test,0,sizeof(float));

  // invoke kernel
  kernel1 <<<1,1>>> ();

  // Copy device variable to host and print
  cudaMemcpyFromSymbol(&h_test, d_test, sizeof(float), 0, cudaMemcpyDeviceToHost);
  // or cudaMemcpyFromSymbol(&h_test, "d_test", sizeof(float), 0, cudaMemcpyDeviceToHost);
  // until CUDA 5.0
  printf("%f\n",h_test);  
}

输出结果:

$ nvcc test.cu -run
1.000000

2019年11月更新

CUDA Toolkit文档指出:“在CUDA 4.1中,使用命名变量字符串作为符号参数已被弃用,并在CUDA 5.0中删除。”


目前,cudaMemset 返回错误消息:'cudaMemset';而 cudaMemcpyFromSymbol 返回错误消息:'returns error msg'(API 已更改,请参见:https://stackoverflow.com/a/9997978/1150712) - zhangxaochen
1
不确定为什么,但是一旦删除引号就可以工作(例如:cudaMemcpyFromSymbol(&h_test, "d_test", sizeof(float), 0, cudaMemcpyDeviceToHost);) - user2343039

4

在使用cudaMemcpy时,我遇到了困难,并得到了错误的值。经过一番努力,我发现cudaMemcpyFromSymbol对我有用。我不得不删除"d_test"周围的引号,并且使用cudaMalloc来代替cudaMemset,因为后者会出现无效参数错误。


1

我的猜测是kernel1更改了它的参数d_test,因为它隐藏了全局变量device。 重命名其中一个或者如果使用CUDA可以这样做,通过设置::d_test来显式地使用全局作用域。


我已经在内核中将d_test的名称更改为test,但这并没有带来任何区别。全局作用域::d_test是什么东西? - Eddy
由于某些原因,我认为您正在使用C++,在其中可以使用scope::var来访问特定范围内的变量。仅使用::将意味着全局范围,因此访问与函数参数具有相同名称的全局变量。对于造成的混淆,我感到抱歉:s - pezcode

0

这段代码对我来说很好用。关键在于这里。

第二个变量应该像这样\n

cudaMemcpyFromSymbol(&h_test, d_test, sizeof(float), 0, cudaMemcpyDeviceToHost);

然后使用以下命令编译您的代码
nvcc -Xcompiler -arch=sm_30 sample.cu -o sample.exe


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接