Массив структур массивов CUDA C - PullRequest
0 голосов
/ 29 июня 2019

Я довольно новичок в CUDA, и я искал создание и создание массива структур массивов, и я нашел пару решений, но ни одно из них не дает мне ясного представления.

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

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

что я не понял из этих 2 кодов и как я смогу объединить эти идеи вместе?
что я пробовал (попытка с массивом из 2 структур с 1 массивом в каждом)):

#include <stdio.h>
#include <stdlib.h>
#define N 10
__inline __host__ void gpuAssert(cudaError_t code, char *file, int line, 
    bool abort=true)
{
if (code != cudaSuccess) 
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code),
file, line);
if (abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }

typedef struct StructA {
    int* arr;
} StructA;

__global__ void kernel2(StructA *in)
{
    in[0].arr[threadIdx.x] = 0;
    in[1].arr[threadIdx.x] = 1;
    printf("d_arr = %d , d_arr2 = %d \n",in[0].arr[threadIdx.x],in[1].arr[threadIdx.x]);
}



int main(){
int* h_arr;
int* h_arr2;
h_arr = (int*)malloc(N*sizeof(int));
h_arr2 = (int*)malloc(N*sizeof(int));
StructA *h_a;
h_a = (StructA*)malloc(sizeof(StructA) * 2);
int *d_arr;
int *d_arr2;
h_arr[0]=1;h_arr[1]=2;h_arr[2]=3,h_arr[3]=4,h_arr[4]=5;h_arr[5]=6;h_arr[6]=7;h_arr[7]=8;h_arr[8]=9;h_arr[9]=10;
h_arr2[0]=1;h_arr2[1]=2;h_arr2[2]=3,h_arr2[3]=4,h_arr2[4]=5;h_arr2[5]=6;h_arr2[6]=7;h_arr2[7]=8;h_arr2[8]=9;h_arr2[9]=10;
// 1. Allocate device array.
gpuErrchk(cudaMalloc((void**) &(d_arr), sizeof(int)*N));
gpuErrchk(cudaMalloc((void**) &(d_arr2), sizeof(int)*N));

// 2. Copy array contents from host to device.
gpuErrchk(cudaMemcpy(d_arr, h_arr, sizeof(int)*N, cudaMemcpyHostToDevice));
gpuErrchk(cudaMemcpy(d_arr2, h_arr2, sizeof(int)*N, cudaMemcpyHostToDevice));

// 3. Point to device pointer in host struct.
h_a[0].arr = d_arr;
h_a[1].arr = d_arr2;

// 4. Call kernel with host struct as argument
kernel2<<<1,N>>>(h_a);
gpuErrchk(cudaPeekAtLastError());
//gpuErrchk(cudaDeviceSynchronize());
// 5. Copy pointer from device to host.
gpuErrchk(cudaMemcpy(h_arr, d_arr, sizeof(int)*N, cudaMemcpyDeviceToHost));

// 6. Point to host pointer in host struct 
//    (or do something else with it if this is not needed)
//h_a.arr = h_arr;
printf("\n%d %d %d %d %d %d %d %d %d %d \n",h_arr[0],h_arr[1],h_arr[2],h_arr[3],h_arr[4],h_arr[5],h_arr[6],h_arr[7],h_arr[8],h_arr[9]);
printf("\n%d %d %d %d %d %d %d %d %d %d \n",h_arr2[0],h_arr2[1],h_arr2[2],h_arr2[3],h_arr2[4],h_arr2[5],h_arr2[6],h_arr2[7],h_arr2[8],h_arr2[9]);
return 0;
}

Ответы [ 2 ]

1 голос
/ 29 июня 2019

Ваш код в основном правильный.

Основной принцип CUDA заключается в том, что вы не можете (не должны) разыменовывать указатель хоста в коде устройства или указатель устройства в коде хоста.

Это указатель хоста:

StructA *h_a;
h_a = (StructA*)malloc(sizeof(StructA) * 2);

Это передает его коду устройства (где он будет разыменован):

kernel2<<<1,N>>>(h_a);

Мы можем исправить это с помощью некоторого дополнительного кода, чтобы скопировать структуры, на которые указывает h_a, в память устройства в новом наборе структур, выделенных d_a, с соответствующим изменением вызова ядра:

// 3a. Copy host structs to device
StructA *d_a;
cudaMalloc(&d_a, sizeof(StructA)*2);
cudaMemcpy(d_a, h_a, sizeof(StructA)*2, cudaMemcpyHostToDevice);


// 4. Call kernel with device struct as argument
kernel2<<<1,N>>>(d_a);

Вот полный пример:

$ cat t4.cu
#include <stdio.h>
#include <stdlib.h>
#define N 10
__inline __host__ void gpuAssert(cudaError_t code, const char *file, int line,
    bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code),
