Computación de altas prestaciones con GPUs

82
Computación de altas prestaciones con GPUs Cursos de verano 2010 Enrique Arias Antúnez José Luis Sánchez García Presente y futuro de los sistemas de computación 1

description

Presente y futuro de los sistemas de computación. Cursos de verano 2010. Computación de altas prestaciones con GPUs. Enrique Arias Antúnez José Luis Sánchez García. Índice. Introducción Arquitectura CUDA Modelo de ejecución Programación Rendimiento. Computación de altas prestaciones. - PowerPoint PPT Presentation

Transcript of Computación de altas prestaciones con GPUs

Page 1: Computación de altas  prestaciones con GPUs

Computación de altas prestaciones con GPUs

Cursos de

verano 2010

Enrique Arias Antúnez

José Luis Sánchez García

Presente y futuro de los sistemas de computación

1

Page 2: Computación de altas  prestaciones con GPUs

Índice

• Introducción• Arquitectura CUDA• Modelo de ejecución• Programación• Rendimiento

2

Page 3: Computación de altas  prestaciones con GPUs

Computación de altas prestaciones

• Los grandes supercomputadores son la única alternativa para cierto tipo de aplicaciones

• Sin embargo, hay aplicaciones con menos exigencias computacionales

• GPGPU– Utilizar GPUs para aplicaciones

de propósito general– Muy atractivo en términos de

rendimiento, consumo y coste– Exclusividad

3

Page 4: Computación de altas  prestaciones con GPUs

Introducción

Rendimiento

Fuente: Nvidia 4

Page 5: Computación de altas  prestaciones con GPUs

Introducción

2 x Xeon QuadCore 2 x Xeon QuadCore + Tesla s1070

Cores 8 8 + 240*4 = 968

Rendimiento pico (sp) 0,17 TFlops 4,14 TFlops

Rendimiento pico (dp) 0,08 TFlops 0,35 TFlops

Precio 2400 euros 9000 euros

Consumo 700 W 1500 W

Precios

Más de 100 millones de GPUs de Nvidia

5

Page 6: Computación de altas  prestaciones con GPUs

Introducción

Uso de los transistores

+ Cache

+ Control

- Cores

+ Cores

- Cache

- Control 6

Page 7: Computación de altas  prestaciones con GPUs

Introducción

Aplicación Descripción Código Kernel % tiempo

H.264 SPEC ‘06 version, change in guess vector 34.811 194 35%

LBM SPEC ‘06 version, change to single precision and print fewer reports

1.481 285 >99%

RC5-72 Distributed.net RC5-72 challenge client code

1.979 218 >99%

FEM Finite element modeling, simulation of 3D graded materials

1.874 146 99%

RPES Rye Polynomial Equation Solver, quantum chem, 2-electron repulsion

1.104 281 99%

PNS Petri Net simulation of a distributed system 322 160 >99%

SAXPY Single-precision implementation of saxpy, used in Linpack’s Gaussian elim. routine

952 31 >99%

TRACF Two Point Angular Correlation Function 536 98 96%

FDTD Finite-Difference Time Domain analysis of 2D electromagnetic wave propagation

1.365 93 16%

MRI-Q Computing a matrix Q, a scanner’s configuration in MRI reconstruction

490 33 >99%7

Page 8: Computación de altas  prestaciones con GPUs

Introducción

0

10

20

30

40

50

60

H.264 LBM RC5-72 FEM RPES PNS SAXPY TPACF FDTD MRI-Q MRI-FHD

KernelApplication

210 457431

316263

GP

U S

pee

dup

Rel

ativ

e to

CP

U

79

• GeForce 8800 GTX vs. 2.2GHz Opteron 248 • 10 speedup en un kernel es típico, si hay suficiente paralelismo• 25 a 400 speedup con optimizaciones• Depende mucho del tipo de aplicación

8

Page 9: Computación de altas  prestaciones con GPUs

Introducción

Programabilidad

2005 2009

Ensamblador

OpenGL

DirectX

Cg

Brook++

RapidMind

CUDA OpenCL

Fac

ilida

d de

pr

ogra

mac

ión

Inicios GPGPUATI Stream

Explosión GPGPU

Futuro GPGPU?

9

Page 10: Computación de altas  prestaciones con GPUs

Índice

Introducción• Arquitectura CUDA• Modelo de ejecución• Programación• Rendimiento

10

Page 11: Computación de altas  prestaciones con GPUs

Arquitectura CUDA

. . .

Memoria global

Conjunto de Streaming Multiprocessors (MP)

SP

DP

SM

8 Scalar Processors (SP)

1 Unidad de Doble Precisión (DP)

