Acabo de comenzar a tratar de aprender CUDA nuevamente y encontré un código que no entiendo completamente.

// declare GPU memory pointers
float * d_in;
float * d_out;

// allocate GPU memory
cudaMalloc((void**) &d_in, ARRAY_BYTES);
cudaMalloc((void**) &d_out, ARRAY_BYTES);

Cuando se declaran los punteros de memoria de la GPU, asignan memoria en el host. Las llamadas a cudaMalloc desechan la información de que d_in y d_out son punteros a flotantes.

No puedo pensar por qué cudaMalloc necesitaría saber dónde se almacenaron originalmente d_in y d_out en la memoria del host. Ni siquiera está claro por qué necesito usar los bytes de host para almacenar cualquier dirección de host a la que apuntan d_in & d_out.

Entonces, ¿cuál es el propósito de las declaraciones de variables originales en el host?

================================================== ====================

Pensé que algo como esto tendría más sentido:

// declare GPU memory pointers
cudaFloat * d_in;
cudaFloat * d_out;

// allocate GPU memory
cudaMalloc((void**) &d_in, ARRAY_BYTES);
cudaMalloc((void**) &d_out, ARRAY_BYTES);

De esta manera, todo lo relacionado con GPU tiene lugar en la GPU. Si d_in o d_out se usan accidentalmente en el código del host, se puede generar un error en el momento de la compilación, ya que esas variables no se definirían en el host.

Supongo que lo que también encuentro confuso es que al almacenar las direcciones de memoria del dispositivo en el host, parece que el dispositivo no está completamente a cargo de administrar su propia memoria. Parece que existe el riesgo de que el código de host sobrescriba accidentalmente el valor de d_in o d_out, ya sea asignándolos accidentalmente en el código de host u otro error más sutil, lo que podría hacer que la GPU pierda el acceso a su propia memoria. Además, parece extraño que las direcciones asignadas a d_in y d_out sean elegidas por el host, en lugar del dispositivo. ¿Por qué el anfitrión debería saber algo sobre qué direcciones están / no están disponibles en el dispositivo?

¿Qué es lo que no entiendo aquí?

0
user1245262 27 mar. 2017 a las 07:58

2 respuestas

La mejor respuesta

Su falla conceptual fundamental es mezclar el código del lado del host y el código del lado del dispositivo. Si llama a cudaMalloc() desde la ejecución del código en la CPU, entonces, bueno, está en la CPU: es usted quien quiere tener los argumentos en la memoria de la CPU, y el resultado en la memoria de la CPU. Tú lo pediste. cudaMalloc le ha dicho a la GPU / dispositivo qué cantidad de su memoria (la del dispositivo) debe asignar, pero si la CPU / host desea acceder a esa memoria, necesita una forma de referirse a ella que el dispositivo comprenderá. La ubicación de la memoria en el dispositivo es una forma de hacerlo.

Alternativamente, usted puede código del lado del dispositivo ; entonces todo tiene lugar en la GPU. (Aunque, francamente, nunca lo he hecho yo mismo y no es una gran idea, excepto en casos especiales).

1
user1245262 29 mar. 2017 a las 18:28

No puedo pensar por qué cudaMalloc necesitaría saber dónde se almacenaron originalmente d_in & d_out en la memoria del host

Eso es solo el C pass by reference idiom.

Ni siquiera está claro por qué necesito usar los bytes del host para almacenar cualquier dirección de host a la que d_in & d_out apunte.

Ok, entonces diseñemos la API a tu manera. Aquí hay una secuencia típica de operaciones en el host: asigne algo de memoria en el dispositivo, copie algunos datos en esa memoria, inicie un núcleo para hacer algo en esa memoria. Puede pensar por sí mismo cómo sería posible hacer esto sin tener los punteros a la memoria asignada almacenados en una variable de host:

cudaMalloc(somebytes);
cudaMemcpy(?????, hostdata, somebytes, cudaMemcpyHOstToDevice);
kernel<<<1,1>>>(?????);

Si puede explicar qué se debe hacer con ????? si no tenemos la dirección de la asignación de memoria en el dispositivo almacenada en una variable de host, entonces realmente está en algo. Si no puede, ha deducido la razón básica por la que almacenamos la dirección de retorno de la memoria asignada en la GPU en las variables del host.

Además, debido al uso de punteros de host mecanografiados para almacenar las direcciones de las asignaciones de dispositivos, la API de tiempo de ejecución de CUDA puede hacer una verificación de tipos. Así que esto:

__global__ void kernel(double *data, int N);

// .....
int N = 1 << 20;
float * d_data;
cudaMalloc((void **)&d_data, N * sizeof(float));
kernel<<<1,1>>>(d_data, N);

Puede informar desajustes de tipo en tiempo de compilación, lo cual es muy útil.

3
3 revs 23 may. 2017 a las 12:25