CUDA глобальные (как в C) динамические массивы, выделенные для памяти устройства - PullRequest
7 голосов
/ 17 сентября 2008

Итак, я пытаюсь написать код, который использует архитектуру Nvidia CUDA. Я заметил, что копирование на устройство и с него действительно повлияло на мою общую производительность, поэтому сейчас я пытаюсь переместить большой объем данных на устройство.

Поскольку эти данные используются во многих функциях, я бы хотел, чтобы они были глобальными. Да, я могу обмениваться указателями, но мне бы очень хотелось знать, как работать с глобальными переменными в этом случае.

Итак, у меня есть функции устройства, которые хотят получить доступ к массиву, выделенному для устройства.

В идеале я мог бы сделать что-то вроде:

__device__ float* global_data;

main()
{
  cudaMalloc(global_data);
  kernel1<<<blah>>>(blah); //access global data
  kernel2<<<blah>>>(blah); //access global data again
}

Однако я не понял, как создать динамический массив. Я нашел способ обойти это, объявив массив следующим образом:

__device__ float global_data[REALLY_LARGE_NUMBER];

И хотя для этого не требуется вызов cudaMalloc, я бы предпочел подход с динамическим распределением.

Ответы [ 6 ]

5 голосов
/ 17 сентября 2008

Что-то вроде этого, вероятно, должно работать.

#include <algorithm>

#define NDEBUG
#define CUT_CHECK_ERROR(errorMessage) do {                                 \
        cudaThreadSynchronize();                                           \
         cudaError_t err = cudaGetLastError();                             \
         if( cudaSuccess != err) {                                         \
                     fprintf(stderr, "Cuda error: %s in file '%s' in line %i : %s.\n",    \
                                             errorMessage, __FILE__, __LINE__, cudaGetErrorString( err) );\
                     exit(EXIT_FAILURE);                                                  \
                 } } while (0)


__device__ float *devPtr;

__global__
void kernel1(float *some_neat_data)
{
    devPtr = some_neat_data;
}

__global__
void kernel2(void)
{
    devPtr[threadIdx.x] *= .3f;
}


int main(int argc, char *argv[])
{
    float* otherDevPtr;
    cudaMalloc((void**)&otherDevPtr, 256 * sizeof(*otherDevPtr));
    cudaMemset(otherDevPtr, 0, 256 * sizeof(*otherDevPtr));

    kernel1<<<1,128>>>(otherDevPtr);
    CUT_CHECK_ERROR("kernel1");

    kernel2<<<1,128>>>();

    CUT_CHECK_ERROR("kernel2");

    return 0;
}

Дай вихрь.

1 голос
/ 18 сентября 2008

Я попытался решить, как выделить временный указатель и передать его простой глобальной функции, аналогичной kernel1.

Хорошая новость в том, что она работает :) 1003 *

Тем не менее, я думаю, что это смущает компилятор, так как теперь я получаю «Рекомендация: не могу сказать, на что указывает указатель, предполагая глобальное пространство памяти», когда я пытаюсь получить доступ к глобальным данным. К счастью, предположение оказывается верным, но предупреждения раздражают.

Во всяком случае, для справки - я посмотрел на многие примеры и выполнил упражнения nvidia, в которых главное - получить вывод «Правильно!». Однако я не посмотрел всех из них. Если кто-нибудь знает пример sdk, в котором выполняется динамическое глобальное распределение памяти устройства, я все равно хотел бы знать.

1 голос
/ 17 сентября 2008

Потратьте некоторое время, сосредотачиваясь на обильной документации, предлагаемой NVIDIA.

Из Руководства по программированию:

float* devPtr;
cudaMalloc((void**)&devPtr, 256 * sizeof(*devPtr));
cudaMemset(devPtr, 0, 256 * sizeof(*devPtr));

Это простой пример того, как выделить память. Теперь в ваших ядрах вы должны принять указатель на число с плавающей точкой, например:

__global__
void kernel1(float *some_neat_data)
{
    some_neat_data[threadIdx.x]++;
}

__global__
void kernel2(float *potentially_that_same_neat_data)
{
    potentially_that_same_neat_data[threadIdx.x] *= 0.3f;
}

Так что теперь вы можете вызывать их так:

float* devPtr;
cudaMalloc((void**)&devPtr, 256 * sizeof(*devPtr));
cudaMemset(devPtr, 0, 256 * sizeof(*devPtr));

kernel1<<<1,128>>>(devPtr);
kernel2<<<1,128>>>(devPtr);

Поскольку эти данные используются в многочисленных функции, я хотел бы, чтобы это было глобальный характер.

Есть несколько веских причин для использования глобалов. Это определенно не один. Я оставлю это в качестве упражнения, чтобы расширить этот пример, чтобы включить перемещение devPtr в глобальную область.

EDIT:

Хорошо, фундаментальная проблема заключается в следующем: ваши ядра могут получать доступ только к памяти устройства, и единственные указатели глобальной области видимости, которые они могут использовать, - это графические процессоры. При вызове ядра из вашего ЦП, за кулисами происходит то, что указатели и примитивы копируются в регистры GPU и / или разделяемую память до того, как ядро ​​будет выполнено.

Итак, самое близкое, что я могу предложить, это использовать cudaMemcpyToSymbol () для достижения ваших целей. Но, на заднем плане, подумайте, что другой подход может быть правильным.

#include <algorithm>

__constant__ float devPtr[1024];

__global__
void kernel1(float *some_neat_data)
{
    some_neat_data[threadIdx.x] = devPtr[0] * devPtr[1];
}

__global__
void kernel2(float *potentially_that_same_neat_data)
{
    potentially_that_same_neat_data[threadIdx.x] *= devPtr[2];
}


int main(int argc, char *argv[])
{
    float some_data[256];
    for (int i = 0; i < sizeof(some_data) / sizeof(some_data[0]); i++)
    {
        some_data[i] = i * 2;
    }
    cudaMemcpyToSymbol(devPtr, some_data, std::min(sizeof(some_data), sizeof(devPtr) ));
    float* otherDevPtr;
    cudaMalloc((void**)&otherDevPtr, 256 * sizeof(*otherDevPtr));
    cudaMemset(otherDevPtr, 0, 256 * sizeof(*otherDevPtr));

    kernel1<<<1,128>>>(otherDevPtr);
    kernel2<<<1,128>>>(otherDevPtr);

    return 0;
}

Не забудьте для этого примера '--host-compilation = c ++'.

0 голосов
/ 18 августа 2010

Поскольку эти данные используются во многих функциях, я бы хотел, чтобы они были глобальными.

-

Есть несколько веских причин для использования глобалов. Это определенно не один. Я оставлю это как упражнение для расширения этого примера, чтобы включить перемещение "devPtr" в глобальную область.

Что если ядро ​​работает с большой константной структурой, состоящей из массивов? Использование так называемой постоянной памяти не вариант, потому что она очень ограничена по размеру ... так что тогда вы должны поместить ее в глобальную память ..

0 голосов
/ 18 сентября 2008

проверьте образцы, включенные в SDK. Многие из этих примеров проектов - хороший способ учиться на собственном примере.

0 голосов
/ 17 сентября 2008

Хм, именно эта проблема с перемещением devPtr в глобальную область была моей проблемой.

У меня есть реализация, которая делает именно это, с двумя ядрами, имеющими указатель на передаваемые данные. Я явно не хочу передавать эти указатели.

Я достаточно внимательно прочитал документацию и заглянул на форумы nvidia (и Google искал около часа), но я не нашел реализации глобального динамического массива устройств, который действительно работает (я пробовал несколько которые компилируются, а затем терпят неудачу новыми и интересными способами).

...