uso básico de varias GPU


¿Cómo puedo usar dos dispositivos para mejorar, por ejemplo el rendimiento del siguiente código (suma de vectores)? Es posible utilizar más dispositivos "al mismo tiempo"? En caso afirmativo, ¿cómo puedo gestionar las asignaciones de los vectores en la memoria global de los diferentes dispositivos?

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <cuda.h>

#define NB 32
#define NT 500
#define N NB*NT

__global__ void add( double *a, double *b, double *c);

//===========================================
__global__ void add( double *a, double *b, double *c){

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

    while(tid < N){
        c[tid] = a[tid] + b[tid];
        tid += blockDim.x * gridDim.x;
    }

}

//============================================
//BEGIN
//===========================================
int main( void ) {

    double *a, *b, *c;
    double *dev_a, *dev_b, *dev_c;

    // allocate the memory on the CPU
    a=(double *)malloc(N*sizeof(double));
    b=(double *)malloc(N*sizeof(double));
    c=(double *)malloc(N*sizeof(double));

    // allocate the memory on the GPU
    cudaMalloc( (void**)&dev_a, N * sizeof(double) );
    cudaMalloc( (void**)&dev_b, N * sizeof(double) );
    cudaMalloc( (void**)&dev_c, N * sizeof(double) );

    // fill the arrays 'a' and 'b' on the CPU
    for (int i=0; i<N; i++) {
        a[i] = (double)i;
        b[i] = (double)i*2;
    }

    // copy the arrays 'a' and 'b' to the GPU
    cudaMemcpy( dev_a, a, N * sizeof(double), cudaMemcpyHostToDevice);
    cudaMemcpy( dev_b, b, N * sizeof(double), cudaMemcpyHostToDevice);

    for(int i=0;i<10000;++i)
        add<<<NB,NT>>>( dev_a, dev_b, dev_c );

    // copy the array 'c' back from the GPU to the CPU
    cudaMemcpy( c, dev_c, N * sizeof(double), cudaMemcpyDeviceToHost);

    // display the results
    // for (int i=0; i<N; i++) {
    //      printf( "%g + %g = %g\n", a[i], b[i], c[i] );
    //  }
    printf("\nGPU done\n");

    // free the memory allocated on the GPU
    cudaFree( dev_a );
    cudaFree( dev_b );
    cudaFree( dev_c );
    // free the memory allocated on the CPU
    free( a );
    free( b );
    free( c );

    return 0;
}

Gracias de antemano. Michele

Author: talonmies, 2012-05-10

1 answers

Desde que CUDA 4.0 fue lanzado, los cálculos multi-GPU del tipo que usted está preguntando son relativamente fáciles. Antes de eso, tendría que usar una aplicación host multi-thread con un subproceso host por GPU y algún tipo de sistema de comunicación inter-thread para usar GPU mutliple dentro de la misma aplicación host.

Ahora es posible hacer algo como esto para la parte de asignación de memoria de su código de host:

double *dev_a[2], *dev_b[2], *dev_c[2];
const int Ns[2] = {N/2, N-(N/2)};

// allocate the memory on the GPUs
for(int dev=0; dev<2; dev++) {
    cudaSetDevice(dev);
    cudaMalloc( (void**)&dev_a[dev], Ns[dev] * sizeof(double) );
    cudaMalloc( (void**)&dev_b[dev], Ns[dev] * sizeof(double) );
    cudaMalloc( (void**)&dev_c[dev], Ns[dev] * sizeof(double) );
}

(descargo de responsabilidad: escrito en el navegador, nunca compilado, nunca probado, uso bajo su propio riesgo).

La idea básica aquí es que use cudaSetDevice para seleccionar entre dispositivos cuando esté preformando operaciones en un dispositivo. Así que en el fragmento anterior, asumí dos GPU y asigné memoria en cada [(N/2) se duplica en el primer dispositivo y N-(N/2) en el segundo].

La transferencia de datos desde el host al dispositivo podría ser tan simple como:

// copy the arrays 'a' and 'b' to the GPUs
for(int dev=0,pos=0; dev<2; pos+=Ns[dev], dev++) {
    cudaSetDevice(dev);
    cudaMemcpy( dev_a[dev], a+pos, Ns[dev] * sizeof(double), cudaMemcpyHostToDevice);
    cudaMemcpy( dev_b[dev], b+pos, Ns[dev] * sizeof(double), cudaMemcpyHostToDevice);
}

(descargo de responsabilidad: escrito en el navegador, nunca compilado, nunca probado, uso propio riesgo).

La sección de lanzamiento del kernel de su código podría verse como:

for(int i=0;i<10000;++i) {
    for(int dev=0; dev<2; dev++) {
        cudaSetDevice(dev);
        add<<<NB,NT>>>( dev_a[dev], dev_b[dev], dev_c[dev], Ns[dev] );
    }
}

(descargo de responsabilidad: escrito en el navegador, nunca compilado, nunca probado, uso bajo su propio riesgo).

Tenga en cuenta que he agregado un argumento adicional a su llamada al núcleo, porque cada instancia del núcleo puede ser llamada con un número diferente de elementos de matriz para procesar. Le dejaré a usted resolver las modificaciones necesarias. Pero, de nuevo, la idea básica es la misma: use cudaSetDevice para seleccionar un dado GPU, luego ejecute kernels en él de la manera normal, con cada kernel obteniendo sus propios argumentos únicos.

Usted debe ser capaz de poner estas partes juntas para producir una aplicación simple multi-GPU. Hay muchas otras características que se pueden utilizar en versiones recientes de CUDA y hardware para ayudar a múltiples aplicaciones de GPU (como el direccionamiento unificado, las instalaciones de igual a igual son más), pero esto debería ser suficiente para comenzar. También hay una simple aplicación Multi-GPU en el CUDA SDK puede mirar para más ideas.

 35
Author: talonmies,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-10 09:48:40