16 KB de Memoria Compartida (SM)

8-16 K Registros11

Page 12: Computación de altas  prestaciones con GPUs

Fermi

~1.5TFLOPS (SP)~800GFLOPS (DP)230 GB/s DRAM

12

Page 13: Computación de altas  prestaciones con GPUs

Espacios de memoria en CUDA

13

Fuente: Nvidia

Page 14: Computación de altas  prestaciones con GPUs

Espacios de memoria en CUDA

Memoria Localización Cache Acceso Ámbito Vida

Registros On-chip N/A R/W Un thread Thread

Compartida On-chip N/A R/W Threads en bloque

Bloque

Global Off-chip No R/W Todos threads y host

Aplicación

Constantes Off-chip Sí R Todos threads y host

Aplicación

Texturas Off-chip Sí R Todos threads y host

Aplicación

14

Page 15: Computación de altas  prestaciones con GPUs

Gestión de memoria con CUDA

• CPU y GPU tienen espacios de memoria independientes

• Transferencia de datos entre ambos espacios de memoria a través del bus PCIExpress

• Reserva, transferencia y liberación explícitas

• Las operaciones con memoria realizadas por el host

15Fuente: Nvidia

Page 16: Computación de altas  prestaciones con GPUs

Gestión de memoria con CUDA

• cudaMalloc()– Obtiene espacio en la

memoria global– Parámetros: dirección del

puntero y el tamaño a reservar• cudaMemset()

– Inicializa a un valor dado– Parámetros: dirección, valor y

cantidad• cudaFree()

– Libera el espacio– Parámetros: dirección

16Fuente: Nvidia

Page 17: Computación de altas  prestaciones con GPUs

Gestión de memoria con CUDA

• cudaMemcpy()– Transfiere datos entre

memorias– Requiere 4 parámetros:

• Puntero al destino• Puntero al origen• Bytes a copiar• Tipo de transferencia:

– Host a host– Host a dispositivo– Dispositivo a host– Dispositivo a dispositivo

17Fuente: Nvidia

Page 18: Computación de altas  prestaciones con GPUs

• Ejemplo– Reservar, inicializar y liberar espacio para una matriz

64x64 de elementos float– Enlazar ese espacio a Md

Gestión de memoria con CUDA

#define BLOCK_SIZE 64float * Md;int size = BLOCK_SIZE*BLOCK_SIZE*sizeof(float);cudaMalloc((void**)&Md,size);cudaMemset(Md,0,size); . . .cudaFree(Md);

18

Page 19: Computación de altas  prestaciones con GPUs

• Ejemplo– Transferir una matriz de 64x64 float de la memoria

de la GPU a la del host y viceversa– Matriz M está en el host y la matriz Md en el

dispositivo

Gestión de memoria con CUDA

cudaMemcpy(M,Md,size,cudaMemcpyDeviceToHost);

cudaMemcpy(Md,M,size,cudaMemcpyHostToDevice);

19

Page 20: Computación de altas  prestaciones con GPUs

Índice

IntroducciónArquitectura CUDA• Modelo de ejecución• Programación• Rendimiento

20

Page 21: Computación de altas  prestaciones con GPUs

Modelo de ejecución

Secuencial

Código

Thread

21

Page 22: Computación de altas  prestaciones con GPUs

Modelo de ejecución

Paralelo

Código

Threads

22

Page 23: Computación de altas  prestaciones con GPUs

kernel

Paralelo

Miles de threads

lanzados a la vez

23

Page 24: Computación de altas  prestaciones con GPUs

Modelo de ejecución

Memoria

Kernel Threads (instancias del

kernel)PCIe

CPU GPU

24

Page 25: Computación de altas  prestaciones con GPUs

Modelo de ejecución

CPU GPU

• Cada thread tiene un identificador• Todos los threads ejecutan el mismo código• Cada thread opera sobre distintos datos

• Modelo SIMD

CPU

• Threads pesados• Sobrecarga

planificación• Cambios de

contexto lentos

CPU

• Threads ligeros• Poca sobrecarga

planificación• Cambios de

contexto rápidos

25

Page 26: Computación de altas  prestaciones con GPUs

Estructura jerárquica de threads

• Los threads se agrupan en bloques de threads• Los bloques de threads se agrupan en Grids• Los threads de un bloque se comunican mediante la

memoria compartida • Todos los threads del Grid se comunican a través de

la memoria global

• Un thread se ejecuta en un procesador escalar (SP)• Un bloque de threads se lanza en un

multiprocesador (MP)• Un MP puede desarrollar varios bloques

Grid 1

Bloque(2,1)

Bloque(2,0)