file, line);
if (abort) exit(code);
}
}
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }

typedef struct StructA {
    int* arr;
} StructA;

__global__ void kernel2(StructA *in)
{
    in[0].arr[threadIdx.x] = 0;
    in[1].arr[threadIdx.x] = 1;
    printf("d_arr = %d , d_arr2 = %d \n",in[0].arr[threadIdx.x],in[1].arr[threadIdx.x]);
}



int main(){
int* h_arr;
int* h_arr2;
h_arr = (int*)malloc(N*sizeof(int));
h_arr2 = (int*)malloc(N*sizeof(int));
StructA *h_a;
h_a = (StructA*)malloc(sizeof(StructA) * 2);
int *d_arr;
int *d_arr2;
h_arr[0]=1;h_arr[1]=2;h_arr[2]=3,h_arr[3]=4,h_arr[4]=5;h_arr[5]=6;h_arr[6]=7;h_arr[7]=8;h_arr[8]=9;h_arr[9]=10;
h_arr2[0]=1;h_arr2[1]=2;h_arr2[2]=3,h_arr2[3]=4,h_arr2[4]=5;h_arr2[5]=6;h_arr2[6]=7;h_arr2[7]=8;h_arr2[8]=9;h_arr2[9]=10;
// 1. Allocate device array.
gpuErrchk(cudaMalloc((void**) &(d_arr), sizeof(int)*N));
gpuErrchk(cudaMalloc((void**) &(d_arr2), sizeof(int)*N));

// 2. Copy array contents from host to device.
gpuErrchk(cudaMemcpy(d_arr, h_arr, sizeof(int)*N, cudaMemcpyHostToDevice));
gpuErrchk(cudaMemcpy(d_arr2, h_arr2, sizeof(int)*N, cudaMemcpyHostToDevice));

// 3. Point to device pointer in host struct.
h_a[0].arr = d_arr;
h_a[1].arr = d_arr2;

// 3a. Copy host structs to device
StructA *d_a;
cudaMalloc(&d_a, sizeof(StructA)*2);
cudaMemcpy(d_a, h_a, sizeof(StructA)*2, cudaMemcpyHostToDevice);


// 4. Call kernel with device struct as argument
kernel2<<<1,N>>>(d_a);
gpuErrchk(cudaPeekAtLastError());
//gpuErrchk(cudaDeviceSynchronize());
// 5. Copy pointer from device to host.
gpuErrchk(cudaMemcpy(h_arr, d_arr, sizeof(int)*N, cudaMemcpyDeviceToHost));

// 6. Point to host pointer in host struct
//    (or do something else with it if this is not needed)
//h_a.arr = h_arr;
printf("\n%d %d %d %d %d %d %d %d %d %d \n",h_arr[0],h_arr[1],h_arr[2],h_arr[3],h_arr[4],h_arr[5],h_arr[6],h_arr[7],h_arr[8],h_arr[9]);
printf("\n%d %d %d %d %d %d %d %d %d %d \n",h_arr2[0],h_arr2[1],h_arr2[2],h_arr2[3],h_arr2[4],h_arr2[5],h_arr2[6],h_arr2[7],h_arr2[8],h_arr2[9]);
return 0;
}
$ nvcc -o t4 t4.cu
$ ./t4
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1
d_arr = 0 , d_arr2 = 1

0 0 0 0 0 0 0 0 0 0

1 2 3 4 5 6 7 8 9 10
$

Обратите внимание, что в последних строках распечатки не отображается второй массив, обновленный на хосте, поскольку вы не скопировали этот массив обратно из памяти устройства в память хоста (после кода вашего ядра есть только один оператор cudaMemcpy) , Вы можете исправить это с помощью другого оператора cudaMemcpy. Я также добавил const к вашему gpuAssert, чтобы избавиться от надоедливых предупреждений компилятора.

Этот ответ может дать вам некоторые другие идеи о том, как обращаться с массивами указателей.

0 голосов
/ 29 июня 2019

В своем коде вы передаете h_a ядру. h_a - это массив C на стороне хоста. Эти массивы распадаются на указатели на свои первые элементы при передаче в качестве параметров функциям; см:

Что такое распадающийся массив?

Итак, ваше ядро ​​получает адрес StructA на стороне хоста - и он не может его использовать. Вы могли бы:

  • Скопируйте h_a на сторону устройства (скажем, на d_a) и используйте это - затухание было бы хорошо, так как это адрес на стороне устройства, на который вы будете подписывать.
  • Используйте фиксированный размер std::array, который не затухает.
  • Выделите h_a для доступа также с устройства - используя cudaMallocManaged(). См. эту презентацию для получения дополнительной информации.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...