1

我正在尝试静态初始化 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 代码访问它。如果你能告诉我如何正确地做到这一点,我将不胜感激。

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

4

1 回答 1

2

初始化诸如 an 之类的 C++ 对象std::map涉及在运行时调用构造函数。您用于初始化std::maps 的 C++11 语法是一种列表初始化形式,它调用' 构造函数的std::initializer_list重载。std::map您的示例 withPLAIN_ARRAY不调用任何构造函数,因为这是聚合初始化的一种形式,仅涉及int按值初始化一些 s ,并且初始化 anint不需要构造函数调用。

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

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

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

__device__
const struct {
    unsigned char byte;
    const char word[10];
} BYTES_TO_WORDS[] = {
    {0xB0, "zero"},
    {0xB1, "one"},
    {0xB2, "two"},
    {0xB3, "three"}
};

但是,与 with 不同std::map,您必须手动实现通过键查找值。


我只想能够创建和初始化一个只读std::map从 CPU 和 GPU 代码访问它

不幸的是,这不是微不足道的,因为您不能将变量同时定义为__device__and __host__。要从主机代码访问__device__变量,您必须使用cudaMemcpyFromSymbol(),这与像正常访问变量相比非常尴尬。因此,您最终可能不得不在主机内存中定义常量,然后将常量从主机内存复制到设备内存:

const byte_word BYTES_TO_WORDS[] = {
    {0xB0, "zero"},
    // ...
};

// uninitialized array
__device__
byte_word DEV_BYTES_TO_WORDS[sizeof BYTES_TO_WORDS / sizeof(byte_word)];

// at startup, use `cudaMemCpyToSymbol()` to populate `DEV_BYTES_TO_WORDS`
// from `BYTES_TO_WORDS`.

另一种方法是使用预处理器定义在两个数组中有效地复制和粘贴相同的初始化程序,而不是在运行时复制数据。在任何情况下,都需要两个单独的数组。

于 2018-04-05T16:32:02.847 回答