Anda di halaman 1dari 41

Indice de contenidos

CUDA
Manuel Ujaldn Martnez
Departamento de Arquitectura de Computadores Universidad de Mlaga

1. Introduccin. [14 diapositivas] 2. Arquitectura. [25] 1. El modelo hardware de CUDA. [3] 2. Las dos primeras generaciones (2006-2009). [5] 3. La tercera generacin: Fermi (2010). [12] 4. La cuarta generacin: Kepler (2012). [5] 3. Programacin. [40] 4. Sintaxis. [23] 1. Elementos bsicos. [11] 2. Algunos ejemplos. [6] 3. La librera en tiempo de ejecucin. [5]

Indice de contenidos (2)


5. Compilacin. [13] 1. El proceso de compilacin. [5] 2. Compilando para otras plataformas. [9] 6. Depuracin y optimizacin. [18] 7. Estrategias de mejora. [14] 8. Bibliografa y herramientas. [3]

I. Introduccin

El camino hacia los sombreadores unificados que dieron lugar a CUDA


! Cuestin del ao 2000: La programacin de video-juegos requiere instrumentos para recrear la riqueza de materiales y efectos del mundo real. ! Respuesta del ao 2002: Aparecen los sombreadores para vrtices y pxeles. ! Cuestin del ao 2005: Algunas etapas del cauce de segmentacin grfico son programables, pero con fuertes restricciones:
! En cmo pueden comunicarse entre ellas. ! En cmo pueden acceder a memoria global.

Alternativas a CUDA para la programacin paralela en GPUs


De menor a mayor afinidad durante esta dcada: ! Fujitsus: Llamadas a procedimientos remotos. ! Brook: El compilador pionero de Stanford. ! PeakStream: Libreras matemticas para Radeon. ! RapidMind: Directivas de ms alto nivel y una generacin de cdigo ms automatizada. Genera cdigo para CPUs x86, GPUs y Cell. ! Tileras: Malla escalable por encima de 512 procesadores. ! CAL (Compute Abstraction Layer) y CTM (Close to Metal): Variantes AMD. ! OpenCL: El nuevo estndar, superconjunto de CUDA.

! Respuesta del ao 2007: Se unifican los sombreadores, y sobre ellos, surge el modelo de programacin CUDA.
5

Los paradigmas de computacin hasta llegar a CUDA

Qu es CUDA? Compute Unified Device Architecture


! Una plataforma diseada conjuntamente a nivel software y hardware para aprovechar la potencia de una GPU en aplicaciones de propsito general. ! A nivel software, permite programar la GPU en C con mnimas pero potentes extensiones SIMD para lograr una ejecucin eficiente y escalable. ! A nivel firmware, ofrece un driver para la programacin GPGPU que es compatible con el que se usa para renderizar. ! A nivel hardware, habilita mltiples niveles de paralelismo. Segn las 3 generaciones ya fabricadas, tenemos (1-2-3):
! Multiprocesadores de 16-30-16 nodos sobre los que se ejecutan bloques. ! Nodos de 8-8-32 procesadores (SPs) sobre los que se ejecutan hilos. ! Jerarqua de memoria: Registros, memoria compartida y global (DRAM).
7 8

Qu es CUDA?
! En esencia, es lenguaje C con mnimas extensiones:
! El programador escribe el programa para un solo hilo (thread), y el cdigo se instancia de forma automtica sobre cientos de threads.

CUDA: Relacin con la CPU


! CUDA ejecuta un programa sobre un device aparte (la GPU), que acta como coprocesador de un host (la CPU).
! Extensin del lenguaje C. ! Biblioteca de funciones con tres tipos de componentes:
! Host: Control y acceso a los devices. ! Device: Funciones especficas de los devices. ! Comunes: Tipos vectoriales y un conjunto de rutinas soportadas por host y device.

! CUDA define:
! Un modelo de arquitectura:
! Con multitud de unidades de proceso (cores) y una sola unidad de control (SIMD).

! Un modelo de programacin:
! Basado en el paralelismo masivo de datos y en el paralelismo de grano fino. ! Escalable: El cdigo se ejecuta sobre cualquier nmero de cores sin recompilar.

! Un modelo de gestin de la memoria:


! Ms explcita al programador, con control explcito de la memoria cach.

HOST

CPU

! Objetivos:
! Construir cdigo escalable a cientos de cores de forma sencilla, permitiendo declarar miles de hilos. ! Permitir computacin heterognea en CPU y GPU.
Memoria principal
9

E/S (PCIe)

GPU

DEVICE

Memoria de vdeo
10

Objetivos de CUDA
! Habilitar paralelismo masivo en GPU ejecutando programas SIMD sin las limitaciones y sobrecargas del API grfico. GPGPU ya no es cdigo OpenGL:
! Dispone de driver independiente optimizado para computacin, no especializado en renderizacin. ! Su API no posee llamadas con vrtices, texturas, polgonos, .... aunque comparte informacin con algunos buffers OpenGL.

Ventajas de CUDA sobre la programacin GPGPU precursora (Cg y OpenGL)


! Memoria direccionable a nivel de byte.
! Un hilo puede acceder a cualquier posicin de memoria.

! Acceso ilimitado a memoria.


! Un hilo puede leer/escribir en tantas posiciones de memoria como sea necesario.

! Permitir al programador involucrarse a distintos grados de exigencia segn el rendimiento:


! Bsico: Posibilita una fcil portabilidad desde C++. ! Medio: Requiere un buen conocimiento de la arquitectura grfica. ! Avanzado: Permite un mapeo eficiente del problema sobre muchos procesadores minimizando conflictos en el acceso a memoria.

! Control explcito de la memoria por parte del programador:


! La cach (shared memory) se gestiona manualmente.

! Sincronizacin para los hilos y la memoria compartida (para cada bloque de hilos CUDA).
! Los hilos pueden cargar datos en memoria compartida de forma cooperativa dentro de un bloque.
11 12

Ventajas de CUDA sobre la programacin GPGPU precursora (2)


! La fase de aprendizaje se acorta, ya que los elementos a utilizar resultan familiares:
! Cdigo: Apenas unas pocas extensiones al C convencional. ! Datos: No se requiere un conocimiento de grficos (vrtices, texturas, pxeles).

Esfuerzo del programador CUDA en relacin al rendimiento esperado

! El tiempo de ejecucin del cdigo no sufre una penalizacin por la sobrecarga del API grfico (DirectX/ OpenGL).
! La depuracin y optimizacin de cdigo resultan mucho ms llevaderas.

13

Fuente: Curso de CUDA impartido en el MIT.

14

Si programamos con CUDA, la arquitectura destino no tiene por qu ser CUDA


! A partir de CUDA 2.1 (2009), podemos compilar indistintamente para many-core GPU o multi-core CPU

Si instalamos una arquitectura CUDA, la programacin tampoco tiene que ser CUDA
! Se soporta Windows, Linux y MacOS, y sobre ellos, herramientas de desarrollo y lenguajes de programacin plenamente consolidados:

! A partir de 2010, el abanico de opciones es ms amplio con OpenCL, Ocelot y el compilador CUDA/C++ de PGI.

15

16

Las posibilidades de CUDA como lenguaje dependen del hardware subyacente


Evolucin del entorno de desarrollo C / C++ y funcionalidad aportada por cada versin de CCC (CUDA Compute Capabilities)
2007
Julio 07 Nov 07 Abril 08

A macroescala, la evolucin tambin ha posibilitado un perfil de usuario ms amplio


! Al contrario de lo que suele ser habitual, CUDA evoluciona desde un usuario ms cientfico a otro ms generalista.

2008
Agosto 08 Julio 09

2009
Nov 09

CUDA C 1.0

CUDA C 1.1

CUDA Visual Profiler 2.2

CUDA C 2.0

CUDA C 2.3

CUDA C/C++ 3.0


Beta

Compilador de C Extensiones C Simple Precisin BLAS FFT SDK (40 ejemplos)

Windows XP 64 Operaciones atmicas Soporte multi-GPU

CUDA HW debugger

Doble precisin

Optimizaciones del compilador Windows Vista de 32 y 64 bits Mac OSX Texturas 3D Interpolacin por hardware

FFT en doble

precisin

Intrnsecos para la conversin de 16 a 32 bits Mejoras en rendimiento

Funcionalidad C+ + Soporte para la arquitectura Fermi Herramientas Drivers

17 40

18

II. Arquitectura

II.1. El modelo hardware de CUDA

19

20

El modelo hardware de CUDA: Un conjunto de procesadores SIMD


! La GPU consta de:
! N multiprocesadores x M cores.
GPU Multiprocesador N Multiprocesador 2 Multiprocesador 1
Unidad de Control SIMD

Arquitectura del sistema de memoria


! Cada multiprocesador tiene:
! Su banco de registros. ! Memoria compartida. ! Una cach de constantes y otra de texturas, ambas de slo lectura y uso marginal.
GPU Multiprocesador N Multiprocesador 2 Multiprocesador 1
Memoria compartida
Registros Procesador 1 Registros Procesador 2 Registros

! Paralelismo masivo:
! Aplicado sobre miles de hilos. ! Compartiendo datos a diferentes niveles.

Core 1

Core 2

Core M

! Computacin heterognea:
! GPU:
! Intensiva en datos. ! Paralelismo de grano fino.

G80 Marco temporal N (multiprocs.)

GT200

GF100 K10 (Kepler) (Fermi) (2 x GK104) 2012-13 8 192 1536


21

! La memoria global es la memoria de vdeo (GDDR5):


! Tres veces ms rpida que la de la CPU, pero... ! ... 500 veces ms lenta que la memoria compartida! (que es SRAM en realidad).

Procesador M

Unidad de Control SIMD

Cach para constantes Cach para texturas

! CPU:

2006-07 2008-09 2010-11 16 8 128 30 8 240 16 32 512

! Gestin y control. M (cores/multip.) ! Paralelismo de grano grueso. Nm. de cores

Memoria global
13 22

Latencia y ancho de banda de la memoria en CUDA


! Memoria de la CPU (o memoria principal - DDR3): ! Memoria de vdeo (o memoria global): ! Memoria compartida
! Ancho de banda con mem. vdeo: 3.2 GB/s.(PCIe) y 5.2 GB/s(PCIe2). ! Gran ancho de banda (80-100 GB/s) y latencia, no pasa por cach. ! Baja latencia, ancho de banda muy elevado, tamao reducido. ! Acta como una cach gestionada por el usuario (scratchpad). ! De slo lectura, alta/baja latencia, pasa por cach.
Host CPU Chipset DRAM Device DRAM
Local Global Constantes Texturas

II.2. Las dos primeras generaciones

! Memoria de texturas/constantes

GPU
Multiprocesador
Registros y memoria compartida

Cachs para constantes y texturas