Bloque(0,0)

Bloque(1,0)

Bloque(0,1)

Bloque(1,1)

Thread(0,0)

Thread(1,0)

Thread(2,0)

Thread(3,0)

Thread(4,0)

Thread(0,1)

Thread(1,1)

Thread(2,1)

Thread(3,1)

Thread(4,1)

Thread(0,2)

Thread(1,2)

Thread(2,2)

Thread(3,2)

Thread(4,2)

Bloque (1,1)

26

Page 27: Computación de altas  prestaciones con GPUs

Modelo de ejecución

Software Hardware

Thread SP

• Un bloque se ejecuta en un MP• Los bloques no migran entre MPs• Varios bloques a la vez en un MP

(según registros y memoria compartida)

Bloque threads MP

Grid GPU

• Un único kernel concurrente

• Cada thread se ejecuta en un SP

27

Page 28: Computación de altas  prestaciones con GPUs

Indentificadores y dimensiones

• El tamaño del grid y de los bloques los determina el programador

• Se usan las variables gridDim y blockDim para referenciar la dimensión de grid y bloque, respectivamente

Grid 1

Bloque(2,1)

Bloque(2,0)

Bloque(0,0)

Bloque(1,0)

Bloque(0,1)

Bloque(1,1)

Thread(0,0)

Thread(1,0)

Thread(2,0)

Thread(3,0)

Thread(4,0)

Thread(0,1)

Thread(1,1)

Thread(2,1)

Thread(3,1)

Thread(4,1)

Thread(0,2)

Thread(1,2)

Thread(2,2)

Thread(3,2)

Thread(4,2)

Bloque (1,1)

gridDim.x

grid

Dim

.y

• Un thread queda indentificado por:– Un identificador propio dentro del bloque al que

pertenece– El identificador del bloque al que pertenece

• Se usan las variables threadIdx y blockIdx para referenciar el identificador del thread dentro del bloque y al bloque dentro del grid, respectivamente

blockDim.x

blo

ckD

im.y

blockDim.z

blockIdx.x=1blockIdx.y=1

threadIdx.x=0threadIdx.y=2

28

Page 29: Computación de altas  prestaciones con GPUs

Planificación

• Se agrupan los threads en bloques• Se asignan identificadores a bloques y threads• Se distribuyen los bloques de threads entre los

multiprocesadores• Los threads de un bloque se ejecutan

concurrentemente en un multiprocesador

tiem

po

warp 8 instrucción 11

warp 1 instrucción 42

warp 3 instrucción 95

warp 8 instrucción 12

...

warp 3 instrucción 96

Bloque 1 Bloque 2 Bloque n

warp 12

m

warp 12

m

warp 12

m

• Los threads de un bloque son agrupados en warps• Un warp es la unidad mínima de planificación y está

formada por 32 threads• Varios warps en cada multiprocesador, pero sólo

uno está en ejecución en cada momento• Los warps cuyos operandos están listos son

seleccionados para ejecución• Todos los threads en un warp ejecutan la misma

instrucción29

Page 30: Computación de altas  prestaciones con GPUs

Planificación• Tres flujos de instrucciones:

warp1, warp3 y warp8

t=k

t=k+1

t=k+2

t=l>k

t=l+1

Warp Instrucciónactual

Estado de laInstrucción

Warp 1 42 Computando

Warp 3 95 Computando

Warp 8 11 Operandos preparados

Planificaen tiempo k

warp 8 instrucción 11

warp 1 instrucción 42

warp 3 instrucción 95

warp 8 instrucción 12

...

warp 3 instrucción 96

30

Page 31: Computación de altas  prestaciones con GPUs

Planificación

warp 8 instrucción 11

warp 1 instrucción 42

warp 3 instrucción 95

warp 8 instrucción 12

...

warp 3 instrucción 96

t=k

t=k+1

t=k+2

t=l>k

t=l+1

Warp CurrentInstruction

InstructionState

Warp 1 42 Preparadoescribir result.

Warp 3 95 Computando

Warp 8 11 Computando

Planifica entiempo k+1

• Tres flujos de instrucciones: warp1, warp3 y warp8

31

Page 32: Computación de altas  prestaciones con GPUs

Índice

IntroducciónArquitectura CUDAModelo de ejecución• Programación• Rendimiento

32

Page 33: Computación de altas  prestaciones con GPUs

Programación

• Computación heterogénea– Parte de código se ejecuta en la CPU– Parte de código se ejecuta en la GPU (kernel)

• La API de CUDA es una extensión al lenguaje ANSI C– Curva de aprendizaje suave

• Extensiones básicas– Modificadores de función– Modificadores de variables– Variables específicas de dimensionado– Directiva para ejecución del kernel

