CUDA выделяет массив массивов - PullRequest
5 голосов
/ 02 декабря 2009

У меня возникли проблемы с выделением массива массивов в CUDA.

void ** data;
cudaMalloc(&data, sizeof(void**)*N); // allocates without problems
for(int i = 0; i < N; i++) {
    cudaMalloc(data + i, getSize(i) * sizeof(void*)); // seg fault is thrown
}

Что я не так сделал?

Ответы [ 6 ]

11 голосов
/ 11 декабря 2009

Необходимо выделить указатели для памяти хоста, затем выделить память устройства для каждого массива и сохранить его указатель в памяти хоста. Затем выделите память для хранения указателей в устройстве а затем скопируйте память хоста в память устройства. Один пример стоит 1000 слов:

__global__ void multi_array_kernel( int N, void** arrays ){
    // stuff
}


int main(){

    const int N_ARRAYS = 20;
    void *h_array = malloc(sizeof(void*) * N_ARRAYS);
    for(int i = 0; i < N_ARRAYS; i++){
        cudaMalloc(&h_array[i], i * sizeof(void*));
        //TODO: check error
    }
    void *d_array = cudaMalloc(sizeof(void*) * N_ARRAYS);

    // Copy to device Memory
    cudaMemcpy(d_array, h_array, sizeof(void*) * N_ARRAYS, cudaMemcpyHostToDevice);

    multi_array_kernel<1,1>(N_ARRAYS, d_array);
    cudaThreadSynchronize();

    for(int i = 0; i < N_ARRAYS; i++){
        cudaFree(h_array[i]); //host not device memory
        //TODO: check error
    }
    cudaFree(d_array);
    free(h_array);
}
4 голосов
/ 03 декабря 2009

Я не верю, что это поддерживается. cudaMalloc() выделяет память устройства, но сохраняет адрес в переменной на хосте. В вашем цикле for вы передаете его адреса в память устройства.

В зависимости от того, что вы пытаетесь выполнить, вы можете выделить data с обычным хостом malloc() перед вызовом цикла for, как он у вас есть. Или выделите один большой блок памяти устройства и вычислите смещения в нем вручную.

Посмотрите разделы 2.4, 3.2.1 и B.2.5 (внизу) Руководства по программированию CUDA *1009* для более подробного обсуждения этого. В частности, в нижней части страницы 108:

Адрес, полученный путем взятия адреса __device__, __shared__ или Переменная __constant__ может использоваться только в коде устройства.

2 голосов
/ 11 октября 2010

Я думаю, что в первом цикле это должно быть &h_array[i], а не &d_array[i].

1 голос
/ 24 августа 2016

У меня была такая же проблема, и мне удалось ее решить.

Ответ FabrizioM был хорошим началом для меня и очень помог мне. Но, тем не менее, я столкнулся с некоторыми проблемами, когда попытался перенести код в свой проект. Используя дополнительные комментарии и посты, я смог написать рабочий пример (VS2012, CUDA7.5). Таким образом, я опубликую свой код как дополнительный ответ и как точку, чтобы начать для других.

Чтобы понять наименование: я использую вектор OpenCV cv :: Mat в качестве входных данных, которые снимаются с нескольких камер, и я обрабатываю эти изображения в ядре.

     void TransferCameraImageToCuda(const std::vector<cv::Mat*>* Images)
{

     int NumberCams     = Images->size();
     int imageSize      = Images->at(0)->cols*Images->at(0)->rows;

     CUdeviceptr*           CamArraysAdressOnDevice_H;
     CUdeviceptr*           CamArraysAdressOnDevice_D;


         //allocate memory on host to store the device-address of each array
         CamArraysAdressOnDevice_H = new CUdeviceptr[NumberCams];

         // allocate memory on the device and store the arrays on the device 
         for (int i = 0; i < NumberCams; i++){
             cudaMalloc((void**)&(CamArraysAdressOnDevice_H[i]), imageSize * sizeof(unsigned short));
             cudaMemcpy((void*)CamArraysAdressOnDevice_H[i], Images->at(i)->data, imageSize * sizeof(unsigned short), cudaMemcpyHostToDevice);
         }

         // allocate memory on the device to store the device-adresses of the arrays
         cudaMalloc((void**)&CamArraysAdressOnDevice_D, sizeof(CUdeviceptr*)* NumberCams);

         // Copy the adress of each device array to the device
         cudaMemcpy(CamArraysAdressOnDevice_D, CamArraysAdressOnDevice_H, sizeof(CUdeviceptr*)* NumberCams, cudaMemcpyHostToDevice);




}

При запуске ядра я передаю указатель устройства на указатель типа данных (беззнаковое короткое **)

DummyKernel<<<gridDim,blockDim>>>(NumberCams, (unsigned short**) CamArraysAdressOnDevice_D)

и определение ядра, например:

__global__ void DummyKernel(int NumberImages, unsigned short** CamImages)
{
    int someIndex = 3458;
    printf("Value Image 0 : %d \n", CamImages[0][someIndex]);
    printf("Value Image 1 : %d \n", CamImages[1][someIndex]);
    printf("Value Image 2 : %d \n", CamImages[2][someIndex]);
}
1 голос
/ 10 февраля 2012

Аналогичная тема на форумах nvidia - http://forums.nvidia.com/index.php?showtopic=69403&st=20

1 голос
/ 05 июля 2011

вы не можете использовать

cudaMalloc(&h_array[i], i * sizeof(void*));

для массива, объявленного как void *

использовать определенный тип данных

CUdeviceptr *h_array = malloc(sizeof(CUdeviceptr *) * N);

или

int *h_array = malloc(sizeof(int *) * N);

и приведите его к void *

cudaMalloc((void *)&h_array[i], i * sizeof(void*));
...