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, состоящей из массивов? Использование так называемой постоянной памяти не является вариантом, потому что это очень ограниченный в размере.. поэтому вы должны поместить его в глобальную память..?