33

Page 34: Computación de altas  prestaciones con GPUs

Modificadores de función

Indica dónde se ejecuta la función: GPU (device) o CPU (host)

__device__ La función debe ejecutarse en el dispositivo

• Sólo puede ser llamada por el propio dispositivo

• Recursividad no soportada

• No pueden declararse variables estáticas dentro de la función

• La función no puede tener un número variable de argumentos

__global__ La función es un kernel que debe ejecutarse en el dispositivo

• Sólo puede ser llamada por el host

• Recursividad no soportada

• No pueden declararse variables estáticas dentro de la función

• La función no puede tener un número variable de argumentos

• La función debe devolver siempre void

__host__ La función debe ejecutarse en el host

• Sólo puede ser llamada por el host

• No puede utilizarse junto con __global__

34

Page 35: Computación de altas  prestaciones con GPUs

Modificadores de variables

Indica en qué parte de la memoria se localiza la variable

__device__ La variable reside en el dispositivo

• Requiere que se indique uno de los otros dos modificadores de variables para indicar dónde exactamente reside la variable en el dispositivo

__constant__ La variable reside en el espacio de memoria constante del dispositivo

• Está viva durante todo el tiempo de ejecución de la aplicación• Accesible por todos los threads del grid, así como desde el host

__shared__ La variable reside en el espacio de memoria compartida del bloque de threads en el dispositivo

• Está viva mientras el bloque está vivo• Accesible sólo por los threads del bloque

35

Page 36: Computación de altas  prestaciones con GPUs

Variables específicas dimensiones

Indican las dimensiones que caracterizan a los identificadores de los threads y bloques

• dim3 gridDim;

– Dimensiones de los grids en bloques (gridDim.z no usado)

• dim3 blockDim;

– Dimensiones del bloque en threads

• dim3 blockIdx;

– Indice del bloque en el grid

• dim3 threadIdx;

– Indice del thread en el bloque

36

Page 37: Computación de altas  prestaciones con GPUs

Directiva ejecución del kernel

Indica cómo debe ejecutarse el kernel en el dispositivo

– Cada vez que se llama a __global__, también debe especificarse la configuración de ejecución del kernel

– Se inserta entre el nombre de la función y el paréntesis del argumento una expresión de la forma: <<< Dg, Db, Ns, S >>>

• Dg: indica las dimensiones de la malla, es decir, la cantidad de bloques• Db: indica las dimensiones de cada bloque, es decir, el número de threads por

bloque• Ns: indica el número de bytes de memoria compartida asignada dinámicamente

por bloque (argumento opcional, 0 por defecto)• S: especifica un stream (argumento opcional, 0 por defecto)

– Las invocaciones de kernels son asíncronas• El control vuelve al programa principal (CPU)

37

Page 38: Computación de altas  prestaciones con GPUs

Ejecución del kernel

• Un kernel debe ser ejecutado del siguiente modo:

__global__ void KernelFunction(…);

dim3 DimGrid(100,50); // 5000 bloques

dim3 DimBlock(4,8,8); // 256 threads/bloque

KernelFunction<<< Dimgrid,Dimblock >>>(…);

38

Page 39: Computación de altas  prestaciones con GPUs

Ejemplo: SAXPY// Definición de la funciónvoid saxpy_serial (int n, float a, float *x, float *y){ for (int i = 0; i < n; ++i) y[i] = a*x[i] + y[i];}

// Llamada a la funciónsaxpy(n,2.0,x,y)

// Definición del kernel__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];}

// Llamada al kernelint nblocks = (n + 255) / 256;saxpy_parallel<<<nblocks, 256>>>(n, 2.0, x, y); 39

Page 40: Computación de altas  prestaciones con GPUs

Índice

IntroducciónArquitectura CUDAModelo de ejecuciónProgramación• Rendimiento

40

Page 41: Computación de altas  prestaciones con GPUs

Optimizaciones

• Reducir sobrecargas en el acceso a memoria– Realizar transferencias asíncronas– Mejorar accesos a memoria global– Usar memoria compartida– Reducir conflictos de bancos en memoria compartida

• Configurar la ejecución– Seleccionar el número de bloques y threads por bloque

• Optimizar a nivel de instrucción– Instrucciones aritméticas– Instrucciones para acceso a memoria– Saltos y sentencias condicionales

41

Page 42: Computación de altas  prestaciones con GPUs

Transferencias asíncronas

• cudaMemcpy es bloqueante– Se devuelve el control al host una vez finalizada la

transferencia

• cudaMemcpyAsync es no bloqueante– Se devuelve el control inmediatamente

