CUDA: как выделить память для данных члена класса

Предположим, у меня есть этот класс:

class Particle
{
    double *_w;
};

И я хочу отправлять объекты из Particle к моему ядру. Выделить пространство для этих объектов легко:

Particle *dev_p;
cudaStatus = cudaMalloc((void**)&dev_P, nParticles * sizeof(Particle));
if (cudaStatus != cudaSuccess) {
    fprintf(stderr, "cudaMalloc failed!");
    goto Error;
}

Также предположим, что nParticles равен 100. Теперь мне нужно выделить 300 двойных для каждого _w в Particle объект. Как я могу это сделать? Я попробовал этот код:

for( int i = 0; i < nParticles; i++){
    cudaStatus = cudaMalloc((void**)&(dev_P[i]._w), 300 * sizeof(double));
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaMalloc failed!");
        goto Error;
    }
}

Но отладка с помощью Nsight прекращается, когда я получаю доступ к dev_p[i]._w[j] .

2 ответа

Решение

Возможно, вам следует включить полный простой пример. (Если я компилирую ваш код выше и запускаю его самостоятельно, в Linux, я получаю ошибку seg во второй операции cudaMalloc). Одна морщина, которую я вижу, состоит в том, что, поскольку на первом этапе вы выделили объекты частиц в память устройства, когда вы собираетесь выделить _w указатели, вы передаете указатель на cudaMalloc, который уже находится в памяти устройства. Вы должны передать указатель на основе хоста в cudaMalloc, который он затем назначит выделенной области в (глобальной) памяти устройства.

Одно из возможных решений, которое, я думаю, соответствует тому, что я вижу в вашем примере:

#include <stdio.h>

#define cudaCheckErrors(msg) \
    do { \
        cudaError_t __err = cudaGetLastError(); \
        if (__err != cudaSuccess) { \
            fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                msg, cudaGetErrorString(__err), \
                __FILE__, __LINE__); \
            fprintf(stderr, "*** FAILED - ABORTING\n"); \
            exit(1); \
        } \
    } while (0)

class Particle
{
    public:
    double *_w;
};

__global__ void test(Particle *p){

  int idx=threadIdx.x + blockDim.x*blockIdx.x;

  if (idx == 2){
    printf("dev_p[2]._w[2] = %f\n", p[idx]._w[2]);
    }
}


int main() {
  int nParticles=100;
  Particle *dev_p;
  double *w[nParticles];
  cudaMalloc((void**)&dev_p, nParticles * sizeof(Particle));
  cudaCheckErrors("cudaMalloc1 fail");

  for( int i = 0; i < nParticles; i++){
    cudaMalloc((void**)&(w[i]), 300 * sizeof(double));
    cudaCheckErrors("cudaMalloc2 fail");
    cudaMemcpy(&(dev_p[i]._w), &(w[i]), sizeof(double *), cudaMemcpyHostToDevice);
    cudaCheckErrors("cudaMemcpy1 fail");
    }
  double testval = 32.7;
  cudaMemcpy(w[2]+2, &testval, sizeof(double), cudaMemcpyHostToDevice);
  cudaCheckErrors("cudaMemcpy2 fail");
  test<<<1, 32>>>(dev_p);
  cudaDeviceSynchronize();
  cudaCheckErrors("kernel fail");
  printf("Done!\n");

}

Здесь мы создаем отдельный набор указателей на хосте для использования в целях cudaMalloc, а затем копируем эти выделенные указатели на устройство для использования в качестве указателей устройства (это допустимо для UVA).

Другой подход заключается в размещении указателей _w на стороне устройства. Это также может служить вашим целям.

Все вышеперечисленное я предполагаю cc 2.0 или выше.

Есть два способа сделать это. Первый - вы распределяете память на хосте, заполняя хост- массив объектами частиц. По завершении вы копируете хост-массив на устройство через cudaMemcpy,

Второй способ - на Ферми и выше можно звонить malloc в ядре, заполняя dev_P массив из ядра.

Другие вопросы по тегам