Два вступительных комментария - использование динамически размещенного 2D-массива - плохая идея в CUDA, а повторное выделение памяти в цикле также не является хорошей идеей. Оба несут ненужные штрафы за производительность.
Для кода хоста, что-то вроде этого:
size_t allocsize = 16000 * sizeof(float);
int n_allocations = 16;
float * dpointer
cudaMalloc((void **)&dpointer, n_allocations * size_t(allocsize));
float * dcurrent = dpointer;
int n = 0;
for(int i=0; ((n==0) && (i<n_allocations)); i++, dcurrent+=allocsize) {
// whatever you do before the kernel
kernel <<< gridsize,blocksize >>> (dcurrent,.....);
// whatever you do after the kernel
}
является предпочтительным. Здесь вы вызываете cudaMalloc только один раз и передаете смещения в выделение, что делает выделение памяти и управление свободным внутри цикла. Структура цикла также означает, что вы не можете работать бесконечно и исчерпать всю память графического процессора.
В самом вопросе о двумерном массиве есть две причины, по которым это плохая идея. Во-первых, для выделения из двумерного массива с N строками требуется (N + 1) вызовов cudaMalloc и копирование памяти хост-устройства, что является медленным и уродливым. Во-вторых, внутри кода ядра, чтобы получить ваши данные, графический процессор должен выполнить два глобальных чтения памяти, одно для перенаправления указателя, чтобы получить адрес строки, а затем одно для извлечения данных из строки. Это намного медленнее, чем эта альтернатива:
#define idx(i,j,lda) ( (j) + ((i)*(lda)) )
__global__ void kernerl(float * Mem, int lda, ....)
{
Mem[idx(0,5,lda)]=1; // MemMem[0][5]=1;
}
, который использует индексацию в одномерном распределении. В GPU транзакции с памятью очень дороги, но FLOPS и IOPS дешевы. Единственное целочисленное умножение - самый эффективный способ сделать это. Если вам нужен доступ к результатам предыдущего вызова ядра, просто передайте смещение к предыдущим результатам и используйте два указателя внутри ядра, примерно так:
__global__ void kernel(float *Mem, int lda, int this, int previous)
{
float * Mem0 = Mem + this;
float * Mem1 = Mem + previous;
}
Эффективные программы с распределенной памятью (а CUDA на самом деле является типом программирования с распределенной памятью) через некоторое время начинают выглядеть как Fortran, но это цена, которую вы платите за мобильность, прозрачность и эффективность.
Надеюсь, это помогло.