Копирование массива указателей в память устройства и обратно (CUDA)

Я пытаюсь использовать cublas функцию cublasSgemmBatched в моем игрушечном примере. В этом примере я сначала выделяю 2D-массивы: h_AA, h_BB размером [6][5] и h_CC размером [6][1]. После этого я скопировал его на устройство, выполнил cublasSgemmBatched и попытался скопировать массив d_CC обратно на хост-массив h_CC. Однако я получил ошибку (cudaErrorLaunchFailure) с копированием устройства на хост, и я не уверен, что правильно скопировал массивы в устройство:

int main(){
    cublasHandle_t handle;
    cudaError_t cudaerr;
    cudaEvent_t start, stop;
    cublasStatus_t stat;
    const float alpha = 1.0f;
    const float beta = 0.0f;
    float **h_AA, **h_BB, **h_CC;
    h_AA = new float*[6];
    h_BB = new float*[6];
    h_CC = new float*[6];
    for (int i = 0; i < 6; i++){
        h_AA[i] = new float[5];
        h_BB[i] = new float[5];
        h_CC[i] = new float[1];
        for (int j = 0; j < 5; j++){
            h_AA[i][j] = j;
            h_BB[i][j] = j;
        }
        h_CC[i][0] = 1;
    }
    float **d_AA, **d_BB, **d_CC;
    cudaMalloc(&d_AA, 6 * sizeof(float*));
    cudaMalloc(&d_BB, 6 * sizeof(float*));
    cudaMalloc(&d_CC, 6 * sizeof(float*));
    cudaerr = cudaMemcpy(d_AA, h_AA, 6 * sizeof(float*), cudaMemcpyHostToDevice);
    cudaerr = cudaMemcpy(d_BB, h_BB, 6 * sizeof(float*), cudaMemcpyHostToDevice);
    cudaerr = cudaMemcpy(d_CC, h_CC, 6 * sizeof(float*), cudaMemcpyHostToDevice);
    stat = cublasCreate(&handle);
    stat = cublasSgemmBatched(handle, CUBLAS_OP_N, CUBLAS_OP_N, 1, 1, 5, &alpha,
             (const float**)d_AA, 1, (const float**)d_BB, 5, &beta, d_CC, 1, 6);
    cudaerr = cudaMemcpy(h_CC, d_CC, 6 * sizeof(float*), cudaMemcpyDeviceToHost);
    cublasDestroy(handle);
}

Итак, этот код работает, однако последний cudaerr возвращает cudaErrorLaunchFailure. Я пытался следовать этому примеру кода на Github.

Спасибо

P.S. Что-то я не понимаю, что такое sizeof(float*) и откуда cudaMalloc знает, сколько памяти требуется для каждого массива (как здесь я определяю размер только 1 измерения).

ОБНОВЛЕНИЕ: Я сделал это!!:

cublasHandle_t handle;
cudaError_t cudaerr;
cudaEvent_t start, stop;
cublasStatus_t stat;
const float alpha = 1.0f;
const float beta = 0.0f;

float *h_A = new float[5];
float *h_B = new float[5];
float *h_C = new float[6];
for (int i = 0; i < 5; i++)
{
    h_A[i] = i;
    h_B[i] = i;
}



