__device__变量上的cudaMemcpyFromSymbol

Pan*_*kis 3 cuda gpu gpgpu

我正在尝试对__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;
}
Run Code Online (Sandbox Code Playgroud)

内核函数vector_projection定义my_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;
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我用cudaMemcpyToSymbolcudaMemcpyFromSymbol传输数据,并从设备.但是,我收到以下错误:

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

脚注:我当然可以避免使用__device__变量并找到这样的工作正常; 我只是想看看如何用__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
Run Code Online (Sandbox Code Playgroud)

Rob*_*lla 6

问题的根源是您不允许在普通主机代码中获取设备变量的地址:

vector_projection<double><<<1,10>>>(DEV_X, 10);
                                    ^
Run Code Online (Sandbox Code Playgroud)

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

要在主机代码中获取设备变量的地址,我们可以使用 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
$
Run Code Online (Sandbox Code Playgroud)

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

T *dx = DEV_X;
Run Code Online (Sandbox Code Playgroud)

并放弃将设备变量作为内核参数传递.如评论中所述,您还可以修改代码以使用统一内存.

关于错误检查,如果您偏离了正确的cuda错误检查并且在偏差中不小心,结果可能会令人困惑.除了由自己的行为引起的错误之外,大多数cuda API调用都可以返回由先前的一些CUDA异步活动(通常是内核调用)导致的错误.