• Las transferencias no bloqueantes permiten solapar computación y comunicación

42

Page 43: Computación de altas  prestaciones con GPUs

Solapar computación y comunicación

• Un stream es un conjunto de operaciones que se completan secuencialmente

• Se puede solapar la comunicación y computación de diferentes streams

• Para ello se debe permitir que la memoria del host esté mapeada en el espacio de direcciones del dispositivo

43

Datos2Datos1

Datos2Datos1

Host→Device Device→HostKernel Cálculos

Datos1 Datos2

Datos2

Host→Device

Device → Host

Kernel Cálculos

Datos1 Datos2

Datos1 Datos2

Datos1

43

Page 44: Computación de altas  prestaciones con GPUs

Memoria global

• Latencia alta– Usarla lo menos posible

• Accesos por half-warp (16 threads)– Intentar completarlos en el menor número de transacciones

posible (coalescing)

1 transacción 16 transacciones

44

Page 45: Computación de altas  prestaciones con GPUs

Memoria global

• Latencia alta– Usarla lo menos posible

• Accesos por half-warp (16 threads)– Intentar completarlos en el menor número de transacciones

posible (coalescing)

1 2 3 4

5 6 7 8

9 10 11 12

13 14 15 16

1

2

3

4

5

6

7

8

memoria globalmatriz

1 2 3 4

5 6 7 8

9 10 11 12

13 14 15 16

1

2

3

4

5

6

7

8

memoria globalmatriz

45

Page 46: Computación de altas  prestaciones con GPUs

Memoria compartida• Dividida en módulos (bancos) • En las GPUs actuales hay 16 bancos de 1KB• Acceso simultáneo a los bancos• Baja latencia (similar registros)• Problema: conflictos en los bancos (acceso al mismo banco por dos o más threads)

Banco 0

Banco 1

Banco 2

Banco 3

Banco 4

Banco 14

Banco 15

Thread 0

Thread 1

Thread 2

Thread 3

Thread 4

Thread 14

Thread 15

. . .. . .

Sin conflictos

Direccionamiento linealStride = 1

Banco 0

Banco 1

Banco 2

Banco 3

Banco 4

Banco 14

Banco 15

Thread 0

Thread 1

Thread 2

Thread 3

Thread 4

Thread 14

Thread 15

. . .. . .

Sin conflictos

Direccionamiento aleatorioPermutación 1:1

Banco 0

Banco 1

Banco 2

Banco 3

Banco 4

Banco 5

Banco 15

Thread 0

Thread 1

Thread 2

Thread 3

Thread 8

Thread 15

. . .

. . .

. . .Thread 9

Con conflictos

Direccionamiento linealStride = 2

46

Page 47: Computación de altas  prestaciones con GPUs

Memoria compartida• Dividida en módulos (bancos) • En las GPUs actuales hay 16 bancos de 1KB• Acceso simultáneo a los bancos• Baja latencia (similar registros)• Problema: conflictos en los bancos (acceso al mismo banco por dos o más threads)

• Cargar los datos desde la memoria global a la memoria compartida

• Sincronizar threads

• Procesar usando sólo memoria compartida

• Sincronizar

• Llevar resultados a memoria global

47

Page 48: Computación de altas  prestaciones con GPUs

Bloques y threads• Ocupación:

– Número de warps activos por MP con respecto al máximo posible de warps activos

• El número total de bloques y de threads por bloque son factores importantes

• Número de bloques– Dependiente de los recursos disponibles– Al menos tantos como MPs– Más de uno por MP para que unos mantengan los recursos ocupados mientras

otros se sincronizan– Suficientemente alto para escalar en futuras versiones

• Número de threads por bloque– Múltiplo del tamaño del warp para facilitar coalescing– Múltiplos de 64, para evitar conflictos en acceso a registros– Entre 128 y 256 buena elección – Más threads no implica necesariamente mayor ocupación

• Experimentación

48

Page 49: Computación de altas  prestaciones con GPUs

Rendimiento a nivel de instrucción

• La productividad a nivel de instrucción depende de– Número de operaciones– Latencia de la memoria– Ancho de banda de la memoria

• Maximizar ancho de banda efectivo– Maximizar el uso de la memoria compartida– Minimizar los accesos a memoria global– Maximizar coalescing en los accesos a memoria global– Solapar comunicación y computación

• Aumentar el rendimiento de la ejecución de las instrucciones– Instrucciones aritméticas más rápidas, si se prefiere velocidad en lugar

de precisión– Instrucciones para accesos a memoria con menos latencia– Evitar sentencias condicionales que generen diferentes caminos en el

mismo warp, pues éstos son serializados