23 24

La arquitectura en general
! Se compone de multiprocesadores dotados de 8 cores, donde los GFLOPS escalan con el nmero de cores (stream processors), y el ancho de banda escala con el nmero de controladores de memoria segn el modelo comercial:
Thread manager

Evolucin segn el nmero de cores


(GTS8600) (GTX9800)

cores (stream procs.)


>100 GB/s.

Gener. CCC TPCs SM/TPC SMs


(GTX200)

G80

G92

GT200 GF100

v1.0 8 2 16 8 128 0 0 2 32

v1.1 8 2 16 8 128 0 0 2 32

v1.3 10 3 30 8 240 1 30 2 60

v2.0 16 1 16 32 512 16 256 4 64

GDDR3

512 bits Video memory

SP/SM SPs DP/SM DPs SFU/SM SFUs

TPC
30 240 624 8 256 141
25

Modelo GeForce GTS8600 GTX9800 GTX200 Multiprocesadores Cores GFLOPS Control. de mem. Anchura del bus A. banda (GB/s) 4 32 93 2 64 32 16 128 429 4 128 70

! CCC = CUDA Compute Capabilities (describe el ritmo evolutivo). ! TPC = Thread Processing Cluster. El hardware del prop. general. ! SFU = Special Function Unit. Operadores matemticos complejos.

26

La primera generacin: G80 (GeForce 8800)


GPU G80 (en torno a 600 MHz, frecuencia muy inferior a la de sus cores)
Multiprocesador 16 Multiprocesador 2 Multiprocesador 1 (los bloques de cdigo CUDA se mapean sobre los multipr.)

La segunda generacin: GT200 (GTX 200)


GPU GTX 200 (en torno a 600 MHz)
Multiprocesador 30 Multiprocesador 2 Multiprocesador 1 (los bloques de cdigo CUDA se mapean sobre los multipr.)

Memoria compartida (16 KB)


Registros Registros

Memoria compartida (16 KB)

Core 1
(1.35 GHz)

Core 2

Registros

Unidad de control
(emite instrucciones SIMD) (los kernels se mapean sobre los cores)

Registros

Registros

Core 8

Core 1
(1.30 GHz)

Core 2

Registros

Unidad de control
(emite instrucciones SIMD) (los kernels se mapean sobre los cores)

Core 8

Cach de texturas

Cach de texturas

Memoria global (hasta 1.5 GB) (GDDR3 @ 2x 800MHz)


27

Memoria global (hasta 4 GB) (GDDR3, 512 bits @ 2x 1.1GHz = 141.7 GB/s)
28

Escalabilidad para futuras generaciones: Alternativas para su crecimiento futuro


GPU ! Aumentar el nmero de Multiprocesador 30 multiprocesadores por pares (escalabilidad en 2 gener.) (nodo bsico), esto es, crecer Multiprocesador 2 en la dimensin Z. Es lo que Multiprocesador 1 hizo la 2 gener. (de 16 a 30). Memoria compartida ! Aumentar el nmero de Registros Registros Registros procesadores de cada Core 2 Core 8 Core 1 multiprocesador, o crecer en la (escalabilidad en 3 gener.) dimensin X. Es lo que hizo la Cach de texturas 3 gener. (de 8 a 32). ! Aumentar el tamao de la memoria compartida, esto es, Memoria global crecer en la dimensin Y.

II. 3. La tercera generacin: Fermi (GF100)

29

30

La nueva GPU en cifras, y respecto a sus predecesores en la saga GeForce


Arquitectura GPU Nombre comercial Ao de lanzamiento Nmero de transistores Nmero de cores Velocidad de clculo en doble precisin Planificadores de warps Shared memory Cach L1 Cach L2 Correccin de errores (DRAM) Anchura del bus de direcciones G80 GeForce 8800 2006 681 millones 128 Ninguna 1 16 KB Ninguna Ninguna No 32 bits GT200 GTX 200 2008 1400 millones 240 30 madds/ciclo 1 16 KB Ninguna Ninguna No 32 bits Fermi GF 100 2010 3000 millones Hasta 512 256 madds/ciclo 2 16 KB + 48 KB (o viceversa) 768 KB S 64 bits
31

Fermi en perspectiva: Redimiento en FLOPS y consumo energtico en vatios


Fabricante Generacin arquit. Nombre comercial Ciclo de reloj Zcalo(s) de procesador Cores por procesador GFLOPS (simple prec.) GFLOPS (doble prec.) Ancho de banda (GB/s.) Consumo del procesador 32 bit FLOPS / watt 64 bit FLOPS / watt Intel Nehalem x5550 2.66 GHz 2 4 170 85 51 200 0,4265 0,8530 nVidia T10P
Tesla C1060

nVidia GT200
GeForce GTX285

nVidia Fermi
Tesla C2050

1.44 GHz 1 30 933 78 102 200 0,3919 4,665

1.47 GHz 1 30 1060 88 159 204 0,4313 5,1980

1.15 GHz 1 14 1030 515 144 247 2,0850 4,1700

32

Principales rasgos de Fermi (GF 100) The soul of a Supercomputer in the body of a GPU
DRAM I/F DRAM I/F

Arquitectura global de Fermi


TPCs contienen un solo SM, pero: SFUs crecen de 60 a 64, SPs de 240 a 512 y DPs de 30 a 256. Hay un claro nfasis en recursos para computacin cientfica frente a recursos para renderizacin clsica. Doble planificador de procesos (scheduler) para los hilos que entran en un multiprocesador. 64 KB de SRAM, que se reparten entre la cach L1 y la memoria compartida a eleccin del programador.

! Principales

diferencias respecto a la generacin anterior:


!!-

DRAM Giga ThreadHOST I/ I/F

Integra el doble de cores.

DRAM I/F

L2 DRAM I/F DRAM I/F

Rendimiento 8 veces superior en aritmtica de punto flotante de doble precisin: A 2 GHz, proporciona 1 TFLOPS. Correccin de errores (ECC). Cachs L1 y L2 transparentes.

!!!-

La disposicin fsica de los cores y la memoria apenas ha cambiado en GF100 respecto a su predecesora, la arquitectura GT200.

El doble de ancho de banda con memoria de vdeo (GDDR5). Hasta 1 TB. de memoria de vdeo. Kernels concurrentes, C++.
33 34

!!-

Arquitectura de los cores


! Unidades aritmtico-lgicas (ALUs):
! Rediseada para optimizar las operaciones sobre enteros de 64 bits. ! Admite operaciones de precisin extendida.

La jerarqua de memoria
! Fermi es la primera GPU que ofrece una cach L1 tpica on-chip, que combina con la shared memory de CUDA en proporcin 3:1 o 1:3 para un total de 64 Kbytes por cada multiprocesador (32 cores). ! Tambin incluye una cach unificada de 768 Kbytes con coherencia de datos para el conjunto total de cores.
35

! La instruccin madd (suma y producto simultneos) est disponible tanto para simple como para doble precisin. ! La FPU (Floating-Point Unit):
! Implementa el formato IEEE-754 en su versin de 2008, aventajando incluso a las CPUs ms avezadas.

FP Unit

INT Unit
Load/Store Units x 16 Special Func Units x 4

13

36

Planificador hardware de threads: GigaThread (HTS - HW. Thread Scheduler)


! Gestiona jerrquicamente miles de threads simultneamente activos. ! El cambio de contexto de la aplicacin es 10 veces ms rpido que en generaciones previas. anteriores.

GigaThread: El motor de transferencia de datos streaming GigaThread (SDT)


! Dos coprocesadores de DMA:
! Permiten solapar computacin y comunicacin en los dos sentidos de las transferencias CPU-GPU. ! Se puede implementar un cauce de segmentacin de hasta 4 etapas.

SDT

HTS

! Ejemplo de actividad:
Kernel 0 Kernel 1
!

SDT0 SDT0

SDT1 SDT1 SDT0 SDT1 SDT0 SDT1


38

Kernel 2 Kernel 3
37

El planificador de instruccin

Ms mejoras en el front-end del procesador: GeForce 100 vs. 104 (para cada multiproc.)
! Emite 2 instrucciones a la vez, ! Emite 4 instrucciones a la vez, selecciona entre 6 cauces de ejec. selecciona entre 7 cauces de ejec.

14

39

40

GeF 104: Planificador dual combinado con superescalaridad (por primera vez en GPU)
! Recursos desequilibrados con respecto al tamao de WARP:
! 48 cores vs 32 warp size.

Visin de conjunto: GeForce 100 vs. 104

! Incremento relativo de la potencia en punto flotante y texturas:


! Duplica el nmero de unids.

! Decremento relativo de las unids de carga/almac. y los interpoladores:


! Mantienen la cantidad.
41 42

Diagrama de bloques: Kepler GK110

II. 4. La cuarta generacin: Kepler (GK110)

7.100 Mt. 15 SMX multiprocs. > 1 TFLOP FP64. 1.5 MB L2 Cache. 384-bit GDDR5. PCI Express Gen3.

43

44

Kepler GK110 SMX vs Fermi SM


! Desde SM hacia SMX.

Mejora de recursos en los SMX


Recurso Floating-point throughput Max. bloques por cada SMX Max. hilos por SMX Ancho de banda del banco de regs. Capacidad del banco de registros Ancho de banda de la memoria compartida Capacidad de la memoria compartida Ancho de banda de la cach L2 Capacidad de la cach L2
45

Kepler GK110 vs Fermi 2-3x 2x 1.3x 2x 2x 2x 1x 2x 2x


46

Innovaciones de Kepler: Paralelismo dinmico


CPU GPU CPU GPU

Innovaciones de Kepler: Paralelismo dinmico (2)


! El paralelismo puede depender de los datos, o incluso de la potencia computacional asociada a las regiones de inters.
Computational Power allocated to regions of interest

La GPU es un co-procesador

La GPU es un procesador autnomo


47

CUDA en 2012

CUDA sobre Kepler


48

Ecosistema corporativo y funcional de CUDA en su vertiente software al finalizar 2011

III. Programacin

530 Universidades ensean CUDA


UIUC MIT Harvard Berkeley Cambridge Oxford IIT Delhi UPC Barcelona Dortmundt ETH Zurich Moscow NTU

Lenguajes
C, C++ DirectX Fortran Java OpenCL Python

Compiladores
PGI Fortran CAPs HMPP MCUDA MPI NOAA Fortran2C OpenMP

Aplicaciones
Oil & Gas Finance CFD

Libreras
FFT BLAS LAPACK Proc. de imgenes Proc. de vdeo Proc. de seal Visin

Consultores
ANEO

Marcas

Medical Biophysics Imaging

Numerics
49

DSP

EDA

GPU Tech
50

