lunes, 5 de abril de 2010

CUDA: modelo de programación

En los últimos años se está explotando el potencial de las tarjetas gráficas para otros propósitos, a parte del que le hemos dado hasta el momento.
Estamos hablando de un procesador capaz de mover millones de polígonos y realizar operaciones sobre matrices enormes en un tiempo mínimo, ¿porqué limitarnos a usarlo únicamente con ese propósito?
Esto se ha denominado como GPUGP (Graphic Process Unit General Purpose) y existen diversas extensiones y lenguajes de programación que explotan algunas arquitecturas de algunas GPUs.

CUDA

En Noviembre de 2006, NVIDIA crea CUDA (Compute Unified Device Architecture), una arquitectura de procesamiento paralelo de propósito general, con un nuevo modelo de programación paralela.
Con esta extesión se puede obtener un código escalable a 100s cores y a 1000s hilos paralelos, independientemente de cuál sea la arquitectura del procesador CUDA.
Empezó como una pequeña extensión de C, pero ya es soportada por OpenCL, Fortran...
Antes de comenzar, vamos a ver una serie de equivalencias y aspectos fundamentales inherentes
del comportamiento de CUDA:
  • Device = GPU
  • Host = CPU
  • Kernel = Función llamada desde el Host que se ejecuta en Device
  • 1 CUDA Kernel se ejecuta mediante un array de Threads.
  • Todos los Threads ejecutan el mismo código.
  • Cada Thread tiene un ID que se usa para direccionar la memoria y tomar las decisiones de control.
  • Unidad básica de operación es el Thread.
  • Los Threads están organizados en bloques de Threads. (Blocks)
  • Los bloques están organizados en mallas de bloques. (Grids).
  • Un Grid solo puede ejecutar un Kernel.




















Los Threads van identificados mediante threadIdx, que es un array de
elementos 3D ( tiene 3 componentes, x, y y z) .
Cada Thread puede venir identificado por un índice, es decir, puede tener 1, 2 ó 3 dimensiones, que dependará de cómo vayamos a explotar el paralelismo y del uso del grid.
Los Threads de un bloque pueden cooperar entre sí mediante el uso de memoria compartida dentro del bloque y sincronizando su ejecución para coordinar los accesos a memoria, para lo que usaremos el identificador mencionado anteriormente.
Los grids pueden ser de 1 o 2 dimensiones, luego cada bloque dentro de un grid puede ser direccionado por un índice de 1 o 2 dimensiones mediante blockIdx.
Asimismo, la dimensión del bloque también se puede obtener desde dentro del kernel mediante blockDim.
Los Cuda Threads pueden acceder a los datos de múltiples espacios de memoria durante su ejecución, ya que:
  • Cada Thread posee su propia memoria local.
  • Cada bloque su propia memoria compartida por todos los threads del bloque y con el mismo tiempo de vida que los Threads que lo componen.
Todos los Trheads tienen acceso a la memoria global.





















El modelo de programación de CUDA asume que los CUDA threads se ejecutan en un device que actúa como coprocesador de un host que ejecuta un programa, proporcionando instrucciones para reservar, liberar, copiar memoria en la memoria del device, así como transferir datos entre el host y el device.
Este modelo asume que host y device poseen su propia DRAM, host memory y device memory.
CUDA.

Kernel
















Escalabilidad y sincronización















El tamaño del bloque es elegido aparentemente de forma “arbitraria”, y el grid es creado con suficientes bloques para tener un Thread por un elemento de la matriz.
Todos los Threads de un bloque se ejecutan dentro del mismo core. El número de threads por bloque está limitado por los recursos de memoria del core:
En la misma GPU, actualmente(?) un bloque puede contener 512 threads.
El tamaño de los datos suele ser más grande que el de los bloques:

Independencia de ejecución entre bloques:

Se busca que sea indiferente el orden en el que se ejecutan, y si se ejecutan en paralelo o en serie.
Si no, usar __syncthreads().

Concluyendo, los bloques son necearios para permitir la escalabilidad a diferentes números de cores.
Accesos a Memoria:

CUDA asume que device y host tienen su propia memoria. En principio, device trabaja con la host memory. Para que trabaje con su propia memoria, CUDA proporciona, entre otros:
  • cudaMalloc(void **, size_t);
  • cudaMemcpy(void *,void *,
  • size_t,cudaMemcpyHostToDevice|
  • cudaMemcpyDeviceToHost);
  • cudaFree(void *);
Compilación:














Apéndice
  • Interoperatividad con Directx y OpenGL.
  • Versión 2.3.1 (26/08/2009)
  • Arquitectura actual(?): nvidia FERMI: 512 cuda cores.
  • 228 universidades enseñan cuda actualmente. (4 de ellas Españolas

No hay comentarios:

Publicar un comentario