49

Page 50: Computación de altas  prestaciones con GPUs

Divergencia de caminos

Branch

Path A

Path B

Salto

Path A

Path B

• Los caminos son serializados

• Incremento del número de instrucciones ejecutadas por el warp

• Los threads vuelven a converger cuando todos los caminos se completan

• 50% de pérdida de rendimiento

50

Page 51: Computación de altas  prestaciones con GPUs

Índice

• Producto Matriz-Matriz• ¿Cómo explotar más la arquitectura?• Buenas prácticas de programación

CUDA• OpenCL

51

Page 52: Computación de altas  prestaciones con GPUs

Índice

• Producto Matriz-Matriz– Implementación secuencial– Recordar– Implementación básica– ¿Cómo compilar?– Implementación memoria compartida

• ¿Cómo explotar más la arquitectura?• Buenas prácticas de programación

CUDA• OpenCL

52

Page 53: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Implementación secuencial

53

Page 54: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• cudaMalloc()• cudaMemcpy()• cudaThreadSynchronize();• cudaFree()• multiplication<<< grid, threads >>>(d_P, d_M, d_N,

M.width, N.width);• __global__ void multiplication(float *P, float *M, float *N,

int wM, int wN)

54

Page 55: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Implementación básica– Cada thread calcula un

elemento de la matriz• Lee una fila de A• Lee una columna de B

55

Page 56: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Implementación básica– Ejercicio 1: (En programa principal)

• a) Repasar las llamadas y el significado • b) Completar llamada a kernel• c) Cálculo de GigaFlops

– Ejercicio 2: (En kernel)• a) Cálculo de índices• b) Cálculo de elemento

– Ejercicio 3: (Pruebas)

56

Page 57: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz• Implementación básica

– Ejercicio 2.a (Ejemplo)

57

(0,0) (1,0) (2,0)

(0,1) (1,1) (2,1)

(0,2) (1,2) (2,2)

(0,0) (1,0) (2,0)

(0,1) (1,1) (2,1)

(0,2) (1,2) (2,2)

(0,0) (1,0) (2,0)

(0,1) (1,1) (2,1)

(0,2) (1,2) (2,2)

(0,0) (1,0) (2,0)

(0,1) (1,1) (2,1)

(0,2) (1,2) (2,2)

(0,0) (1,0) (2,0)

(0,1) (1,1) (2,1)

(0,2) (1,2) (2,2)

(0,0) (1,0) (2,0)

(0,1) (1,1) (2,1)

(0,2) (1,2) (2,2)

5

3(0,0) (1,0)

(0,1) (1,1)

(0,2) (1,2)

Page 58: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz• ¿Cómo compilar?

gmultiply: gmultiply.cu

nvcc gmultiply.cu

-keep

--ptxas-options=-v

-o gmultiply

-lcutil

-L /usr/local/cuda/sdk/lib

-I /usr/local/cuda/sdk/common/inc58

Page 59: Computación de altas  prestaciones con GPUs

GPU GeForce 285GTX

• 30 multiprocesadores (MPs)– 8 cores (FPUs) por multiprocesador, a 1476 MHz– mad: multiplicación-suma (considera 2 flops a efectos de cálculo de productividad) – 2 SFU (unidades funcionales especiales) por multiprocesador

• Total 240 cores

• 797 GFLOPS (rendimiento pico)– (30 MPs) x (8x2 + 2 Flops/MP) x 1476 MHz = 797 GFLOPS

• 1GB RAM– 1242 MHz (double data rate, 2484 MHz)– 512 bits (ancho bus)– 159 GB/s = (2484 MHz) x (512 / 8 bytes)

59

Page 60: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Se usan los cores (no las SFUs)– (240 cores) x 1476 MHz =

= 354,24 Goperaciones/s

• De 8 instrucciones sólo 2 son FLOP• Máxima productividad

– 354,24 x ¼ = 88,56 GLOPS

• ¿Por qué no se llega a ese valor?

60

$Lt_0_7:

ld.global.f32 %f2, [%r15+0];

ld.global.f32 %f3, [%r17+0];

mad.f32 %f1, %f2, %f3, %f1;

add.s32 %r17, %r17, 4096;

add.s32 %r15, %r15, 4;

setp.ne.u32 %p1, %r15, %r16;

@%p1 bra $Lt_0_7;

• Accesos a memoria– ¼ operaciones son cargas– (240 cores) x (¼ cargas) x (4bytes/carga) x (1476 MHz) = 354 GBs > 159 GBs

El ancho de banda con memoria global no es suficiente

Page 61: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Cada dato de entrada es leído por width threads