float **h_AA, **h_BB, **h_CC;
h_AA = (float**)malloc(6* sizeof(float*));
h_BB = (float**)malloc(6 * sizeof(float*));
h_CC = (float**)malloc(6 * sizeof(float*));
for (int i = 0; i < 6; i++){
    cudaMalloc((void **)&h_AA[i], 5 * sizeof(float));
    cudaMalloc((void **)&h_BB[i], 5 * sizeof(float));
    cudaMalloc((void **)&h_CC[i], sizeof(float));
    cudaMemcpy(h_AA[i], h_A, 5 * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(h_BB[i], h_B, 5 * sizeof(float), cudaMemcpyHostToDevice);
}
float **d_AA, **d_BB, **d_CC;
cudaMalloc(&d_AA, 6 * sizeof(float*));
cudaMalloc(&d_BB, 6 * sizeof(float*));
cudaMalloc(&d_CC, 6 * sizeof(float*));
cudaerr = cudaMemcpy(d_AA, h_AA, 6 * sizeof(float*), cudaMemcpyHostToDevice);
cudaerr = cudaMemcpy(d_BB, h_BB, 6 * sizeof(float*), cudaMemcpyHostToDevice);
cudaerr = cudaMemcpy(d_CC, h_CC, 6 * sizeof(float*), cudaMemcpyHostToDevice);
stat = cublasCreate(&handle);
    stat = cublasSgemmBatched(handle, CUBLAS_OP_N, CUBLAS_OP_N, 1, 1, 5, &alpha, 
             (const float**)d_AA, 1, (const float**)d_BB, 5, &beta, d_CC, 1, 6);
    cudaerr = cudaMemcpy(h_CC, d_CC, sizeof(float), cudaMemcpyDeviceToHost);
    for (int i = 0; i < 6;i++)
        cudaMemcpy(h_C+i, h_CC[i], sizeof(float), cudaMemcpyDeviceToHost);
cublasDestroy(handle);

person Mikhail Genkin    schedule 13.01.2015    source источник
comment
Перепутанные данные, которые вы передаете, вызывают сбой одного из ядер, запущенных пакетным вызовом gemm. Как асинхронная ошибка, вы можете не получать уведомления об этом до следующего вызова cuda. Вы изучили пример кода cuda пакетной обработки?   -  person Robert Crovella    schedule 14.01.2015
comment
я не делала, делаю это прямо сейчас   -  person Mikhail Genkin    schedule 14.01.2015
comment
Я сделал это! Спасибо. Итак, правильно ли я понимаю: чтобы иметь дело с массивами 2D-устройств, вы должны создать хост-массив указателей на массивы устройств, а затем скопировать этот массив в память массива 2D-устройств. Чтобы получить массив 2D-хостов из массива 2D-устройств, вы должны снова использовать промежуточный 2D-массив, который представляет собой массив указателей хостов на массивы устройств. Я разместил рабочий код в обновлении   -  person Mikhail Genkin    schedule 14.01.2015
comment
Да, это пример потребности в механизме глубокого копирования, который был бы похож на то, что вам пришлось бы делать, если бы вы хотели скопировать матрицу на устройство и иметь возможность получить к ней прямой доступ, используя нотацию с двойным индексом. То, что вы называете массивами 2D-устройств, по-прежнему является линейным/сглаженным массивом. Аспект 2D или глубокого копирования возникает потому, что у вас есть массив этих массивов, который вы хотите передать на устройство, и это аналогично механизму глубокого копирования, необходимому для передачи массива с двойным индексом. Почему бы вам не опубликовать свое обновление в качестве ответа. Это нормально, чтобы ответить на свой вопрос   -  person Robert Crovella    schedule 14.01.2015


Ответы (1)


Итак, я понял ответ (спасибо @Robert Crovella): чтобы создать device array of pointers to device arrays (для пакетных функций), нужно сначала создать host array of pointers to device arrays, а затем скопировать его в device array of pointers to device arrays. То же самое и с передачей обратно на хост: нужно использовать промежуточный host array of pointers to device arrays.

cublasHandle_t handle;
cudaError_t cudaerr;
cudaEvent_t start, stop;
cublasStatus_t stat;
const float alpha = 1.0f;
const float beta = 0.0f;

float *h_A = new float[5];
float *h_B = new float[5];
float *h_C = new float[6];
for (int i = 0; i < 5; i++)
{
    h_A[i] = i;
    h_B[i] = i;
}



float **h_AA, **h_BB, **h_CC;
h_AA = (float**)malloc(6* sizeof(float*));
h_BB = (float**)malloc(6 * sizeof(float*));
h_CC = (float**)malloc(6 * sizeof(float*));
for (int i = 0; i < 6; i++){
    cudaMalloc((void **)&h_AA[i], 5 * sizeof(float));
    cudaMalloc((void **)&h_BB[i], 5 * sizeof(float));
    cudaMalloc((void **)&h_CC[i], sizeof(float));
    cudaMemcpy(h_AA[i], h_A, 5 * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(h_BB[i], h_B, 5 * sizeof(float), cudaMemcpyHostToDevice);
}
float **d_AA, **d_BB, **d_CC;
cudaMalloc(&d_AA, 6 * sizeof(float*));
cudaMalloc(&d_BB, 6 * sizeof(float*));
cudaMalloc(&d_CC, 6 * sizeof(float*));
cudaerr = cudaMemcpy(d_AA, h_AA, 6 * sizeof(float*), cudaMemcpyHostToDevice);
cudaerr = cudaMemcpy(d_BB, h_BB, 6 * sizeof(float*), cudaMemcpyHostToDevice);
cudaerr = cudaMemcpy(d_CC, h_CC, 6 * sizeof(float*), cudaMemcpyHostToDevice);
stat = cublasCreate(&handle);
    stat = cublasSgemmBatched(handle, CUBLAS_OP_N, CUBLAS_OP_N, 1, 1, 5, &alpha, 
             (const float**)d_AA, 1, (const float**)d_BB, 5, &beta, d_CC, 1, 6);
    cudaerr = cudaMemcpy(h_CC, d_CC, sizeof(float), cudaMemcpyDeviceToHost);
    for (int i = 0; i < 6;i++)
        cudaMemcpy(h_C+i, h_CC[i], sizeof(float), cudaMemcpyDeviceToHost);
cublasDestroy(handle);
person Mikhail Genkin    schedule 13.01.2015