CUDA global (как в C) динамические массивы, выделенные в память устройства

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

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

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

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

__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 ответов


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

#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;
}

дать ему шанс.


потратьте некоторое время на обширную документацию, предлагаемую NVIDIA.

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

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

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

__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. При вызове ядра из вашего процессора за кулисами происходит то, что указатели и примитивы копируются в регистры 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++ ' для этого примера.


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

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

однако, я думаю, что это смущает компилятор, поскольку теперь я получаю "Advisory: не могу сказать, на что указывает указатель, предполагая глобальное пространство памяти" всякий раз, когда я пытаюсь получить доступ к глобальным данным. К счастью, предположение оказывается правильным, но предупреждения раздражают.

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


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

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

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


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


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

-

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

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