【问题标题】:CUDA cudaMemcpyFromSymbol "invalid device symbol" error?CUDA cudaMemcpyFromSymbol“无效的设备符号”错误?
【发布时间】:2018-12-02 12:19:36
【问题描述】:

所以我看到一个parent 的问题,关于如何使用cudaMemcpyToSymbol 从主机复制到 GPU 上的常量内存。

我的问题是如何做相反的事情,使用cudaMemcpyFromSymbol从设备常量内存复制到主机。

在下面的最小可重现示例中,我得到了

  • 1) invalid device symbol 使用 cudaMemcpyFromSymbol(const_d_a, b, size); 时出错,或
  • 2) 如果我使用cudaMemcpyFromSymbol(&b, const_d_a, size, cudaMemcpyDeviceToHost),则得到segmentation fault

我已经咨询了manual,它建议我按照 1) 进行编码,this SO question 建议我按照 2) 进行编码。他们都不在这里工作。

有人可以帮忙提出解决方法吗?我一定是理解不正确...谢谢!

代码如下:

// a basic CUDA function to test working with device constant memory
#include <stdio.h>
#include <cuda.h>

const unsigned int N = 10;    // size of vectors

__constant__ float const_d_a[N * sizeof(float)];  

int main()
{
    float * a, * b;  // a and b are vectors. c is the result
    a = (float *)calloc(N, sizeof(float));
    b = (float *)calloc(N, sizeof(float));

    /**************************** Exp 1: sequential ***************************/
    int i;
    int size = N * sizeof(float);
    for (i = 0; i < N; i++){
        a[i] = (float)i / 0.23 + 1;
    }


    // 1. copy a to constant memory
    cudaError_t err = cudaMemcpyToSymbol(const_d_a, a, size);
    if (err != cudaSuccess){
        printf("%s in %s at line %d\n", cudaGetErrorString(err), __FILE__, __LINE__);
        exit(EXIT_FAILURE);
    }

    cudaError_t err2 = cudaMemcpyFromSymbol(const_d_a, b, size);
    if (err2 != cudaSuccess){
        printf("%s in %s at line %d\n", cudaGetErrorString(err2), __FILE__, __LINE__);
        exit(EXIT_FAILURE);
    }

    double checksum0, checksum1;
    for (i = 0; i < N; i++){
        checksum0 += a[i];
        checksum1 += b[i];
    }

    printf("Checksum for elements in host memory is %f\n.", checksum0);
    printf("Checksum for elements in constant memory is %f\n.", checksum1);

    return 0;
}

【问题讨论】:

    标签: cuda


    【解决方案1】:

    在 CUDA 中,各种 cudaMemcpy* 操作是在 C 标准库 memcpy 例程之后建模的。在该函数中,第一个指针总是是目标指针,第二个指针是总是源指针。对于所有 cudaMemcpy* 函数也是如此。

    因此,如果你想做cudaMemcpyToSymbol,符号最好是传递给函数的第一个(目标)参数(第二个参数是主机指针)。如果你想做cudaMemcpyFromSymbol,符号需要是第二个参数(源位置),主机指针是第一个参数。这不是你在这里所拥有的:

    cudaError_t err2 = cudaMemcpyFromSymbol(const_d_a, b, size);
                                              ^        ^
                                              |       This should be the symbol.
                                              |  
                                       This is supposed to be the host destination pointer.
    

    您可以通过查看API documentation 来发现这一点。

    如果我们在该行代码中颠倒这两个参数的顺序:

    cudaError_t err2 = cudaMemcpyFromSymbol(b, const_d_a, size);
    

    您的代码将毫无错误地运行,并且打印的最终结果将匹配。

    在这些函数中,没有必要在ab 指针中使用与号。 ab 已经是指针。在您链接的示例中,pi_gpu_h 不是指针。它是一个普通的变量。要使用cudaMemcpyFromSymbol 向其复制某些内容,必须获取该普通变量的地址,因为该函数需要一个(目标)指针。

    顺便说一句,这看起来不对:

    __constant__ float const_d_a[N * sizeof(float)];  
    

    这实际上是一个静态数组声明,除了 __constant__ 装饰器之外,它应该等同于在 C 或 C++ 中的实现方式。如果您想要存储N float 数量,则无需在此处将N 乘以sizeof(float)。只需 N 本身就可以做到这一点:

    __constant__ float const_d_a[N];
    

    但是,保持原样不会对您发布的代码造成问题。

    【讨论】:

    • 感谢罗伯特的及时回复!很清楚,我现在明白我错在哪里了。对于我链接的示例,用户建议我们使用cudaMemcpyFromSymbol(&amp;pi_gpu_h, pi_gpu, sizeof(double), cudaMemcpyDeviceToHost),我想这个界面已经过时了?因为我们不需要最终参数。
    • 也感谢您指出我在数组声明中的额外sizeof(float) 规范。这可能解决了我在另一段代码中遇到的另一个错误。我把这个数组声明误认为是 malloc 函数,我需要在其中指定确切的字节数。谢谢!
    • 如果你研究我链接的文档,你会发现这里有默认参数。函数的默认参数不是 CUDA 特定的概念,您可以通过学习 C++ 来了解它。由于存在默认参数,因此可以提供或省略它们。但是,您链接的示例确实包含错误。它与提供的参数的顺序有关。但是,我不会尝试在 cmets 的空间中解释它。
    • 我已经更正了您链接的问题中的答案。错误的性质描述为here
    • 感谢您的提示!我现在明白 1)cudaMemcpyDeviceToHost 参数设置为默认值,2)原始答案错过了偏移参数,因此顺序有问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多