Nota generada a partir de liga

Compute Unified Device Architecture (CUDA)

Un poco de historia...

La industria de videojuegos impulsó el desarrollo de las tarjetas gráficas a una velocidad sin precedente a partir del año 1999 para incrementar el nivel de detalle visual en los juegos de video. Alrededor del 2003 se planteó la posibilidad de utilizar las unidades de procesamiento gráfico para procesamiento en paralelo relacionado con aplicaciones distintas al ambiente de gráficas. A partir del 2006 la empresa NVIDIA introdujo CUDA, una plataforma GPGPU* y un modelo de programación que facilita el procesamiento en paralelo en las GPU's.

*GPGPU es un término que se utilizó para referirse a la programación general en unidades de procesamiento gráfico, hoy en día se conoce simplemente como GPU programming. Ver General-purpose computing on graphics processing units.

Desde entonces las tarjetas gráficas han creado una brecha significativa con las unidades de procesamiento, CPU's. Ver por ejemplo las gráficas que NVIDIA publica año tras año y que están relacionadas con el número de operaciones en punto flotante por segundo (FLOPS) y la transferencia de datos en la memoria RAM de la GPU: from-graphics-processing-to-general-purpose-parallel-computing

Hoy en día (2020) se continúa el desarrollo de GPU's con mayor RAM, con mayor capacidad de cómputo y mejor conectividad con la CPU (la GPU y la CPU están conectadas por una interconexión de nombre PCI). Estos avances han permitido resolver problemas con mejor exactitud que los resueltos con las CPU's, por ejemplo en el terreno de deep learning en reconocimiento de imágenes. Ver ImageNet Classification with Deep Convolutional Neural Networks, 2012: A Breakthrough Year for Deep Learning.

Para avances al día de hoy (2020) ver NVIDIA Turing Architecture In-Depth, samsung-amd-rdna-gpu-2021, playstation-5-specifications-revealed-but-design-is-still-a-mystery, xbox-series-x-tech y recientemente IBM Supercomputer Summit Attacks Coronavirus….

La arquitectura en la que podemos ubicar a las GPU's es en la de un sistema MIMD y SIMD. De hecho es SIMT: Simple Instruction Multiple Thread en un modelo de sistema de memoria compartida pues "los threads en un warp* cargan la misma instrucción para ser ejecutada". Ver 2.1.Un_poco_de_historia_y_generalidades para las clasificación de Flynn en la que se encuentran los sistemas MIMD y SIMD.

* Un warp en el contexto de GPU programming es un conjunto de threads y que equivale a $32$, esto es, un warp equivale a $32$ threads.

¿Diferencia con la CPU multicore?

  • En cuanto al hardware:

GPU

Obs: obsérvese en el dibujo anterior la diferencia en tamaño del caché en la CPU y GPU. También la unidad de control es más pequeña en la GPU.

A diferencia de una máquina multicore o multi CPU's con la habilidad de lanzar en un instante de tiempo unos cuantos threads, por ejemplo cuatro threads en una máquina quad core, la GPU puede lanzar cientos o miles de threads en un instante siendo cada core heavily multithreaded. Sí hay restricciones en el número de threads que se pueden lanzar en un instante pues las tarjetas gráficas tienen diferentes características (modelo) y arquitecturas (ver List of NVIDIA GPU's) pero la diferencia es grande. Por ejemplo, el modelo GT 200 (2009) en un instante puede lanzar 30,720 threads.

Ver How Graphics Cards Work y How Microprocessors Work para más información.

¿Otras compañías producen tarjetas gráficas?

Ver por ejemplo la lista de GPU's de Advanced Micro Devices

¿Si tengo una tarjeta gráfica de AMD puedo correr un programa de CUDA?

No es posible pero entre las alternativas están:

¿Si tengo una tarjeta gráfica de NVIDIA un poco antigua puedo correr un programa de CUDA?

Las GPU's producidas por NVIDIA desde 2006 son capaces de correr programas basados en CUDA C. La cuestión sería revisar qué compute capability tiene tu tarjeta. Ver Compute Capabilities para las características que tienen las tarjetas más actuales.

¿Qué es CUDA C?

Es una extensión al lenguaje C de programación en el que se utiliza una nueva sintaxis para procesamiento en la GPU. Contiene también una librería runtime que define funciones que se ejecutan desde el host por ejemplo para alojar y desalojar memoria en el device, transferir datos entre la memoria host y la memoria device* o manejar múltiples devices. La librería runtime está hecha encima de una API de C de bajo nivel llamada NVIDIA CUDA Driver API la cual es accesible desde el código. Para información de la API de la librería runtime ver NVIDIA CUDA Runtime API.

* La transferencia de datos entre la memoria del host a device o viceversa es un bottleneck fuerte que nos lleva a replantear la necesidad de tal transferencia.

¿A qué se refiere la terminología de host y device?

Host es la máquina multicore o multi CPU's y device es la GPU. Una máquina puede tener múltiples GPU's por lo que tendrá múltiples devices.

Tengo una tarjeta NVIDIA CUDA capable ¿qué debo realizar primero?

Realizar instalaciones dependiendo de tu sistema operativo. Ver Instalación donde además se encontrará información para instalación de nvidia-docker.

Instalé lo necesario y al ejecutar en la terminal nvcc -V obtengo la versión... ¿cómo puedo probar mi instalación?

1) Obteniendo información del NVIDIA driver ejecutando en la terminal:

$nvidia-smi

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.26       Driver Version: 440.26       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 750     Off  | 00000000:01:00.0 Off |                  N/A |
| 40%   29C    P8     1W /  38W |     55MiB /   978MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

2) Compilando y ejecutando el siguiente programa de CUDA C:

Programa de hello world: hello_world.cu

#include<stdio.h>
__global__ void func(void){
    printf("Hello world del bloque %d del thread %d!\n", blockIdx.x, threadIdx.x);
}
int main(void){
    func<<<2,3>>>(); //2 bloques de 3 threads cada uno
    cudaDeviceSynchronize();
    printf("Hola del cpu thread\n");
    return 0;
}

Compilación: $nvcc hello_world.cu -o hello_world.out

Ejecución: $./hello_world.out:

Hello world del bloque 1 del thread 0!
Hello world del bloque 1 del thread 1!
Hello world del bloque 1 del thread 2!
Hello world del bloque 0 del thread 0!
Hello world del bloque 0 del thread 1!
Hello world del bloque 0 del thread 2!
Hola del cpu thread

3) Haciendo un query a la GPU para ver qué características tiene (lo siguiente es posible ejecutar sólo si se instaló el CUDA toolkit):

/usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery

/usr/local/cuda/samples/1_Utilities/deviceQuery/deviceQuery Starting...

 CUDA Device Query (Runtime API) version (CUDART static linking)


Detected 1 CUDA Capable device(s)

Device 0: "Tesla K80"
  CUDA Driver Version / Runtime Version          9.1 / 9.1
  CUDA Capability Major/Minor version number:    3.7
  Total amount of global memory:                 11441 MBytes (11996954624 bytes)
  (13) Multiprocessors, (192) CUDA Cores/MP:     2496 CUDA Cores
  GPU Max Clock rate:                            824 MHz (0.82 GHz)
  Memory Clock rate:                             2505 Mhz
  Memory Bus Width:                              384-bit
  L2 Cache Size:                                 1572864 bytes
  Maximum Texture Dimension Size (x,y,z)         1D=(65536), 2D=(65536, 65536), 3D=(4096, 4096, 4096)
  Maximum Layered 1D Texture Size, (num) layers  1D=(16384), 2048 layers
  Maximum Layered 2D Texture Size, (num) layers  2D=(16384, 16384), 2048 layers
  Total amount of constant memory:               65536 bytes
  Total amount of shared memory per block:       49152 bytes
  Total number of registers available per block: 65536
  Warp size:                                     32
  Maximum number of threads per multiprocessor:  2048
  Maximum number of threads per block:           1024
  Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
  Max dimension size of a grid size    (x,y,z): (2147483647, 65535, 65535)
  Maximum memory pitch:                          2147483647 bytes
  Texture alignment:                             512 bytes
  Concurrent copy and kernel execution:          Yes with 2 copy engine(s)
  Run time limit on kernels:                     No
  Integrated GPU sharing Host Memory:            No
  Support host page-locked memory mapping:       Yes
  Alignment requirement for Surfaces:            Yes
  Device has ECC support:                        Enabled
  Device supports Unified Addressing (UVA):      Yes
  Supports Cooperative Kernel Launch:            No
  Supports MultiDevice Co-op Kernel Launch:      No
  Device PCI Domain ID / Bus ID / location ID:   0 / 0 / 30
  Compute Mode:
     < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >

deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 9.1, CUDA Runtime Version = 9.1, NumDevs = 1
Result = PASS

¿Qué librerías/paquetes/extensiones se revisarán en el módulo II del curso?

  • CUDA_C. En ésta nota además de revisar las keywords al lenguaje C que NVIDIA añadió, se hará una descripción pequeña del hardware de la GPU.

Introducción al uso de funciones en:

¿Por qué usar CUDA y CUDA-C o más general cómputo en la GPU?

  • NVIDIA como se mencionó al inicio de la nota fue de las primeras compañías en utilizar la GPU para tareas no relacionadas con el área de gráficos y ha colaborado en el avance del conocimiento de las GPU's y desarrollo de algoritmos y tarjetas gráficas. Otra compañía es Khronos_Group por ejemplo, quien actualmente desarrolla OpenCl.

  • El cómputo en la GPU constituye hoy en día (2020) una alternativa fuerte a la implementación de modelos de machine learning ampliamente utilizada por la comunidad científica. En el terreno de cómputo matricial y deep learning* se encuentran tensorflow, caffe, Theano (aunque ya no es soportado (2020) pero está retomado en PyMC3), Pytorch y keras como ejemplos de lo anterior.

*Deep learning se ha utilizado para resolver problemas en machine learning típicos. Ejemplos de esto son la clasificación de imágenes, de sonidos o análisis de textos, ver por ejemplo Practical text analysis using deep learning.

*El paper plantea una discusión a realizar con la frase ...change in the state-of-the-art algorithms can render specialized hardware less effective in the future. Ver por ejemplo Tensor Cores, NVIDIA TENSOR CORES, The Next Generation of Deep Learning, The most powerful computers on the planet* como ejemplos de hardware especializado en aprendizaje automático con Tensorflow.

*Summit powered by 9,126 IBM Power9 CPUs and over 27,000 NVIDIA V100 Tensor Core GPUS, is able to do 200 quadrillion calculations per second... IBM Supercomputer Summit Attacks Coronavirus….

Preguntas de comprehensión

1)¿Qué factores han determinado un mejor performance de una GPU vs una CPU? (contrasta los diseños de una CPU vs una GPU).

2)¿Dentro de qué modelo de arquitectura de máquinas se ubica a la GPU y que puede comparársele con el modelo Single Program Multiple Data (SPMD) dentro de la taxonomía de Flynn?

3)¿Qué significan las siglas CUDA y detalla qué es CUDA.

Referencias

  1. N. Matloff, Parallel Computing for Data Science. With Examples in R, C++ and CUDA, 2014.

  2. CUDA