Вопрос по – базовое использование нескольких графических процессоров

22

Как я могу использовать два устройства, чтобы улучшить, например производительность следующего кода (сумма векторов)? Можно ли использовать больше устройств "одновременно"? Если да, как я могу управлять распределением векторов в глобальной памяти различных устройств?

#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;
}

Заранее спасибо. Michele

Ваш Ответ

1   ответ
36

С момента выпуска CUDA 4.0 вычисления с использованием нескольких графических процессоров того типа, о котором вы спрашиваете, относительно просты. До этого вам понадобилось бы использовать многопоточное хост-приложение с одним хост-потоком на GPU и какую-то систему межпотоковой связи, чтобы использовать несколько GPU внутри одного хост-приложения.

Теперь можно сделать что-то вроде этого для части выделения памяти вашего кода хоста:

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) );
}

(заявление об отказе от ответственности: написано в браузере, никогда не компилируется, никогда не тестируется, используйте на свой страх и риск).

Основная идея здесь заключается в том, что вы используетеcudaSetDevice выбирать между устройствами, когда вы выполняете операции на устройстве. Итак, в приведенном выше фрагменте я предположил два графических процессора и выделенную память на каждом [(N / 2) double на первом устройстве и N- (N / 2) на втором].

Передача данных с хоста на устройство может быть такой простой, как:

// 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);
}

(заявление об отказе от ответственности: написано в браузере, никогда не компилируется, никогда не тестируется, используйте на свой страх и риск).

Раздел запуска вашего кода в ядре может выглядеть примерно так:

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] );
    }
}

(заявление об отказе от ответственности: написано в браузере, никогда не компилируется, никогда не тестируется, используйте на свой страх и риск).

Обратите внимание, что я добавил дополнительный аргумент в ваш вызов ядра, потому что каждый экземпляр ядра может вызываться с разным количеством элементов массива для обработки. Я оставлю это вам для разработки необходимых изменений. Но, опять же, основная идея та же: использоватьcudaSetDevice чтобы выбрать конкретный графический процессор, затем запустить ядра на нем обычным способом, причем каждое ядро получает свои уникальные аргументы.

Вы должны быть в состоянии собрать эти части вместе, чтобы создать простое приложение с несколькими графическими процессорами. Существует множество других функций, которые можно использовать в последних версиях CUDA и аппаратном обеспечении для поддержки нескольких приложений с графическим процессором (например, унифицированная адресация, одноранговые возможности и многое другое), но этого должно быть достаточно для начала работы. В CUDA SDK есть также простое приложение muLti-GPU, на которое вы можете посмотреть другие идеи.

Error: User Rate Limit Exceeded micheletuttafesta
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededcudaMemcpyAsyncError: User Rate Limit ExceededConcurrency in CUDA multi-GPU executions.

Похожие вопросы