Terminologa
! Host: La CPU y la memoria de la placa base [DDR3 en 2012]. ! Device: La tarjeta grfica [GPU + memoria de vdeo]:
! GPU: Nvidia GeForce/Tesla. ! Memoria de vdeo: GDDR5 en 2012.

Computacin heterognea

Host

Device
51 52

Un fichero, dos cdigos


#include <iostream> #include <algorithm> using namespace std; #define N 1024 #define RADIUS 3 #define BLOCK_SIZE 16 __global__ void stencil_1d(int *in, int *out) { __shared__ int temp[BLOCK_SIZE + 2 * RADIUS]; int gindex = threadIdx.x + blockIdx.x * blockDim.x; int lindex = threadIdx.x + RADIUS; // Read input elements into shared memory temp[lindex] = in[gindex]; if (threadIdx.x < RADIUS) { temp[lindex - RADIUS] = in[gindex - RADIUS]; temp[lindex + BLOCK_SIZE] = in[gindex + BLOCK_SIZE]; } // Synchronize (ensure all the data is available) __syncthreads(); // Apply the stencil int result = 0; for (int offset = -RADIUS ; offset <= RADIUS ; offset++) result += temp[lindex + offset]; // Store the result out[gindex] = result;

El proceso de ejecucin en GPU (1/3)

DEVICE CODE: parallel function HOST CODE: - serial code - parallel code - serial code

Bus PCI

