C ++

Programación de GPU con C ++

Programación de GPU con C ++

Descripción general

En esta guía, exploraremos el poder de la programación de GPU con C++. Los desarrolladores pueden esperar un rendimiento increíble con C ++, y acceder a la potencia fenomenal de la GPU con un lenguaje de bajo nivel puede producir algunos de los cálculos más rápidos disponibles actualmente.

Requisitos

Si bien cualquier máquina capaz de ejecutar una versión moderna de Linux puede admitir un compilador C ++, necesitará una GPU basada en NVIDIA para seguir este ejercicio. Si no tiene una GPU, puede activar una instancia con tecnología de GPU en Amazon Web Services u otro proveedor de nube de su elección.

Si elige una máquina física, asegúrese de tener instalados los controladores patentados de NVIDIA. Puede encontrar instrucciones para esto aquí: https: // linuxhint.com / install-nvidia-drivers-linux /

Además del controlador, necesitará el kit de herramientas CUDA. En este ejemplo, usaremos Ubuntu 16.04 LTS, pero hay descargas disponibles para la mayoría de las distribuciones principales en la siguiente URL: https: // desarrollador.nvidia.com / cuda-downloads

Para Ubuntu, elegiría el .descarga basada en deb. El archivo descargado no tendrá un .deb de forma predeterminada, por lo que recomiendo cambiarle el nombre para tener una .deb al final. Luego, puede instalar con:

sudo dpkg -i nombre-paquete.debutante

Es probable que se le solicite que instale una clave GPG y, de ser así, siga las instrucciones proporcionadas para hacerlo.

Una vez que haya hecho eso, actualice sus repositorios:

sudo apt-get update
sudo apt-get install cuda -y

Una vez hecho esto, recomiendo reiniciar para asegurarse de que todo esté cargado correctamente.

Los beneficios del desarrollo de GPU

Las CPU manejan muchas entradas y salidas diferentes y contienen una gran variedad de funciones no solo para lidiar con una amplia variedad de necesidades de programas, sino también para administrar diferentes configuraciones de hardware. También manejan la memoria, el almacenamiento en caché, el bus del sistema, la segmentación y la funcionalidad de E / S, lo que los convierte en un gato para todos los oficios.

Las GPU son todo lo contrario: contienen muchos procesadores individuales que se centran en funciones matemáticas muy simples. Debido a esto, procesan tareas muchas veces más rápido que las CPU. Al especializarse en funciones escalares (una función que toma una o más entradas pero devuelve solo una salida), logran un rendimiento extremo a costa de una especialización extrema.

Código de ejemplo

En el código de ejemplo, sumamos vectores juntos. He agregado una versión de CPU y GPU del código para comparar la velocidad.
gpu-ejemplo.cpp contenido a continuación:

#include "cuda_runtime.h "
#incluir
#incluir
#incluir
#incluir
#incluir
typedef std :: chrono :: high_resolution_clock Reloj;
#definir ITER 65535
// Versión de CPU de la función de adición de vectores
void vector_add_cpu (int * a, int * b, int * c, int n)
int i;
// Suma los elementos vectoriales ayb al vector c
para (i = 0; i < n; ++i)
c [i] = a [i] + b [i];


// Versión GPU de la función de adición de vectores
__global__ void vector_add_gpu (int * gpu_a, int * gpu_b, int * gpu_c, int n)
int i = threadIdx.X;
// No se necesita un bucle for porque el tiempo de ejecución de CUDA
// subirá este ITER veces
gpu_c [i] = gpu_a [i] + gpu_b [i];

int main ()
int * a, * b, * c;
int * gpu_a, * gpu_b, * gpu_c;
a = (int *) malloc (ITER * tamaño de (int));
b = (int *) malloc (ITER * tamaño de (int));
c = (int *) malloc (ITER * tamaño de (int));
// Necesitamos variables accesibles a la GPU,
// entonces cudaMallocManaged proporciona estos
cudaMallocManaged (& gpu_a, ITER * tamaño de (int));
cudaMallocManaged (& gpu_b, ITER * tamaño de (int));
cudaMallocManaged (& gpu_c, ITER * tamaño de (int));
para (int i = 0; i < ITER; ++i)
a [i] = i;
b [i] = i;
c [i] = i;

// Llamar a la función de la CPU y cronometrarla
auto cpu_start = Reloj :: ahora ();
vector_add_cpu (a, b, c, ITER);
auto cpu_end = Reloj :: ahora ();
std :: cout << "vector_add_cpu: "
<< std::chrono::duration_cast(cpu_end - cpu_start).contar()
<< " nanoseconds.\n";
// Llamar a la función GPU y cronometrarla
// Los soportes de triple ángulo son una extensión de tiempo de ejecución CUDA que permite
// parámetros de una llamada al kernel CUDA que se van a pasar.
// En este ejemplo, estamos pasando un bloque de subprocesos con subprocesos ITER.
auto gpu_start = Reloj :: ahora ();
vector_add_gpu <<<1, ITER>>> (gpu_a, gpu_b, gpu_c, ITER);
cudaDeviceSynchronize ();
auto gpu_end = Reloj :: ahora ();
std :: cout << "vector_add_gpu: "
<< std::chrono::duration_cast(gpu_end - gpu_start).contar()
<< " nanoseconds.\n";
// Liberar las asignaciones de memoria basadas en la función de la GPU
cudaFree (a);
cudaFree (b);
cudaFree (c);
// Liberar las asignaciones de memoria basadas en la función de la CPU
libre (a);
libre (b);
libre (c);
return 0;

Makefile contenido a continuación:

INC = -I / usr / local / cuda / include
NVCC = / usr / local / cuda / bin / nvcc
NVCC_OPT = -std = c ++ 11
todas:
$ (NVCC) $ (NVCC_OPT) gpu-ejemplo.cpp -o gpu-example
limpio:
-rm -f gpu-example

Para ejecutar el ejemplo, compílelo:

fabricar

Luego ejecuta el programa:

./ gpu-example

Como puede ver, la versión de la CPU (vector_add_cpu) se ejecuta considerablemente más lenta que la versión de la GPU (vector_add_gpu).

De lo contrario, es posible que deba ajustar la definición de ITER en gpu-example.cu a un número mayor. Esto se debe a que el tiempo de configuración de la GPU es más largo que algunos bucles más pequeños que consumen mucha CPU. Encontré que 65535 funciona bien en mi máquina, pero su millaje puede variar. Sin embargo, una vez que supera este umbral, la GPU es dramáticamente más rápida que la CPU.

Conclusión

Espero que hayas aprendido mucho de nuestra introducción a la programación de GPU con C++. El ejemplo anterior no logra mucho, pero los conceptos demostrados proporcionan un marco que puede utilizar para incorporar sus ideas y liberar el poder de su GPU.

Los 10 mejores juegos para jugar en Ubuntu
La plataforma Windows ha sido una de las plataformas dominantes para juegos debido al gran porcentaje de juegos que se están desarrollando hoy para ad...
5 mejores juegos de arcade para Linux
Hoy en día, las computadoras son máquinas serias que se usan para jugar. Si no puede obtener la nueva puntuación más alta, sabrá a qué me refiero. En ...
Batalla por Wesnoth 1.13.6 Desarrollo liberado
Batalla por Wesnoth 1.13.6 lanzado el mes pasado, es el sexto lanzamiento de desarrollo en el 1.13.x y ofrece una serie de mejoras, sobre todo en la i...