• Latencia memoria global ~ 400 ciclos• Latencia memoria compartida ~ 10 ciclos

• Llevar cada dato de entrada a memoria compartida y que sea leído por varios threads

61

Page 62: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Cada dato de entrada es leído por width threads

• Latencia memoria global ~ 400 ciclos• Latencia memoria compartida ~ 10 ciclos

• Llevar cada dato de entrada a memoria compartida y que sea leído por varios threads

Producto de sub-matrices

• Cada sub-matriz es calculada por un bloque de threads

• Los datos de entrada necesarios son llevados a memoria compartida 62

Page 63: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Implementación memoria compartida– Disminuir accesos a

memoria– ¿Cómo lo haríais?

63

Page 64: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

64

C1,0C0,0

C0,1

C2,0 C3,0

C1,1

C0,2 C2,2 C3,2C1,2

C3,1C2,1

C0,3 C2,3 C3,3C1,3

A2,0

A1,1

A1,0A0,0

A0,1

A3,0

A2,1 A3,1

A2,2

A1,3

A1,2A0,2

A0,3

A3,2

A2,3 A3,3

B0,3 B1,3

B1,2

B1,1

B1,0B0,0

B0,1

B0,2

B2,3 B3,3

B3,2

B3,1

B3,0B2,0

B2,1

B2,2

Page 65: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

65

C0,0

thread0,0

C1,0

thread1,0

C0,1

thread0,1

C1,1

thread1,1

A0,0 x B0,0 A0,0 x B1,0 A0,1 x B0,0 A0,1 x B1,0

A1,0 x B0,1 A1,0 x B1,1 A1,1 x B0,1 A1,1 x B1,1

A2,0 x B0,2 A2,0 x B1,2 A2,1 x B0,2 A2,1 x B1,2

A3,0 x B0,3 A3,0 x B1,3 A3,1 x B0,3 A3,1 x B1,3

orde

n ac

ceso

s

En una primera fase se llevan a memoria compartida

En una segunda fase se llevan a memoria compartida

Cada Ai,j y Bi,j se lee dos veces

Cada thread calcula un punto de la matriz resultado

Page 66: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

66

C1,0C0,0

C0,1

C2,0 C3,0

C1,1

C0,2 C2,2 C3,2C1,2

C3,1C2,1

C0,3 C2,3 C3,3C1,3

A2,0

A1,1

A1,0A0,0

A0,1

A3,0

A2,1 A3,1

A2,2

A1,3

A1,2A0,2

A0,3

A3,2

A2,3 A3,3

B0,3 B1,3

B1,2

B1,1

B1,0B0,0

B0,1

B0,2

B2,3 B3,3

B3,2

B3,1

B3,0B2,0

B2,1

B2,2

(1,1)

(1,0)(0,0)

(0,1)

threads

C0,0 C1,0

C1,1C0,1

calculan

memoria compartida

As Bs

cada thread lleva un dato de A y B

Page 67: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

67

C1,0C0,0

C0,1

C2,0 C3,0

C1,1

C0,2 C2,2 C3,2C1,2

C3,1C2,1

C0,3 C2,3 C3,3C1,3

A2,0

A1,1

A1,0A0,0

A0,1

A3,0

A2,1 A3,1

A2,2

A1,3

A1,2A0,2

A0,3

A3,2

A2,3 A3,3

B0,3 B1,3

B1,2

B1,1

B1,0B0,0

B0,1

B0,2

B2,3 B3,3

B3,2

B3,1

B3,0B2,0

B2,1

B2,2

(1,1)

(1,0)(0,0)

(0,1)

threads

C0,0 C1,0

C1,1C0,1

calculan

memoria compartida

As Bs

A0,0 A1,0

A1,1A0,1

B0,0 B1,0

B1,1B0,1

C0,0 = A0,0 x B0,0 + A1,0 x B0,1

C1,0 = A0,0 x B1,0 + A1,0 x B1,1

C0,1 = A0,1 x B0,0 + A1,1 x B0,1

C1,1 = A0,1 x B1,0 + A1,1 x B1,1

Page 68: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

68

C1,0C0,0

C0,1

C2,0 C3,0

C1,1

C0,2 C2,2 C3,2C1,2

C3,1C2,1

C0,3 C2,3 C3,3C1,3

A2,0

A1,1

A1,0A0,0

A0,1

A3,0

A2,1 A3,1

A2,2

A1,3

A1,2A0,2

A0,3

A3,2

A2,3 A3,3

B0,3 B1,3

B1,2

B1,1

B1,0B0,0

B0,1

B0,2

B2,3 B3,3

B3,2

B3,1

B3,0B2,0

B2,1

