CUDA错误:“__ device__,__ constant__和__shared__变量不支持动态初始化”

时间:2018-04-05 15:05:57

标签: c++ memory-management cuda gpu variable-initialization

我正在尝试静态初始化GPU内存中的只读std::map变量,如下所示:

// EXAMPLE 1:
using namespace std;

// first attempt: __device__ extern const
__device__ extern const map<char, const char*> BYTES_TO_WORDS = {
{0xB0, "zero"}, {0xB1, "one"}, {0xB2, "two"}, {0xB3, "three"}};

// second attempt: __const__ static
enum class Color{RED, GREEN, BLUE};
enum class Device{PC, TABLET, PHONE};

__constant__ static map<Color, Device> COLORS_TO_THINGS = {
{Color::RED,Device::PC},{Color::GREEN,Device::TABLET},{Color::BLUE,Device::PHONE}};

但我收到以下错误:

dynamic initialization is not supported for __device__, __constant__ and __shared__ variables

我很困惑,因为当我尝试这样的事情时,我没有收到这个错误:

// EXAMPLE 2:
__device__ extern int PLAIN_ARRAY[] = {1, 2, 3, 4, 5};

我只是希望能够创建并初始化只读 std::map并从CPU和GPU代码访问它。如果你能告诉我如何正确地做到这一点,我将不胜感激。

修改 有人指出,设备代码不支持标准库。但我得到的错误似乎表明这是一个内存管理问题。

1 个答案:

答案 0 :(得分:2)

初始化诸如std::map之类的C ++对象涉及在运行时调用构造函数。您用来初始化std::map的C ++ 11语法是list initialization的一种形式,它调用std::initializer_list构造函数的std::map重载。您使用PLAIN_ARRAY的示例不会调用任何构造函数,因为这是aggregate initialization的一种形式,它只涉及按值初始化某些int,初始化int不需要构造函数调用。

在CUDA中,不可能对存储在GPU上的全局变量使用任何类型的动态初始化,例如__device____constant__变量,这意味着对象的初始值必须是在编译时已知,并且不仅在调用构造函数后在运行时生成。

另一个问题是,即使在可以在设备代码中调用构造函数的上下文中,您也无法调用std::map的构造函数,因为它是C ++标准库的一部分,它没有{{ 1}}构造函数,也没有任何其他__device__成员函数,因此它只能在主机代码中使用。 CUDA运行时没有为C ++ STL类定义任何类型的设备功能。即使您从主机内存到GPU内存设置__device__ cudaMemcpy(),您也无法使用该对象,因为它的所有成员函数都是std::map函数,没有__host__对应项,其次,__device__将在内部包含引用动态分配的主机内存的指针成员变量,这些内存不是GPU上的有效内存地址。

另一种方法是使用结构的普通数组而不是地图,例如:

std::map

但是,与__device__ const struct { unsigned char byte; const char word[10]; } BYTES_TO_WORDS[] = { {0xB0, "zero"}, {0xB1, "one"}, {0xB2, "two"}, {0xB3, "three"} }; 不同,您必须手动实现按键查找值。

  

我只是希望能够创建和初始化只读std::map从CPU和GPU代码访问它

不幸的是,这并非易事,因为您无法将变量定义为std::map__device__。要从主机代码访问__host__变量,您必须使用__device__,与仅正常访问变量相比,这是非常尴尬的。因此,您可能最终必须在主机内存中定义常量,然后将常量从主机内存复制到设备内存:

cudaMemcpyFromSymbol()

另一种方法是使用预处理器定义来跨两个数组有效地复制和粘贴相同的初始化程序,而不是在运行时复制数据。无论如何,需要两个独立的阵列。