void fill_ints(int *x, int n) { fill_n(x, n, 1); } int main(void) { int *in, *out; // host copies of a, b, c int *d_in, *d_out; // device copies of a, b, c int size = (N + 2*RADIUS) * sizeof(int); // Alloc space for host copies and setup values in = (int *)malloc(size); fill_ints(in, N + 2*RADIUS); out = (int *)malloc(size); fill_ints(out, N + 2*RADIUS); // Alloc space for device copies cudaMalloc((void **)&d_in, size); cudaMalloc((void **)&d_out, size); // Copy to device cudaMemcpy(d_in, in, size, cudaMemcpyHostToDevice); cudaMemcpy(d_out, out, size, cudaMemcpyHostToDevice); // Launch stencil_1d() kernel on GPU stencil_1d<<<N/BLOCK_SIZE,BLOCK_SIZE>>>(d_in + RADIUS, d_out + RADIUS); // Copy result back to host cudaMemcpy(out, d_out, size, cudaMemcpyDeviceToHost); // Cleanup free(in); free(out); cudaFree(d_in); cudaFree(d_out); return 0;

1. Copiar los datos de entrada desde la memoria de la CPU a la memoria de la GPU.

53

54

El proceso de ejecucin en GPU (2/3)

El proceso de ejecucin en GPU (3/3)

PCI Bus

PCI Bus

1. Copiar los datos de entrada desde la memoria de la CPU a la memoria de la GPU. 2. Cargar el programa de la GPU y ejecutar, utilizando la memoria compartida y/o la cach para mejorar el rendimiento.

1. Copiar los datos de entrada desde la memoria de la CPU a la memoria de la GPU. 2. Cargar el programa de la GPU y ejecutar, utilizando la memoria compartida y/o la cach para mejorar el rendimiento. 3. Copiar los resultados desde la memoria de la GPU a la memoria de la CPU.
55 56

El clsico ejemplo
int main(void) { printf("Hello World!\n"); return 0; }

Hello World! con cdigo en GPU (1/2)


__global__ void mykernel(void)

Output: $ nvcc hello.cu $ a.out Hello World! $

{ } int main(void) { mykernel<<<1,1>>>(); printf("Hello World!\n"); return 0; }

! Dos nuevos elementos sintcticos:


! La palabra clave __global__ indica una funcin que se ejecuta en la GPU y se llama desde el cdigo del host. ! mykernel<<<1,1>>> indica el lanzamiento de un kernel CUDA desde el host.

Es C estndar que se ejecuta en el host (la CPU). El compilador de Nvidia (nvcc) puede utilizarse para compilar programas que no contengan cdigo para alojar en GPU.

! Eso es todo lo que se necesita para ejecutar una funcin en la GPU.

nvcc separa el cdigo fuente en dos: Host y device. De las funciones del device (ej: mykernel()) se encarga nvcc. De las funciones del host (ej: main()) se encarga el compilador de C. gcc (for UNIX), cl.exe (for Windows).
57 58

Hello World! con cdigo en GPU (2/2)


__global__ void mykernel(void) { } int main(void) { mykernel<<<1,1>>>(); printf("Hello World!\n"); return 0; }

Si tenemos una arquitectura CUDA, podemos programarla de muy diversas maneras...

Output: $ nvcc hello.cu $ a.out Hello World! $


C++ C

GPU Computing

OpenCL

tm

Direct Compute

Fortran

Java and Python

! mykernel() no hace nada en esta ocasin. ! El triple signo "<" y ">" delimita una llamadas desde el host al device, cuyos parmetros determinan el paralelismo en forma de bloques e hilos.
59

NVIDIA GPU with the CUDA Parallel Computing Architecture

! ... aunque este curso se focaliza sobre CUDA C.

60

La evolucin de CUDA
! En los ltimos 5 aos, Nvidia ha vendido ms de 350 millones de GPUs que aceptan CUDA para su programacin. ! Pero CUDA ha evolucionado en la direccin opuesta a la que estamos acostumbrados: Desde los investigadores hasta los usuarios ms genricos.
Versin de CUDA [ao] 1.0 [2007] 2.0 [2008] 3.0 [2009] 4.0 [2011] 5.0 [2012] Usuarios Muchos investigadores y algunos usuarios madrugadores. Cientficos y aplicaciones para computacin de altas prestaciones. Lderes en la innovacin de aplicaciones. Adopcin mucho ms extensa de desarrolladores. Optimizada para Kepler y disponible pblicamente en el tercer trimestre de 2012.
61

El modelo de programacin CUDA


! La GPU (device) ofrece a la CPU (host) la visin de un coprocesador altamente ramificado en hilos.
! Que tiene su propia memoria DRAM. ! Donde los hilos se ejecutan en paralelo sobre los ncleos (cores o stream processors) de un multiprocesador.
GPU Multiprocesador 1 Multiprocesador 2 Multiprocesador N

! Los hilos de CUDA son extremadamente ligeros.


! Se crean en un tiempo muy efmero. ! La conmutacin de contexto es inmediata.

! Objetivo del programador: Declarar miles de hilos, que la GPU necesita para lograr rendimiento y escalabilidad.
62

Cada modelo se diferencia en pocos parmetros para generar el catlogo comercial


! Debemos esperar que estas diferencias crezcan entre modelos asociados a diferentes generaciones. ! Las diferencias tambin crecern cuando dos tarjetas pertenezcan a gamas diferentes:
! 300-500! para la gama alta. ! 150-250! para la gama media. ! 60-120! para la gama baja.

Ejemplo de catlogo comercial


Modelo comercial Generacin Ao Precio de lanzamiento Nmero de transistores Proceso de fabricacin Nmero de SMs Cores en cada SM Nmero total de cores Reloj de los cores (MHz) Tecnologa de memoria Reloj de memoria (MHz) Ancho del bus de mem. Tamao de la memoria
63

GTX 285 2009

GTX 480 2010

GTX 465 GF 100 2010

GTX 460 1 GB. GF 104 (10+) 2011

GTX 460 768MB. GF 104 2011

GT 200 (9) GF 100 (10)

! 300 1400 mill.


TSMC 55 nm.

! 500
TSMC 40 nm.

! 250
TSMC 40 nm

! 230 1950 mill.


TSMC 40 nm.

! 200 1950 mill.


TSMC 40 nm.

3000 mill. 3000 mill. 15 32 480 1401 GDDR5 4x 924 384 bits 1.5 GB. 11 32 352 1215 GDDR5 4x 802 256 bits 1 GB.

30 8 240 1476 GDDR3 2x 1242 512 bits 1 GB.

7 48 336 1350 GDDR5 4x 900 256 bits 1 GB.

7 48 336 1350 GDDR5 4x 900 192 bits 768 MB.


64

! La memoria de vdeo puede diferir tambin cuando emerge una nueva tecnologa, como en el caso de la GDDR5. ! Los modelos difieren tambin en sus prestaciones grficas, pero aqu no estamos interesados en ellas.

Ejemplo de catlogo comercial (cont.)


Model comercial Generacin Ao FP 64 bits vs. 32 bits Frec. GPU (MHz) Direcc. a texturas Filtrado de texturas Rasterizadores GTX 285 2009 GTX 480 2010 GTX 465 GF 100 2010 GTX 460 768 MB. GTX 460 1 GB. GF 104 (10+) 2011 GF 104 2011 GT 200 (9th) GF 100 (10th)

Estructura de un programa CUDA


! Cada multiprocesador procesa lotes de bloques, uno detrs de otro
! Bloques activos = los bloques procesados por un multiprocesador en un lote. ! Hilos activos = todos los que provienen de los bloques que se encuentren activos.

1 vs. 16 648 80 80 32

1 vs. 8 700 60 60 48

1 vs. 8 607 44 44 32

1 vs. 6 675 56 56 32

1 vs. 6 675 56 56 24

! Los registros y la memoria compartida de un multiprocesador se reparten entre sus hilos activos. Para un kernel dado, el nmero de bloques activos depende de:
! El nmero de registros requeridos por el kernel. ! La memoria compartida consumida por el kernel.
65 66

Conceptos bsicos
Los programadores se enfrentan al reto de exponer el paralelismo para mltiples cores y para mltiples hilos por core. Para ello, deben usar los siguientes elementos: Device = GPU = conjunto de multiprocesadores. Multiprocesador = conjunto de cores + memoria compartida. Kernel = programa ejecutndose en GPU. Grid = matriz de bloques de hilos que ejecutan un kernel. Bloque de hilos (thread block) = Grupo de hilos SIMD que: ! Ejecutan un kernel delimitando su dominio computacional segn su threadID y blockID. ! Pueden comunicarse a travs de la memoria compartida del multiprocesador.

Recursos y limitaciones segn la GPU que utilicemos para programar CUDA


Parmetro Valor segn gener. GPU CUDA Compute Capabilities (CCC) 1.0 y 1.1 1.2 y 1.3 Fermi Multiprocesadores / GPU Cores / Multiprocesador Hilos / Warp Bloques de hilos / Multiprocesador Hilos / Bloque Hilos / Multiprocesador Registros de 32 bits / Multiprocesador Memoria compartida / Multiprocesador
67

Limitacin HW. HW. SW. SW. SW. SW. HW. HW.

Impacto

! ! ! ! !

16 8 32 8 512 768 8K 16K

30 14-16 8 32 8 512 32 32 8 1024

Escalabilidad Escalabilidad Throughput Throughput Paralelismo Paralelismo Working set Working set
68

1 024 1 536 16K 16K 32K 16K 48K

Planificacin de instrucciones: Bloques de hilos


B1 Bn Mximo 8 bloques
!! !
Mximo 512-1024 hilos

WARPs. Concepto
Registros Memoria compartida

Asignacin a un multiproc.

Core Core Core Core SFU

Core Core Core Core SFU

Mximo 768, 1024 1536 hilos segn la gener. de GPU

! Los hilos se asignan a los multiprocesadores en bloques, que constituyen la unidad de asignacin de hilos. ! Cada multiprocesador puede tener hasta 8 bloques y cada bloque hasta 512, 512 o 1024 hilos. En total, caben un mximo de 768, 1024 o 1536 hilos en cada multiprocesador. ! Los hilos de un bloque comparten informacin a travs de memoria compartida, y se sincronizan mediante barreras.

! Cada bloque activo se descompone en WARPs o grupos de 32 hilos con ID correlativo que se ejecutan usando tiempo compartido en un multiprocesador. ! Los hilos de un WARP se ejecutan fsicamente en paralelo. ! Los WARPs y los bloques se ejecutan lgicamente en paralelo.

CPU (host) Kernel 1

GPU (device) Grid 1 Bloque (0, 0) Bloque (1, 0) Bloque (2, 0)

Bloque (0, 1)

Bloque (1, 1)

Bloque (2, 1)

Kernel 2 Bloque (1, 0) Warp 0

Grid 2

...

32 hilos

Multiproc.

32 hilos 32 hilos
Hilo (0, 0)

Hilo (31, 0)

Hilo (32, 0)

Warp 1

Hilo (63, 0)

Bloque de hilos

Warps
16 16

Hilo (0, 1)

Warp 2

Hilo (31,

Hilo (32, 1)

Warp 3

Hilo (63,

DRAM
Hilo (0, 2)

69

Half Warps Device Memory

Warp 4

Hilo (31, 2)

Hilo (32, 2)

Warp 5

Hilo (63, 2)

70

WARPs. Ejecucin
! Dado que el tamao del WARP es de 32 hilos:
! Si hay 8 procesadores en cada multiprocesador (como en las series 8, 9 y 10 de GeForce), cada WARP de una instruccin consume 4 ciclos de reloj en su ejecucin. ! Si hay 32 procesadores en cada multiprocesador (a partir de Fermi), cada WARP de una instruccin consume un solo ciclo.
Warp j (32 hilos) Instruccin x de warp j Bsqueda de instruccin L1 datos Registros

WARPs. Ejecucin (2)


! Ejemplo:
! Si 3 bloques de hilos se asignan a un mismo multiprocesador y cada uno de estos bloques tiene 256 hilos, Cuntos WARPS hay en ese multiprocesador?
! Cada bloque tendr 8 WARPS. ! Habr 8 * 3 = 24 WARPS. ! En un instante concreto, slo uno de esos 24 WARPS estar ejecutndose fsicamente en el hardware del multiprocesador.
WARPS del bloque 1

t0 t1 t2 t31

WARPS del bloque 2 t0 t1 t2 t31

Multiprocesador streaming
Cach L1 de instrs. Cach L1 de datos

Bi

Bsqueda y emisin de instrucciones Memoria compartida SP SP SFU SP SP SP SP SP SP SFU

Memoria compartida Core Core Core Core SFU SFU Core Core Core Core

71

72

WARPs. Planificacin
Un ejemplo ideal sobre la G80 (en la prctica, la ejecucin del WARP no es tan uniforme como ilustramos aqu - esto se ver mejor dos diapositivas ms tarde):
Multiprocesador

WARPs. Planificacin (2)


! Cuando acaba un WARP, los candidatos a ser ejecutados a continuacin se eligen de entre la cola de WARPs disponibles. ! Todos los hilos del WARP ejecutan la misma instruccin. ! Si se realiza un acceso a memoria global, se requiere un mnimo de:
! 13 WARPs para ocultar la latencia a memoria en torno a 200 ciclos. ! 27 WARPs para ocultar la latencia de 400 ciclos, y as en adelante.
73

Planificador de WARPS del multiprocesador tiempo warp 8 instruccin 11 warp 1 instruccin 42 warp 3 instruccin 95 . . . warp 8 instruccin 12 warp 3 instruccin 96
74

Warp 5 Instr. 10

Warp 23 Instr. 17

Warp 5 Instr. 11

Warp 12 Instr. 3

Warp 12 Instr. 4

Warp 11 Instr. 8

Warp 15 Instr. 1

! El WARP es la unidad de planificacin. Se usa:

ciclos

! Round-robin/aging para seleccionar el prximo WARP a planificar de entre aquellos con operandos ya ledos. ! Scoreboarding para evitar riesgos en el anlisis de dependencias.

! El cambio de contexto entre WARPs de un multiprocesador se lleva a cabo sin penalidad en ciclos de ejecucin.

WARPs. Planificacin (3)


! A pesar de todo, multitud de factores impredecibles en tiempo de ejecucin dificultan un reparto equilibrado de la carga computacional entre los multiprocesadores. Aqu vemos la duracin de los ltimos 8 WARPS asignados a cada SM de la G80 en un momento dado de una ejecucin:

Recopilando sobre kernels, bloques y paralelismo


! Los kernels se lanzan en grids.
! Slo se ejecuta un kernel a la vez.

Grid
Bloque (0, 0)
Memoria compartida

! Un bloque se ejecuta en un multiprocesador (SM).


! El bloque no migra.

Bloque (1, 0)
Memoria compartida

! Varios bloques pueden residir concurrentemente en un SM.


! Con las consabidas limitaciones:
! 8 bloques concurrentes. ! 512/512/1024 hilos en cada SM. ! 768/1024/1536 hilos en total.

Regs

Regs

Regs

Regs

! Y otras limitaciones entran en juego debido al uso conjunto de la memoria:


! El banco de registros se particiona entre todos los hilos existentes. ! La memoria compartida se particiona entre todos los bloques de hilos.
75

Hilo (0, 0)

Hilo (1, 0)

Hilo (0, 0)

Hilo (1, 0)

Memoria global

76

Escalabilidad transparente
! Dado que los bloques de hilos no pueden sincronizarse:
! El hardware tiene libertad para acomodar su ejecucin en cualquier multiprocesador en cualquier instante. ! Pueden correr secuencial o concurrentemente, y cualquier permutacin en su ejecucin paralela debe ser vlida.
Device Kernel grid Bloque 0 Bloque 1 Bloque 2 Bloque 3 Bloque 0 Bloque 1 Bloque 4 Bloque 5 Bloque 6 Bloque 7 Bloque 2 Bloque 3 Bloque 4 Bloque 5 Bloque 6 Bloque 7 Bloque 0 Bloque 1 Bloque 2 Bloque 3 Device

Kernels (y su relacin con los hilos)


! Las porciones paralelas de una aplicacin que corre en la CPU se ejecutan en la GPU como kernels. ! Slo un kernel se ejecuta en un momento dado en una GPU (esto es, NO hay multiproceso de kernels). ! Cuando el kernel acaba, todos los recursos de la GPU se liberan y se asignan al siguiente kernel. Es decir:
! Los hilos se ejecutan en paralelo (dentro de su bloque). ! Los bloques se ejecutan en paralelo (dentro de su kernel). ! Los kernels se lanzan secuencialmente. 0 1 2 3 4 5

6 7

threadID

! Segn su threadID, cada hilo:


! Ejecuta el mismo cdigo sobre un rea diferente de datos. ! Puede tomar decisiones de control para diferenciar su ejecucin del resto.

Bloque 4

Bloque 5

! Un kernel puede ejecutarse sobre cualquier nmero de multiprocesadores. ! El lanzamiento de un kernel sirve como punto de sincronizacin para los bloques.

float x = input[threadID]; float y = func(x); output[threadID] = y;

Bloque 6

Bloque 7

77

78

Hilos (y su relacin con los bloques)


! La cooperacin entre hilos resulta muy valiosa:
! Comparten resultados para ahorrar computaciones. ! Comparten accesos a memoria de vdeo para reducir drsticamente el ancho de banda (y el consumo del chip).

Particionamiento de computaciones y datos


! Un bloque de hilos es un lote de hilos que pueden cooperar:
! Compartiendo datos a travs de memoria compartida. ! Sincronizando su ejecucin para acceder a memoria sin conflictos.
CPU (host) GPU (device) Grid 1 Kernel 1 Bloque (0, 0) Bloque (0, 1) Grid 2 Kernel 2 Bloque (1, 0) Bloque (1, 1) Bloque (2, 0) Bloque (2, 1)

! El bloque garantiza rendimiento y escalabilidad, ya que permite replicar la ejecucin de un grupo de hilos tantas veces como sea necesario en funcin del volumen de datos:
! Permitiendo mantener el paralelismo de grano fino. ! Sin penalidad, ya que el cambio de contexto es gratis.
Bloque de hilos 0
threadID
0 1 2 3 4 5 6 7

Bloque de hilos 1
0 1 2 3 4 5 6 7

Bloque de hilos N-1


0 1 2 3 4 5 6 7

float x = input[threadID]; float y = func(x); output[threadID] = y;

float x = input[threadID]; float y = func(x); output[threadID] = y;

float x = input[threadID]; float y = func(x); output[threadID] = y;

! Un kernel se ejecuta como una malla o grid 1D o 2D de bloques de hebras 1D, 2D o 3D. ! Los hilos y los bloques tienen IDs para que cada hilo pueda acotar sobre qu datos trabaja, y simplificar el dir. a memoria al procesar datos multidim.
79

Bloque (1, 1)
Hilo Hilo (0, 0) (1, 0) Hilo (2, 0) Hilo Hilo (3, 0) (4, 0)

Hilo Hilo Hilo Hilo Hilo (0, 1) (1, 1) (2, 1) (3, 1) (4, 1) Hilo Hilo Hilo Hilo Hilo (0, 2) (1, 2) (2, 2) (3, 2) (4, 2)
80

Manipulacin de datos
! Una de las diferencias ms importantes entre CPU y GPU (y una de las claves para que la GPU se acerque a su rendimiento pico). ! La cach es 1000 veces menor en la GPU, por lo que los programadores deben gestionar la localidad de acceso a los datos con mucho esmero: ! La memoria local aloja a las variables de la GPU declaradas sin prefijo. ! Los registros albergan a escalares y resultados parciales. ! La memoria de vdeo (global) aloja a los vectores de datos.
Memoria Ubicacin Cach Local Compartida Global Constantes De texturas Acceso mbito Prefijo Vigencia

Tipos de memoria y su jurisdiccin


Hilo Bloque !!!
Registros Memoria compartida

Integrada en la GPU

! Los hilos pueden compartir el espacio de memoria compartida para comunicarse entre ellos.
!!! Grid 0 !!! !!! Grid 1 !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
Memoria Memoria para global (lectura y constantes (lectura) escritura) Memoria para texturas (lectura)

Off-chip On-chip Off-chip Off-chip Off-chip

No No S S

Lect./escr.

Un hilo

__device__ __shared__ __global__ __texture__

Hilo Bloque Kernel Kernel Kernel


81

Lect./escr. Hilos de un bloque Lect./escr. Los hilos y la CPU Lectura Lectura Los hilos y la CPU

Externa a la GPU (pero dentro de la tarjeta grfica)

Los hilos y la CPU __constant__

! El nivel de memoria global es el nico visible a todos los entes definidos por el programador (hilos, bloques, kernels y grids).
82

Espacios de memoria (1)


! La CPU y la GPU tiene espacios de memoria separados:
! Para comunicar ambos procesadores, se utiliza el bus PCI-express. ! En la GPU se utilizan funciones para alojar memoria y copiar datos de la CPU de forma similar a como la CPU procede en lenguaje C (malloc para alojar y free para liberar).

Esta problemtica se ha resuelto parcialmente en la versin 4.0 de CUDA

! Los punteros son slo direcciones:


! No se puede conocer a travs del valor de un puntero si la direccin pertenece al espacio de la CPU o al de la GPU. ! Hay que ir con mucha cautela a la hora de acceder a los datos a travs de punteros, ya que si un dato de la CPU trata de accederse desde la GPU o viceversa, el programa se colgar.

83

84

Espacios de memoria (2)


! Cada hilo puede:
! Leer/escribir registros per-thread. ! Leer/escribir memoria compartida per-block. ! Leer/escribir memoria global per-grid.
Grid en GPU Bloque (0, 0)
Memoria compartida Registros Registros

Principales debilidades/riesgos de CUDA


Bloque (1, 0)
Memoria compartida Registros Registros

! Cada hilo tambin puede, por motivos de comodidad o rendimiento:


! Leer/escribir memoria local per-thread (usado por el compilador para volcar registros en la memoria global). ! Leer memoria de constantes per-grid. ! Leer memoria de texturas per-grid.

Hilo (0, 0)

Hilo (1, 0)

Hilo (0, 0)

Hilo (1, 0)

Memoria local

Memoria local

Memoria local

Memoria local

! La CPU puede:
! Leer/escribir en memoria CPU (host) global, de constantes y de texturas (mapeadas sobre DRAM)

Memoria global
Memoria de constantes Memoria de texturas
85

! El ancho de banda entre memoria global (la de la tarjeta grfica) y los procesadores puede saturarse fcilmente. Las tareas que tienen un bajo ndice de reutilizacin de datos se quedan hambrientas. Ej: Suma vector-escalar, producto de vectores. ! Limitada capacidad del banco de registros y la memoria compartida que comparten todos los hilos de un multiprocesador. Utilizar el CUDA Occupancy Calculator para ayudarse en la toma de decisiones. ! Los saltos condicionales degradan notablemente el rendimiento si no se estructuran de forma sabia.
86

Cinco claves para maximizar el rendimiento del cdigo


1. Expresar explcitamente todo el paralelismo posible aplicando SIMD de grano fino para definir multitud de hilos. 1. Si los hilos de un mismo bloque necesitan comunicarse, utilizar la memoria compartida y __syncthreads() 2. Si los hilos de diferentes bloques necesitan comunicarse, utilizar la memoria global y descomponer la computacin en mltiples kernels. 2. Aprovechar el ancho de banda con memoria: Pocas transferencias grandes en lugar de muchas pequeas. 3. Optimizar la localidad de acceso: Reutilizacin de datos. 4. Ocultar latencias con memoria global maximizando la ocupacin de unidades funcionales. Intensidad aritmtica. 5. Maximizar el CPI del cdigo (throughput): Seleccionar la instruccin de menor latencia en el repertorio CUDA.
87

IV. Sintaxis

88

CUDA es C con algunas palabras clave ms. Un ejemplo preliminar

IV. 1. Los elementos bsicos

void saxpy_serial(int n, float a, float *x, float *y) { for (int i = 0; i < n; ++i) y[i] = a*x[i] + y[i]; Cdigo C estndar } // Invocar al kernel SAXPY secuencial saxpy_serial(n, 2.0, x, y);

Cdigo CUDA equivalente de ejecucin paralela en GPU:


__global__ void saxpy_parallel(int n, float a, float *x, float *y) { int i = blockIdx.x*blockDim.x + threadIdx.x; if (i < n) y[i] = a*x[i] + y[i]; } // Invocar al kernel SAXPY paralelo con 256 hilos/bloque int nblocks = (n + 255) / 256; saxpy_parallel<<<nblocks, 256>>>(n, 2.0, x, y);
89 90

Lista de extensiones sobre el lenguaje C


! Modificadores para las variables (type qualifiers):
! global, device, shared, local, constant.
__device__ float array[N]; __global__ void med_filter(float *image) { __shared__ float region[M]; ... region[threadIdx.x] = image[i]; __syncthreads() ... } image[j] = result;

La interaccin entre la CPU y la GPU


! CUDA extiende el lenguaje C con un nuevo tipo de funcin, kernel, que ejecutan en paralelo los hilos activos en GPU. ! El resto del cdigo es C nativo que se ejecuta sobre la CPU de forma convencional. ! De esta manera, el tpico main() de C combina la ejecucin secuencial en CPU y paralela en GPU de kernels CUDA. ! Un kernel se lanza siempre de forma asncrona, esto es, el control regresa de forma inmediata a la CPU. ! Cada kernel GPU tiene una barrera implcita a su conclusin, esto es, no finaliza hasta que no lo hagan todos sus hilos. ! Aprovecharemos al mximo el biprocesador CPU-GPU si les vamos intercalando cdigo con similar carga computacional.

! Palabras clave (keywords):


! threadIdx, blockIdx.

! Funciones intrnsecas (intrinsics):


! __syncthreads

! API en tiempo de ejecucin:


! Memoria, smbolos, gestin de la ejecucin.

// Alojar memoria en la GPU void *myimage; cudaMalloc(&myimage, bytes); // 100 bloques de hilos, 10 hilos por bloque convolve<<<100, 10>>> (myimage);
91

! Funciones kernel para lanzar cdigo a la GPU desde la CPU.

92

La interaccin entre la CPU y la GPU (cont.)


__global__ kernelA(){} __global__ kernelB(){} int main() kernelA <<< dimGridA, dimBlockA >>> (params.); kernelB <<< dimGridB, dimBlockB >>> (params.);

Identificacin de los entes al programar (sobre un ejemplo de suma de matrices)


! Para cada uno de los 4x4 bloques del grid:
Matrices A, B y C de 16x16 elementos

Ejecucin

CPU GPU CPU GPU CPU

! BlockIdx: vector (1D o 2D) que identifica el bloque dentro del grid.

Las tres matrices se particionan igual, otorgando un elemento a cada hilo

! Para cada uno de los 4x4 hilos del bloque:


! ThreadIdx: vector (1D, 2D o 3D) que identifica el hilo dentro de su bloque.
__global__ void matAdd (oat A[N][N],oat B[N][N],oat C[N][N]) { int i = blockIdx.x*blockDim.x + threadIdx.x; int j = blockIdx.y*blockDim.y + threadIdx.y; C[i][j] = A[i][j] + B[i][j]; { int main(){ dim3 dimBlock(4,4); dim3 dimGrid (N/dimBlock.x, N/dimBlock.y); matAdd <<< dimGrid, dimBlock >>> (A, B, C);
93

Qu hilo computa C[15,2]? blockIdx = (3,0) threadIdx = (3,2)


BlockDim.x BlockIdx.y es 0 !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! BlockIdx.x es 3 !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!

blockDim.y

Grid de bloques

Un kernel no comienza su ejecucin en GPU hasta que no hayan finalizado todas las llamadas CUDA anteriores.

94

Control de flujo para el programa


! Cada multiprocesador ejecuta los 32 hilos de un warp segn el paradigma SIMD, esto es, todos los hilos ejecutan la misma instruccin. ! Si las bifurcaciones afectan de forma diferente a los hilos de un warp (divergencia), se secuencializa la ejecucin de los hilos del warp (es SIMD, no SPMD), con la consiguiente prdida de rendimiento. ! Para evitar esto, existen algunos trucos:
if (ThreadIdx.x > 2) { } Con divergencia if (ThreadIdx.x / WARP_SIZE >2) { } Sin divergencia
95

Modificadores para las funciones y lanzamiento de ejecuciones en GPU


! Modificadores para las funciones ejecutadas en la GPU:
! __global__ void MyKernel() { } // Invocado por la CPU ! __device__ oat MyFunc() { } // Invocado por la GPU

! Modificadores para las variables que residen en la GPU:


! __shared__ oat MySharedArray[32]; // En mem. cach ! __constant__ oat MyConstantArray[32];

! Configuracin de la ejecucin para lanzar kernels:


! ! ! ! dim2 gridDim(100,50); // 5000 bloques de hilos dim3 blockDim(4,8,8); // 256 bloques de hilos MyKernel <<< gridDim,blockDim >>> (pars.); // Lanzam. Nota: Opcionalmente, puede haber un tercer parmetro tras blockDim para indicar la cantidad de memoria compartida que ser alojada dinmicamente por cada kernel durante su ejecucin.
96

Variables y funciones intrnsecas


! dim3 gridDim; ! dim3 blockDim; // Dimensin del grid // Dimensin del bloque

Funciones para conocer en tiempo de ejecucin con qu recursos contamos


! Cada GPU disponible en la capa hardware recibe un nmero entero consecutivo que la identifica, comenzando por el 0. ! Para conocer el nmero de GPUs disponibles:
! cudaGetDeviceCount(int* count);

! uint3 blockIdx; // Indice del bloque dentro de la malla ! uint3 threadIdx; // Indice del hilo dentro del bloque ! void __syncthreads(); // Sincronizacin entre threads

! Para conocer los recursos disponibles en la GPU dev (cach, registros, frecuencia de reloj, ...):
! cudaGetDeviceProperties(struct cudaDeviceProp* prop, int dev);

! Para conocer la mejor GPU que rene ciertos requisitos:


! cudaChooseDevice(int* dev, const struct cudaDeviceProp* prop);

El programador debe elegir el tamao del bloque y el nmero de bloques para explotar al mximo el paralelismo del cdigo durante su ejecucin.
97

! Para seleccionar una GPU concreta:


! cudaSetDevice(int dev);

! Para conocer en qu GPU estamos ejecutando el cdigo:


! cudaGetDevice(int* dev);
98

Ejemplo: Salida de la funcin cudaGetDeviceProperties


! El programa se encuentra dentro del SDK de nVidia.

Para gestionar la memoria de vdeo


! Para reservar y liberar memoria en la GPU:
! cudaMalloc(puntero, tamao) ! cudaFree(puntero)

! Para mover reas de memoria entre CPU y GPU:


! En la CPU, declaramos malloc(h_A). ! En la GPU, declaramos cudaMalloc(d_A). ! Y una vez hecho esto:
! Para pasar los datos desde la CPU a la GPU:
! cudaMemcpy(d_A, h_A, numBytes, cudaMemcpyHostToDevice);

! Para pasar los datos desde la GPU a la CPU:


! cudaMemcpy(h_A, d_A, numBytes, cudaMemcpyDeviceToHost);

99

! El prefijo h_ suele usarse para punteros en memoria principal. Idem d_ para punteros en mem. de vdeo.

100

Ejemplo 1: Descripcin del cdigo a programar

IV. 2. Algunos ejemplos

! Alojar N enteros en la memoria de la CPU. ! Alojar N enteros en la memoria de la GPU. ! Inicializar la memoria de la GPU a cero. ! Copiar los valores desde la GPU a la CPU. ! Imprimir los valores.

101

102

Ejemplo 1: Implementacin
int main() { int N = 16; int num_bytes = N*sizeof(int); int *d_a=0, *h_a=0; h_a = (int*) malloc(num_bytes); cudaMalloc( (void**)&d_a, num_bytes); if( 0==h_a || 0==d_a ) printf("No pude alojar la memoria\n"); cudaMemset( d_a, 0, num_bytes); cudaMemcpy( h_a, d_a, num_bytes, cudaMemcpyDeviceToHost); for (int i=0; i<N; i++) printf("%d ", h_a[i] ); free(h_a); cudaFree(d_a); }

Transferencias de memoria asncronas


! Las llamadas a cudaMemcpy() son sncronas, esto es:
! No comienzan hasta que no hayan finalizado todas las llamadas CUDA que le preceden. ! El retorno a la CPU no tiene lugar hasta que no se haya realizado la copia en memoria.

// Punteros en device (GPU) y host (CPU)

! A partir de CUDA Compute Capabilities 1.2 es posible utilizar la variante cudaMemcpyAsync(), cuyas diferencias son las siguientes:
! El retorno a la CPU tiene lugar de forma inmediata. ! Podemos solapar comunicacin y computacin. ! En la seccin de optimizaciones pondremos un ejemplo basado en streams.
103 104

Ejemplo 2: Incrementar un valor b a los N elementos de un vector


Programa C en CPU (compilado con gcc) El kernel CUDA que se ejecuta en la GPU (parte superior), seguido del cdigo host en CPU. Este archivo se compila con nvcc (ver ms adelante).
__global__ void increment_gpu(float *a, float b, int N) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx < N) a[idx] = a[idx] + b; }

Ejemplo 2: Incrementar un valor b a los N elementos de un vector


Con N = 16 y blockDim = 4, tenemos 4 bloques de hilos, encargndose cada hilo de computar un elemento del vector.
Extensiones al lenguaje

void increment_cpu(float *a, float b, int N) { for (int idx = 0; idx<N; idx++) a[idx] = a[idx] + b; }

blockIdx.x = 0 blockDim.x = 4 threadIdx.x = 0,1,2,3 idx = 0,1,2,3

blockIdx.x = 1 blockDim.x = 4 threadIdx.x = 0,1,2,3 idx = 4,5,6,7

blockIdx.x = 2 blockDim.x = 4 threadIdx.x = 0,1,2,3 idx = 8,9,10,11

blockIdx.x = 3 blockDim.x = 4 threadIdx.x = 0,1,2,3 idx = 12,13,14,15


Patrn de acceso comn

void main() { ..... increment_cpu(a, b, N); }

void main() { .. dim3 dimBlock (blocksize); dim3 dimGrid( ceil( N / (float)blocksize) ); increment_gpu<<<dimGrid, dimBlock>>>(a, b, N); } 62
105

int idx = (blockId.x * blockDim.x) + threadIdx.x; Se mapear del ndice local threadIdx al ndice global

Nota: blockDim debera ser >= 32 (warp size) en cdigo real, esto es slo un ejemplo
63
106

Cdigo en CPU para el ejemplo 2 (azul es C, verde es CUDA, rojo son vars.)
// aloja memoria en la CPU unsigned int numBytes = N * sizeof(float); float* h_A = (float*) malloc(numBytes); // aloja memoria en la GPU float* d_A = 0; cudaMalloc((void**)&d_A, numbytes); // copia los datos de la CPU a la GPU cudaMemcpy(d_A, h_A, numBytes, cudaMemcpyHostToDevice); // ejecuta el kernel. B es es nmero de bloques increment_gpu <<< N/blockSize, blockSize >>> (d_A, b); // copia los datos de regreso a la CPU cudaMemcpy(h_A, d_A, numBytes, cudaMemcpyDeviceToHost); // libera la memoria de vdeo cudaFree(d_A);

IV. 3. La librera en tiempo de ejecucin

64

107

108

Composicin del API de CUDA


! En su conjunto, el API (Application Program Interface) de CUDA contiene:
! Dos libreras dinmicas para tratar las extensiones del lenguaje C y las porciones del cdigo que se van a ejecutar en la GPU (device):
! CUDA core library (cuda). ! CUDA runtime library (cudart).

Common Runtime Component (CRC)


! Proporciona:
! Tipos de datos vectoriales:
! [u]char[1..4]. ! [u]short[1..4]. ! [u]int[1..4]. ! [u]long[1..4]. ! float[1..4]. ! Sus elementos pueden accederse mediante los selectores x,y,z,w como hacamos antiguamente en Cg. Ejemplo: uint4 param; int abcisas = param.x;

! Una librera en tiempo de ejecucin estructurada en tres componentes:


! Common Runtime Component. ! Host Runtime Component. ! Device Runtime Component.

! Tipos de datos basados en uint3 para especificar dimensiones: dim3 ! Un subconjunto de la librera runtime C soportada en los cdigos tanto de la CPU como de la GPU para los tipos de datos escalares:
! pow, sqrt, cbrt, hypot. ! exp, exp2, expm1, log, log2, log10, log1p. ! sin, asin, sinh, asinh, cos, acos, cosh, acosh, tan, atan, tanh, atanh. ! ceil, floor, trunc, round.

109

110

Host Runtime Component


! Proporciona funciones para la gestin de:
! Dispositivos GPU, incluyendo sistemas multi-GPU. ! Gestin de memoria. ! Manejo de errores.

Device Runtime Component


! Algunas funciones matemticas (por ejemplo, las trigonomtricas) disponen de variantes mucho ms rpidas que se ejecutan en hardware dedicado en la GPU. Aunque pueden no ser tan precisas como las de CPU, hay multitud de casos (como los clasificadores de imgenes) en los que aceleran notablemente sin degradar el resultado final:
! __sin, __cos, __tan. ! __pow, __exp. ! __log, __log2, __log10.

! Se inicializa la primera vez que se utiliza una de estas funciones runtime.

! Barreras de sincronizacin entre los hilos de un bloque:


! __synchthreads().
111 112

Interoperabilidad con los programadores de los API grficos (OpenGL y Direct3D)


! Manejo de texturas:
! cudaBindTexture() ! cudaBindTextureToArray()

V. Compilacin

! Interoperabilidad con los API grficos:


! Ejemplo con OpenGL: cudaGLMapBufferObject() ! Ejemplo con Direct3D: cudaD3D9MapVertexBuffer()

! Vertex Buffer Objects y Pixel Buffer Objects, tanto de OpenGL como de Direct3D, pueden mapearse sobre el espacio de direcciones de CUDA, y luego se manejan como memoria global en el cdigo de la GPU. ! Los datos de una imagen alojados en Pixel Buffer Objects pueden mostrarse con glDrawPixels/glTexImage2D.

113

114

El proceso global

V. 1. El proceso de compilacin

void funcion_en_CPU( ) { ... } void otras_funcs_CPU(int ...) { ... } void saxpy_serial(float ... ) { for (int i = 0; i < n; ++i) y[i] = a*x[i] + y[i]; } void main( ) { float x; saxpy_serial(..); ... }

Kernels CUDA

Resto del cdigo C

NVCC (Open64)

Compilador de la CPU

Identificar los kernels Ficheros objeto CUDA y rees- Ficheros objeto de la CPU CUDA Enlazador cribirlos para aprovechar paralelismo en GPU Ejecutable

CPU-GPU
115 116

Los diferentes mdulos de compilacin


! El cdigo fuente CUDA se compila con NVCC.
! NVCC separa el cdigo que se ejecuta en CPU del que lo hace en GPU.
C/C++ CUDA Application

Compilador NVCC y mquina virtual PTX


Cdigo fuente
C/C++ CUDA Application
float4 me = gx[gtid]; me.x += me.y * me.z;

! EDG
NVCC Cdigo CPU EDG PTX Code CPU Code

! Separa cdigo GPU y CPU.

! Open64
! Genera ensamblador PTX.

! La compilacin se realiza en dos etapas:


! Virtual: Genera cdigo PTX (Parallel Thread eXecution). ! Fsica: Genera el binario para una GPU especfica (o incluso para una CPU multicore - ver Ocelot 5 diap. ms adelante).

Virtual

! Parallel Thread eXecution (PTX)


Open64

Fsico

PTX to Target

Compiler

! Mquina virtual e ISA. ! Modelo de programacin. ! Recursos y estado de ejecucin.

PTX Code G80 GPU

Cdigo objeto

ld.global.v4.f32 mad.f32
117

{$f1,$f3,$f5,$f7}, [$r9+0]; $f1, $f5, $f3, $f1;


118

NVCC (NVidia CUDA Compiler)


! NVCC es un driver del compilador.
! Invoca a los compiladores y herramientas, como cudacc, g++, cl, ...

! NVCC produce como salida:

Compilation process in Linux: ! Cdigo C para la CPU, que debe luego compilarse con el resto de la aplicacin utilizando otra herramienta. ! Cdigo objeto PTX para la GPU.

V. 2. Compilando para otras plataformas

Compilation process in Windows:

119

120

Compilando cdigo para otras plataformas

Ocelot http://code.google.com/p/gpuocelot
! Entorno de compilacin dinmico para el cdigo PTX sobre sistemas heterogneos que aporta:
! Otras plataformas. ! Anlisis del PTX. !

! En su versin 2.0 de Febrero de 2011 permite que los programas CUDA puedan ejecutarse sobre:
121

! GPUs de AMD/ATI. ! CPUs x86 de Intel.

122

Swan http://www.multiscalelab.org/swan
! Es un traductor fuente-a-fuente de cdigo CUDA a OpenCL:
! Proporciona un API comn que abstrae los soportes en tiempo de ejecucin de CUDA y OpenCL. ! Preserva la comodidad de los lanzamientos de los kernels en CUDA (<<<grid,block>>>) generando cdigo fuente C para las funciones de puntos de entrada a los kernels. ! ... pero el proceso de conversin no es automtico y requiere de intervencin humana.

MCUDA http://impact.crhc.illinois.edu/mcuda.php
! Desarrollado por el grupo de investigacin IMPACT de la Universidad de Illinois. ! Es un entorno de trabajo basado en Linux que trata de portar eficientemente cdigos CUDA a arquitecturas CPU. ! Est disponible para su descarga gratuita... ... pero no parece que la Web est actualizada de forma continua.
123 124

! Resulta til para:


! Evaluar el rendimiento en OpenCL de un cdigo CUDA ya existente. ! Reducir la dependencia del nvcc cuando compilamos cdigo host. ! Soportar mltiples CUDA compute capabilities en un mismo binario. ! Contar con una librera en tiempo de ejecucin para gestionar

Compilador CUDA x86 de PGI http://www.pgroup.com


! Diferencias con las herramientas anteriores:
! No es un traductor desde el cdigo fuente, sino que acta en tiempo de ejecucin. En 2012, permitir crear un binario unificado que simplificar enormemente la distribucin de software.

Accediendo a CUDA desde otros lenguajes


! CUDA puede incorporarse a otro lenguaje que contemple llamadas C/C++. Para simplificar el procesor, podemos utilizar generadores de interfaz de propsito general. ! SWIG [http://swig.org] (Simplified Wrapper and Interface Generator) es la aproximacin ms slida a este respecto. Est activamente soportado, ampliamente utilizado y ya disponible para: AllegroCL, C#, CFFI, CHICKEN, CLISP, D, Go language, Guile, Java, Lua, MxScheme/Racket, Ocaml, Octave, Perl, PHP, Python, R, Ruby, Tcl/Tk. ! Tambin disponemos de una conexin con Matlab:
! En una GPU: Utilizar Jacket (plataforma para computacin numrica). ! En mltiples GPUs: Utilizar MatWorks Parallel Computing Toolbox.
125 126

! Principales ventajas:
! Velocidad: El cdigo compilado es capaz de ejecutarse en una plataforma x86 que incluso no disponga de GPU. Esto otorga al compilador la facultad de vectorizar el cdigo para instrucciones SSE de 128 bits o sus sucesoras AVX de 256 bits. ! Transparencia: Incluso aquellas aplicaciones que utilizan recursos nativos de la GPU como las unidades de texturas tendrn un comportamiento idntico en CPU y GPU.

OpenACC: Una alternativa a la computacin cientfica CUDA para el programador medio


! Es un nuevo estndar para programacin paralela basado en directivas (como OpenMP), que:
! Se insertan en programas C, C++ o Fortran programs. ! Orientan al compilador para paralelizar ciertas secciones del cdigo.

OpenACC (cont.)
! Las directivas proporcionan una base de cdigo comn
! Multi plataforma. ! Multi firma comercial.

! Objetivo: Orientado a un programador medio, el cdigo es portable a otros aceleradores, e incluso CPUs multicore. ! Primeros desarrollos y difusin comercial a cargo de:
! Portland Group (PGI). ! Cray.

! Esto abre un camino ideal para preservar la inversin en aplicaciones heredadas, permitiendo una fcil migracin hacia la computacin acelerada. ! La cuestin clave es Cunto rendimiento sacrificamos?
! Los primeros informes indican 5-10% respecto a CUDA en algunos casos. ! Otras aplicaciones han logrado una ganancia de 5x invirtiendo nicamente el tiempo de una semana o incluso un solo da.

! Primeros centros de supercomputacin en utilizarlo:


! Estados Unidos: Oak Ridge National Lab. ! Europa: Swiss National Supercomputing Centre.
127

! Additional information: [http://www.OpenACC-standard.org]


128

El ltimo paso dado por nVidia para facilitar la interrelacin de CUDA: Abrir el compilador
! Al liberar el cdigo fuente (Dic11), resulta muy cmodo y eficiente conectar con otros lenguajes por encima y arquitecturas por debajo.

VI. Depuracin y optimizacin

129

130

nVidia Parallel Nsight (v. 1.5 en 2011)


! Herramienta para la depuracin y optimizacin (debugging & profiling) integrada en MS Visual Studio. ! Disponible para Windows Vista y Windows 7. ! Permite depuracin remota y local para:
! CUDA C/C++, Direct Compute, HLSL.

Depuracin con nVidia Parallel Nsight

! Localizador de errores en acceso a memoria (memchecker)


! CUDA C/C++.

! Optimizacin (profiling) para:


! CUDA C/C++, OpenCL, Direct Compute.

131

15

132

Profiling con Nvidia Parallel Nsight

Depuracin: Modo de emulacin del dispositivo (Device Emulation Mode)


! Un ejecutable compilado en modo de emulacin del dispositivo (nvcc -deviceemu) corre ntegramente en la CPU (host) usando el soporte en tiempo de ejecucin de CUDA sin necesidad de GPU ni driver. ! Posibilidades de modo de emulacin:
! Utilizar el soporte para la depuracin de que disponga la CPU (breakpoints, inspeccin de registros, etctera). ! Acceder a los datos de la GPU desde el cdigo de la CPU. ! Llamar a cualquier funcin de la CPU desde el cdigo de la GPU (por ejemplo, printf) y viceversa. ! Detectar situaciones de interbloqueo por el uso indebido de __syncthreads.
133 134

Carencias del modo de emulacin del dispositivo


! Los hilos del cdigo se ejecutan de forma secuencial, por lo que el acceso simultneo a la misma posicin de memoria desde mltiples hilos produce (potencialmente) resultados distintos. ! El acceso a valores mediante punteros de GPU en la CPU o punteros de CPU en la GPU puede producir resultados correctos en el modo de emulacin, pero dar un error al ser ejecutado. ! Los resultados de la computacin en punto flotante diferirn ligeramente debido a:
! Las diferentes salidas del compilador. ! Diferentes conjuntos de instrucciones. ! El uso de precisin extendida para la generacin de resultados intermedios.
135

CUDA Visual Profiler


! Proporciona ciertas mediciones estratgicas para la localizacin de problemas de rendimiento.
! Temporizacin entre GPU y CPU para las invocaciones a los kernels y las llamadas a memcpy. ! Evolucin a determinados pasos de tiempo.

! Acceso a contadores de rendimiento hardware.

136

Contadores que proporciona el profiler


! Se sigue la pista de ciertos eventos definidos por el programa a travs de contadores hardware que registran determinadas seales emitidas por el chip: ! ! ! ! gld_incoherent gld_coherent gst_incoherent gst_coherent

Interpretacin de los contadores del profiler


! Representan los eventos dentro de un warp de hilos. ! Involucran a un solo multiprocesador
! Los valores no se correspondern con el nmero total de warps lanzados para un kernel dado. ! Debemos lanzar un nmero suficiente de bloques de hilos para asegurar que el multiprocesador involucrado tiene asignado un porcentaje consistente del trabajo total.

Cargas/almac. en memoria global son coherentes (coalesced) o incoherentes (non-coalesced)

! local_load ! local_store ! branch ! divergent_branch

Cargas y almacenamientos locales Nmero total de bifurcaciones (branches) y bifurcaciones divergentes tomadas por los hilos

! Los valores se utilizan principalmente para cuantificar la mejora de rendimiento producida por una versin optimizada del cdigo.
! Se trata de reducir gld/gst_incoherent, divergent_branch y warp_serialize.
137 138

! instructions cuenta del nmero de instrucciones ejecutadas. ! warp_serialize warps de hilos que se han sido secuencializados por los conflictos de direccionamiento a la memoria compartida o a la memoria de constantes. ! cta_launched bloques de hilos que han sido ejecutados.

Vdeo ilustrativo del uso del CUDA Visual Profiler (4 min. 34 seg.)

Maximizar la ocupacin para ocultar la latencia


! Dnde se producen las latencias:
! En el acceso a memoria global: 400-600 ciclos. ! En dependencias read-after-write entre registros.
! El resultado de las instrucciones slo puede leerse 11 ciclos ms tarde.

! Se bloquean las instrucciones que presentan dependencias en el mismo hilo, pero no las que se encuentran en otros hilos. ! Para ocultar esta latencia, debemos ejecutar tantos hilos por multiprocesador como sea posible, eligiendo los parmetros de ejecucin que maximicen:
ocupacin = (n warps activos)/(mx. n warps activos)
139

Nota: El mximo n de warps activos en las series 8, 9 y 10 de GeForce es de 24, y en Fermi es de 48.

140

Restricciones en la configuracin de la ejecucin


! Mximo n de hilos por bloque: 512 ! N de hilos activos limitados por los recursos.
! N de registros por multiprocesador: 8192 16384 (serie 10 en adelante). ! Cantidad de memoria compartida por multiprocesador: 16K 48K (Fermi en adelante).

Para conocer la utilizacin de los recursos


! Compilar el cdigo del kernel con el flag -cubin para conocer cmo se estn usando los registros.
! Alternativa on-line: nvcc --ptxas-options=-v

! Abrir el archivo de texto .cubin y mirar la seccin code.


architecture {sm_10} Memoria local para cada hilo abiversion {0} (usada por el compilador para modname {cubin} volcar contenidos de los registros code { en memoria) name = myGPUcode lmem = 0 Memoria compartida usada smem = 68 por cada bloque de hilos reg = 20 bar = 0 Registros usados bincode { 0xa0004205 0x04200780 0x40024c09 0x00200780 por cada hilo
141 142

! Utilizar el flagmaxrregcount=N en NVCC


! N = n mximo de registros deseado por kernel. ! En algn momento puede sobrevenir un spilling a LMEM (trasvase entre el banco de registros y la memoria local que emplea el compilador para su uso interno): ! Merma el rendimiento (LMEM es lento). ! Rastrear el archivo .cubin para descubrir el uso de LMEM (ver sig.).

Heursticos para la configuracin de la ejecucin


! El nmero de hilos por bloque debe ser un mltiplo de 32.
! Para no desperdiciar en la ejecucin de warps incompletos

Heursticos para la configuracin de la ejecucin (2)


! Reglas generales:
! (1) Pensar en muchos bloques. ! (2) Pensar en hilos ligeros.
! Para que el cdigo sea escalable en generaciones futuras. ! Para que los bloques puedan ser procesados en pipeline.

! El nmero de bloques debe superar al de multiprocs.


! Para que todos ellos tengan al menos un bloque que ejecutar.

! Los recursos por bloque (registros y memoria compartida) deben ser al menos la mitad del total disponible.
! De lo contrario, resulta mejor fusionar bloques.

! El nmero de bloques debe superar en ms del doble al de multiprocesadores.


! Para tener siempre ms de un bloque activo por multiprocesador que garantice su ocupacin en caso de que alguno se bloquee.

! El nmero de bloques debe superar el centenar.


! Para que el cdigo sea escalable en futuras generaciones.
143 144

Heursticos para la configuracin de la ejecucin (3)


! Conflicto: Ms hilos por bloque significa mejor ocultacin de latencia, pero menos registros por hilo. ! Sugerencia: Utilizar un mnimo de 64 hilos por bloque, o incluso mejor, 192 256 hilos (si an disponemos de suficientes registros). ! Conflicto: Incrementar la ocupacin no significa necesariamente aumentar el rendimiento, pero una baja ocupacin del multiprocesador no permite ocultar latencias en kernels limitados por el ancho de banda a memoria. ! Sugerencia: Prestar atencin a la intensidad aritmtica y al paralelismo disponible.
145

Parametrizacin de una aplicacin


! Todo lo que concierne al rendimiento es dependiente de la aplicacin, por lo que hay que experimentar con ella para lograr resultados ptimos. ! Las GPUs evolucionan en muchos aspectos:
! El nmero de multiprocesadores. 16 en la serie 8 de GeForce, 30 en la serie 10, 16 de nuevo en Fermi (pero con 32 cores cada uno). ! Ancho de banda con memoria: En torno a 100 GB/s. ! Tamao de la memoria compartida: 16 KB. para cada multiprocesador, salvo en Fermi, que puede ampliarse a 48 KB. ! Tamao del banco de registros: 8K para cada multiprocesador(G80), 16K (GT200), 32K (Fermi). ! Hilos: Comprobar el lmite por bloque y el lmite total.
! Por bloque: 512 (G80), 512 (GT200), 1024 (Fermi). ! Total: 768 (G80), 1024 (GT200), 1536 (Fermi).
146

CUDA Occupancy Calculator


! Asesora en la seleccin de los parmetros de configuracin
! http://developer.download.nvidia.com/compute/cuda/CUDA_Occupancy_calculator.xls

Para alcanzar el mayor grado de paralelismo, fijarse en el rea naranja del CUDA Occup.
! El primer dato es el nmero de hilos por bloque:
! Pondremos 256 como primer valor estimativo. ! El lmite oscila entre 512 y 1024 segn la arquitectura.

! El segundo dato es el n de registros que gasta cada hilo.


! Esto lo obtenemos del archivo .cubin. ! El lmite oscila entre 8K (G80), 16K (GT200) o 32K (Fermi) para cada multiprocesador, as que consumiendo 10 registros/hilo es posible ejecutar 768 hilos en la GPU G80, o sea, 3 bloques de 256 hilos en cada multiprocesador: 3 x 256 x 10 = 7680 < 8192 registros. ! Si hubiramos consumido 11 registros/hilo, ya no podramos acomodar 3 bloques, sino slo 2, por lo que perderamos 1/3 del paralelismo. => Habra que esforzarse en reducir de 11 a 10.
147 148

Para alcanzar el mayor grado de paralelismo, fijarse en el rea naranja del CUDA Occup.
! El tercer dato es el gasto de memoria compartida en cada bloque de hilos:
! Esto tambin lo obtenemos del archivo .cubin, aunque podemos llevar una contabilidad manual, ya que todo depende de la declaracin de variables __shared__ elegida por nosotros. ! El lmite es de 16 KB., y en Fermi, podemos escoger tambin 48 KB. ! Para el caso anterior sobre la G80, no gastaremos ms de 5 KB de memoria compartida por cada bloque para que podamos ejecutar el mximo de 3 bloques en paralelo en cada multiprocesador:
! 3 x 5 KB. = 15 KB < 16 KB.

VII. Estrategias de mejora

! A partir de 5.33 KB. de memoria compartida usada por cada bloque, estamos sacrificando un 33% del paralelismo, igual que suceda antes si no ramos capaces de bajar a 10 registros/kernel.
149 150

(1) Solapamiento de computacin y comunicaciones


! Posibilidades:
! Solapar una transferencia de datos a la GPU con la computacin en CPU de una funcin que se invoca justo a continuacin. Aprovecha que el lanzamiento de kernels desde la CPU es asncrono (ver ejemplo 1 en la siguiente diapositiva). ! Solapar una transferencia de datos CPU ! GPU con la computacin de un kernel en GPU. Slo es posible a partir de CCC 1.1. Ms complejo de implementar, pues necesitamos tres cosas:
1. Alojar con cudaMallocHost() memoria pinned en CPU (para que no pagine). 2. Definir streams (listas de operaciones CUDA que se ejecutan secuencialmente). 3. Transferir los datos CPU ! GPU usando las variantes asncronas (cudaMemcpyAsync(dst,src,size,dir,stream)), que devuelven inmediatamente el control a la CPU, para desde ah lanzar el kernel que se ejecutar en la GPU en paralelo con la transferencia entre CPU y GPU (ver ej. 2).
151

(1) Solapamiento de computacin y comunicaciones (cont.)


! Ejemplo 1 (caso sencillo). Notar que 0 es el stream por defecto:
cudaMemcpyAsync(A_d, A_h, size, cudaMemcpyHostToDevice, 0); MyGPUkernel <<< grid, block >>> (A_d); MyCPUfunction();

! Ejemplo 2 (el caso complejo). Notar que las dos ltimas sentencias no suponen solape adicional:
cudaStreamCreate(&stream1); cudaStreamCreate(&stream2); cudaMemcpyAsync(dst, src, size, dir, stream1); kernel<<<grid, block, 0, stream2>>>(); cudaMemcpyAsync(dst2, src2, size, stream2); cudaStreamQuery(stream2);

solape

solape
152

(2) Optimizar el uso de la memoria


! Minimizar las transferencias entre CPU y GPU.
! Ya que este ancho de banda es muy inferior al de la memoria de vdeo. Por ejemplo, segn nuestra Fermi (GeForce GTX 480):
! Host to Device Bandwidth: 2.25 GB/sg. Device to Host Bandwidth: 2.00 GB/sg. ! Device to Device Bandwidth: 117.83 GB/sg.

(3) Uso de la memoria compartida


Si el tamao se conoce en tiempo de compilacin
__global__ void kernel (...) { ... __shared__ float sData[256]; ... } void main() { ... kernel<<<nBlocks,blocksize>>>(...); ... } } ...

Si el tamao se desconoce en tiempo de compilacin


__global__ void kernel (...) { ... extern __shared__ float sData[]; ... } void main() { ... smBytes = blockSize * sizeof(float); kernel<<<nBlocks,blocksize,smBytes>>>(...);

! Si se quiere aumentar el ancho de banda, usar memoria pinned (sin abusar), que aprovecha mejor PCI-express (3.2 GB/s. en v1.0 y 5.2 GB/s. en v2.0, hasta 4 y 8 GB/s. si usamos el chipset nForce 680i).

! Agrupar las transferencias de datos entre CPU y GPU


! Ya que la latencia predomina en el coste sobre el ancho de banda.

! Pasar algunas funciones de CPU a GPU aunque no puedan explotar mucho paralelismo
! Si eso evita un doble trasiego de datos de GPU a CPU y regreso.
153

154

(4) Eliminar los conflictos en el acceso a los 16 bancos de memoria compartida


! Se dispone de un espacio total de 16/48(*) KB. en cada multiproc. ! Se estructura en 16/32(*) bancos con palabras de 32 bits y entrelazado de orden inferior. ! (*) Valores mejorados en Fermi. ! Para el acceso simultneo de los hilos a memoria en cada medio warp, debe darse alguna de las siguientes condiciones: 1. Que los hilos accedan a bancos diferentes (cualquier permutacin). 2. Que todos los hilos accedan a la misma palabra de un banco.
Caso 1: Caso 2:

(5) Seleccionar 16 o 48 KB de memoria shared en Fermi (L1 es el resto hasta 64 KB)


! Es la eleccin que nos delata como programador currante o laxo. Si bien muchos cdigos heredados de familias anteriores son nativos para la primera opcin, la que CUDA toma por defecto es la segunda. ! La seleccin se realiza mediante la siguiente llamada CUDA en el host:
cudaError_t cudaFuncSetCacheCong (const char func, enum cudaFuncCache);

! Ejemplo: ! Para ejecutar nuestro kernel bajo una config. Fermi de 16 KB de shared:
cudaFuncSetCacheCong (mykernel, cudaFuncCachePreferL1);

! Y para hacerlo con 48 KB de shared memory (y solo 16 KB de L1):


cudaFuncSetCacheCong (mykernel, cudaFuncCachePreferShared);
155 156

Resumen de las optimizaciones de memoria


! El ancho de banda efectivo puede variar un orden de magnitud dependiendo del patrn de acceso si sabemos utilizar las siguientes armas:
! Accesos coalesced a memoria global (menos importante en Fermi). ! Accesos a memoria compartida sin conflictos a sus 16 bancos. ! Accesos a memoria de texturas (que pasa por cach). ! Accesos a memoria de constantes que tienen una misma direccin.

VIII. Bibliografa y herramientas

! Recordar que:
! Procesar datos es ms rpido que moverlos, ya que las GPUs dedican muchos ms transistores a las ALUs que a la memoria. ! Cuanto menos ahogado se encuentre un kernel por el acceso a memoria, mejor se comportar en las futuras arquitecturas GPU.
157 158

Bibliografa
! GPU Gems, Gems 2 y Gems 3. ! CUDA Programming Guide. Las bases de CUDA. ! CUDA Best Practices Guide. Para optimizar cdigo. ! CUDA Zone (http://www.nvidia.com/cuda).
! Los cdigos que se han desarrollado en CUDA junto a los factores de aceleracin logrados. ! Los artculos de investigacin que describen las aplicaciones y su implementacin. ! Tutoriales, forums, cursos de programacin paralela, ...

Herramientas software
! CUDA Toolkit, ya en su versin 3.0 para Fermi.
! Compilador nvcc, libreras y documentacin. ! Implementaciones disponibles para Linux, MacOS y Windows 7.

! CUDA SDK:
! Scripts de compilacin y algunas utilidades. ! Cdigos de ejemplo y whitepapers.

! GPGPU: El movimiento originario de la programacin de propsito general sobre la GPU que desde 2003 aglutina las novedades ms interesantes. http:///www.gpgpu.org

159

! CUDA Occupancy Calculator. ! CUDA Profiler: Analiza tiempos de ejecucin, accesos coalesced a memoria, warp divergentes, conflictos en el acceso a memoria compartida, ... ! nVidia Parallel Nsight: Entorno de desarrollo integrado en Visual Studio.

160

Todo est en nuestra pgina Web: http://cursos.ujaldon.es

161

Anda mungkin juga menyukai