【问题标题】:cudaMemcpyFromSymbol on a __device__ variable__device__ 变量上的 cudaMemcpyFromSymbol
【发布时间】:2014-09-28 04:19:23
【问题描述】:

我正在尝试在 __device__ 变量上应用内核函数,根据规范,该变量位于“全局内存中”

#include <stdio.h>
#include "sys_data.h"
#include "my_helper.cuh"
#include "helper_cuda.h"
#include <cuda_runtime.h>


double X[10] = {1,-2,3,-4,5,-6,7,-8,9,-10};
double Y[10] = {0};
__device__ double DEV_X[10];


int main(void) {
    checkCudaErrors(cudaMemcpyToSymbol(DEV_X, X,10*sizeof(double)));
    vector_projection<double><<<1,10>>>(DEV_X, 10);
    getLastCudaError("oops");
    checkCudaErrors(cudaMemcpyFromSymbol(Y, DEV_X, 10*sizeof(double)));
    return 0;
}

核函数vector_projectionmy_helper.cuh中定义如下:

template<typename T> __global__ void vector_projection(T *dx, int n) {
    int tid;
    tid = threadIdx.x + blockIdx.x * blockDim.x;
    if (tid < n) {
        if (dx[tid] < 0)
            dx[tid] = (T) 0;
    }
}

如您所见,我使用cudaMemcpyToSymbolcudaMemcpyFromSymbol 将数据传入和传出设备。但是,我收到以下错误:

CUDA error at ../src/vectorAdd.cu:19 code=4(cudaErrorLaunchFailure) 
  "cudaMemcpyFromSymbol(Y, DEV_X, 10*sizeof(double))" 

脚注:我当然可以避免使用__device__ 变量,而选择like this 可以正常工作的东西;我只是想看看如何用__device__ 变量做同样的事情(如果可能的话)。

更新:cuda-memcheck 的输出可以在http://pastebin.com/AW9vmjFs 找到。我得到的错误信息如下:

========= Invalid __global__ read of size 8
=========     at 0x000000c8 in /home/ubuntu/Test0001/Debug/../src/my_helper.cuh:75:void vector_projection<double>(double*, int)
=========     by thread (9,0,0) in block (0,0,0)
=========     Address 0x000370e8 is out of bounds

【问题讨论】:

  • 您的vector_projection 内核在执行期间失败。您的getLastCudaError 调用将捕获某些类型的内核问题。其他人可能要等到下一个同步点(即cudaMemcpyFromSymbol)才会出现。文档表明这些调用可以从以前的异步活动中返回错误。尝试使用cuda-memcheck 运行您的代码。如果您按照here 所述进行 cuda 错误检查,您将得到更明确的指示,表明问题出在内核上。
  • 谢谢@RobertCrovella。确实,我的内核似乎有问题;见pastebin.com/AW9vmjFs。在调用cudaMemcpyToSymbol 之前需要分配DEV_X 吗?我无法弄清楚问题可能是什么......

标签: cuda gpu gpgpu


【解决方案1】:

问题的根源在于你是not allowed to take the address of a device variable in ordinary host code

vector_projection<double><<<1,10>>>(DEV_X, 10);
                                    ^

虽然这似乎编译正确,但实际传递的地址是垃圾。

要在主机代码中获取设备变量的地址,我们可以使用cudaGetSymbolAddress

这是一个可以为我正确编译和运行的示例:

$ cat t577.cu
#include <stdio.h>

double X[10] = {1,-2,3,-4,5,-6,7,-8,9,-10};
double Y[10] = {0};
__device__ double DEV_X[10];

template<typename T> __global__ void vector_projection(T *dx, int n) {
    int tid;
    tid = threadIdx.x + blockIdx.x * blockDim.x;
    if (tid < n) {
        if (dx[tid] < 0)
            dx[tid] = (T) 0;
    }
}



int main(void) {
    cudaMemcpyToSymbol(DEV_X, X,10*sizeof(double));
    double *my_dx;
    cudaGetSymbolAddress((void **)&my_dx, DEV_X);
    vector_projection<double><<<1,10>>>(my_dx, 10);
    cudaMemcpyFromSymbol(Y, DEV_X, 10*sizeof(double));
    for (int i = 0; i < 10; i++)
      printf("%d: %f\n", i, Y[i]);
    return 0;
}
$ nvcc -arch=sm_35 -o t577 t577.cu
$ cuda-memcheck ./t577
========= CUDA-MEMCHECK
0: 1.000000
1: 0.000000
2: 3.000000
3: 0.000000
4: 5.000000
5: 0.000000
6: 7.000000
7: 0.000000
8: 9.000000
9: 0.000000
========= ERROR SUMMARY: 0 errors
$

这不是解决此问题的唯一方法。在设备代码中获取设备变量的地址是合法的,因此您可以使用以下代码修改内核:

T *dx = DEV_X;

并放弃将设备变量作为内核参数传递。根据 cmets 中的建议,您还可以修改代码以使用 Unified Memory

关于错误检查,如果您偏离proper cuda error checking 并且不小心偏离,结果可能会令人困惑。大多数 cuda API 调用除了由自身行为引起的错误外,还可能返回由以前的一些 CUDA 异步活动(通常是内核调用)导致的错误。

【讨论】:

  • 非常感谢。确实奏效了。我发现 DEV_X 之前的修饰符 __managed__ 也可以解决问题,原因与您在回答中解释的原因相同。在性能方面,__device__ 变量的使用与这样的比较(变量在main 函数的范围内声明)如何:pastebin.com/rx9nUnGX
  • 说到纯粹关于设备代码的性能,无论设备指针是如何创建的,无论是静态的(使用__device__),代码性能应该没有显着差异、动态(使用cudaMalloc)或通过UM,无论是静态(__managed__ __device__)还是动态(使用cudaMallocManaged)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-07
  • 1970-01-01
  • 2011-10-01
相关资源
最近更新 更多