computación de altas prestaciones con gpus
Post on 12-Jan-2016
69 Views
Preview:
DESCRIPTION
TRANSCRIPT
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
Índice
• Introducción• Arquitectura CUDA• Modelo de ejecución• Programación• Rendimiento
2
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
Introducción
Rendimiento
Fuente: Nvidia 4
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
Introducción
Uso de los transistores
+ Cache
+ Control
- Cores
+ Cores
- Cache
- Control 6
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
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
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
Índice
Introducción• Arquitectura CUDA• Modelo de ejecución• Programación• Rendimiento
10
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
Fermi
~1.5TFLOPS (SP)~800GFLOPS (DP)230 GB/s DRAM
12
Espacios de memoria en CUDA
13
Fuente: Nvidia
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
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
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
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
• 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
• 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
Índice
IntroducciónArquitectura CUDA• Modelo de ejecución• Programación• Rendimiento
20
Modelo de ejecución
Secuencial
Código
Thread
21
Modelo de ejecución
Paralelo
Código
Threads
22
kernel
Paralelo
Miles de threads
lanzados a la vez
23
Modelo de ejecución
Memoria
Kernel Threads (instancias del
kernel)PCIe
CPU GPU
24
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
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
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
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
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
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
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
Índice
IntroducciónArquitectura CUDAModelo de ejecución• Programación• Rendimiento
32
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
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
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
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
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
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
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
Índice
IntroducciónArquitectura CUDAModelo de ejecuciónProgramación• Rendimiento
40
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
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
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
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
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
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
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
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
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
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
Índice
• Producto Matriz-Matriz• ¿Cómo explotar más la arquitectura?• Buenas prácticas de programación
CUDA• OpenCL
51
Í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
Producto Matriz-Matriz
• Implementación secuencial
53
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
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
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
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)
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
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
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
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
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
Producto Matriz-Matriz
• Implementación memoria compartida– Disminuir accesos a
memoria– ¿Cómo lo haríais?
63
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
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
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
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
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
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
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
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
Índice
Producto Matriz-Matriz• ¿Cómo explotar más la
arquitectura?• Buenas prácticas de programación
CUDA• OpenCL
72
¿Cómo explotar más la arquitectura?
• Sistemas Distribuidos mediante MPI
• Sistemas compartidos mediante OpenMP
73
Índice
Producto Matriz-Matriz¿Cómo explotar más la
arquitectura?• Buenas prácticas de programación
CUDA• OpenCL
74
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
Í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
¿Qué es OpenCL?
• OpenCL: Open Computing Language
• Propuesto por a
• ¿Quién está involucrado?
¿Qué diferencia OpenCL de CUDA? (I)
• Punteros– CUDA
struct Node {Node* next}
n=n->next
– Openclstruct Node {unsigned int next;}
next=bufBase+n
¿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
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]
}
Índice
Producto Matriz-Matriz¿Cómo explotar más la
arquitectura?Buenas prácticas de programación
CUDAOpenCL
81
top related