B2,2

(1,1)

(1,0)(0,0)

(0,1)

threads

C0,0 C1,0

C1,1C0,1

calculan

memoria compartida

As Bs

A2,0 A3,0

A3,1A2,1

B0,2 B1,2

B1,3B0,3

C0,0 = A0,0 x B0,0 + A1,0 x B0,1 + A2,0 x B0,2 + A3,0 x B0,3

C1,0 = A0,0 x B1,0 + A1,0 x B1,1 + A2,0 x B1,2 + A3,0 x B1,3

C0,1 = A0,1 x B0,0 + A1,1 x B0,1 + A2,1 x B0,2 + A3,1 x B0,3

C1,1 = A0,1 x B1,0 + A1,1 x B1,1 + A2,1 x B1,2 + A3,1 x B1,3

Page 69: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Bloque de threads 16 x 16 = 256 threads• Width = 4096 256 x 256 = 65536 bloques de threads• • Cada dato en una sub-matriz es leído por 16 threads• Los accesos a memoria global se reducen en un factor 16

al usar memoria compartida• Se requiere ahora 354 / 16 ~ 22 GBs

Ahora la memoria no es motivo para no alcanzar la productividad deseada

Page 70: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Implementación memoria compartida– Ejercicio 1: (En programa principal)

• a) Repasar las llamadas y el significado • b) Completar llamada a kernel• c) Cálculo de GigaFlops

– Ejercicio 2: (En kernel)• a) Cálculo de índices• b) Cálculo de elemento

– Ejercicio 3: (Pruebas)

70

Page 71: Computación de altas  prestaciones con GPUs

Producto Matriz-Matriz

• Implementación memoria compartida– Disminuir accesos a

memoria– ¿Cómo lo haríais?– Veamos el código

ahora e identifiquemos los diferentes elementos

71

Page 72: Computación de altas  prestaciones con GPUs

Índice

Producto Matriz-Matriz• ¿Cómo explotar más la

arquitectura?• Buenas prácticas de programación

CUDA• OpenCL

72

Page 73: Computación de altas  prestaciones con GPUs

¿Cómo explotar más la arquitectura?

• Sistemas Distribuidos mediante MPI

• Sistemas compartidos mediante OpenMP

73

Page 74: Computación de altas  prestaciones con GPUs

Índice

Producto Matriz-Matriz¿Cómo explotar más la

arquitectura?• Buenas prácticas de programación

CUDA• OpenCL

74

Page 75: Computación de altas  prestaciones con GPUs

Guía de buenas prácticas de programación CUDA

• Maximizar ejecución paralela• Optimizar el uso de la memoria

de cara obtener un mayor ancho de banda en el acceso a memoria

• Optimizar el uso de las instrucciones para conseguir una mayor productividad

75

Page 76: Computación de altas  prestaciones con GPUs

Índice

Producto Matriz-Matriz¿Cómo explotar más la

arquitectura?Buenas prácticas de programación

CUDA• OpenCL

– ¿Qué es OpenCL?– ¿Qué diferencia OpenCL de CUDA?– Ejemplo: Suma de vectores

76

Page 77: Computación de altas  prestaciones con GPUs

¿Qué es OpenCL?

• OpenCL: Open Computing Language

• Propuesto por a

• ¿Quién está involucrado?

Page 78: Computación de altas  prestaciones con GPUs

¿Qué diferencia OpenCL de CUDA? (I)

• Punteros– CUDA

struct Node {Node* next}

n=n->next

– Openclstruct Node {unsigned int next;}

next=bufBase+n

Page 79: Computación de altas  prestaciones con GPUs

¿Qué diferencia OpenCL de CUDA? (II)

• Kernels– CUDA

• Programa compilado en formato binario

– OpenCL• Se compila en tiempo de

ejecución

– Palabras Clave y lenguaje utilizado para los kernel

Page 80: Computación de altas  prestaciones con GPUs

Ejemplo: Suma de vectores

• CUDA_global_ void

SumaVec(const float *a, const float *b, float *c)

// Índice al elemento del vector

int indice=blockIdx.x*blockDim.x+threadIdx.x

c[indice]=a[indice]+b[indice]

}

• OpenCL_kernel void

SumaVec(_global const float *a, _global const float *b, _global float *c)

// Índice al elemento del vector

int indice=get_global_id(0)

c[indice]=a[indice]+b[indice]

}

Page 81: Computación de altas  prestaciones con GPUs

Índice

Producto Matriz-Matriz¿Cómo explotar más la

arquitectura?Buenas prácticas de programación

CUDAOpenCL

81

Page 82: Computación de altas  prestaciones con GPUs