Teoria acerca de CUDA

34
7/24/2019 Teoria acerca de CUDA http://slidepdf.com/reader/full/teoria-acerca-de-cuda 1/34 Manuel Ujaldon Nvidia CUDA Fellow Profesor del Dpto. de Arquitectura de Computadores. Universidad de M‡laga (Espa–a). Conjoint Senior Lecturer. University of Newcastle (Australia) RIO 2014 R’o Cuarto (Argentina), 17 y 18 de Febrero, 2014 2  Agradecimientos  Al personal de Nvidia, por compartir conmigo ideas, material, diagramas, presentaciones, ... AlfabŽticamente: Bill Dally [2010-2011: Consumo energŽtico, Echelon y dise–os futuros]. Simon Green [2007-2009: Los pilares de CUDA]. Sumit Gupta [2008-2009: El hardware de Tesla]. Mark Harris [2008, 2012: CUDA, OpenACC, Lenguajes de Programaci—n, Librer’as]. Wen-Mei Hwu [2009: Programaci—n y trucos para mejorar el rendimiento]. Stephen Jones [2012: Kepler]. David B. Kirk [2008-2009: Hardware de Nvidia]. Timothy Lanfear [2012: Kepler y OpenACC]. David Luebke [2007-2008: Hardware de Nvidia]. Lars Nyland [2012: Kepler]. Edmondo Orlotti [2012: CUDA 5.0, OpenACC]. Cyril Zeller [2008-2009: Fundamentos de CUDA]. ... por mencionar s—lo unos pocos del equipo que contribuy— a esta presentaci—n. Contenidos del tutorial 1. Introducci—n. [18 diapositivas] 2. Arquitectura. [15] 1. El modelo hardware de CUDA. [3] 2. La primera generaci—n: Tesla (2006-2009). [3] 3. La segunda generaci—n: Fermi (2010-2011). [4] 4. La tercera generaci—n: Kepler (2012-2014). [5] 3. Programaci—n. [15] 4. Sintaxis. [16] 1. Elementos b‡sicos. [10] 2. Un par de ejemplos preliminares. [6] 5. Compilaci—n. [9] 6. Ejemplos: VectorAdd, Stencil, MxM. [25] 7. Bibliograf’a y herramientas. [6] 3 Prerrequisitos para este tutorial Se requiere estar familiarizado con el lenguaje C. No es necesaria experiencia en programaci—n paralela (aunque si se tiene, ayuda bastante). No se necesitan conocimientos sobre la arquitectura de la GPU: Empezaremos describiendo sus pilares b‡sicos. No es necesario tener experiencia en programaci—n gr‡fica. Eso era antes cuando GPGPU se basaba en los shaders y Cg. Con CUDA, no hace falta desenvolverse con vŽrtices, p’xeles o texturas porque es transparente a todos estos recursos. 4

Transcript of Teoria acerca de CUDA

Page 1: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 1/34

Manuel UjaldonNvidia CUDA FellowProfesor del Dpto. de Arquitectura de Computadores. Universidad de M‡laga (Espa–a).

Conjoint Senior Lecturer. University of Newcastle (Australia)

RIO 2014R’o Cuarto (Argentina), 17 y 18 de Febrero, 2014

2

 Agradecimientos

 Al personal de Nvidia, por compartir conmigo ideas,material, diagramas, presentaciones, ... AlfabŽticamente:

Bill Dally [2010-2011: Consumo energŽtico, Echelon y dise–os futuros].

Simon Green [2007-2009: Los pilares de CUDA].

Sumit Gupta [2008-2009: El hardware de Tesla].Mark Harris [2008, 2012: CUDA, OpenACC, Lenguajes de Programaci—n, Librer’as].

Wen-Mei Hwu [2009: Programaci—n y trucos para mejorar el rendimiento].

Stephen Jones [2012: Kepler].

David B. Kirk [2008-2009: Hardware de Nvidia].

Timothy Lanfear [2012: Kepler y OpenACC].

David Luebke [2007-2008: Hardware de Nvidia].

Lars Nyland [2012: Kepler].

Edmondo Orlotti [2012: CUDA 5.0, OpenACC].

Cyril Zeller [2008-2009: Fundamentos de CUDA].

... por mencionar s—lo unos pocos del equipo que contribuy— a estapresentaci—n.

Contenidos del tutorial

1. Introducci—n. [18 diapositivas]

2. Arquitectura. [15]

1. El modelo hardware de CUDA. [3]

2. La primera generaci—n: Tesla (2006-2009). [3]

3. La segunda generaci—n: Fermi (2010-2011). [4]

4. La tercera generaci—n: Kepler (2012-2014). [5]

3. Programaci—n. [15]

4. Sintaxis. [16]

1. Elementos b‡sicos. [10]

2. Un par de ejemplos preliminares. [6]

5. Compilaci—n. [9]

6. Ejemplos: VectorAdd, Stencil, MxM. [25]

7. Bibliograf’a y herramientas. [6] 3

Prerrequisitos para este tutorial

Se requiere estar familiarizado con el lenguaje C.

No es necesaria experiencia en programaci—n paralela(aunque si se tiene, ayuda bastante).

No se necesitan conocimientos sobre la arquitectura de la

GPU: Empezaremos describiendo sus pilares b‡sicos.No es necesario tener experiencia en programaci—n

gr‡fica. Eso era antes cuando GPGPU se basaba en losshaders y Cg. Con CUDA, no hace falta desenvolverse convŽrtices, p’xeles o texturas porque es transparente a todosestos recursos.

4

Page 2: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 2/34

Page 3: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 3/34

Las 3 cualidades que han hechode la GPU un procesador œnico

Control simplificado.El control de un hilo se amortiza en otros 31 (warp size = 32).

Escalabilidad.

 Aprovech‡ndose del gran volumen de datos que manejan lasaplicaciones, se define un modelo de paralelizaci—n sostenible.

Productividad.Se habilitan multitud de mecanismos para que cuando un hilo pase

a realizar operaciones que no permitan su ejecuci—n veloz, otrooculte su latencia tomando el procesador de forma inmediata.

Palabras clave esenciales para CUDA:Warp, SIMD, ocultaci—n de latencia, conmutaci—n de contexto

gratis. 9

ÀQuŽ es CUDA? ÒCompute Unified Device ArchitectureÓ 

Una plataforma dise–ada conjuntamente a nivel software yhardware para aprovechar la potencia de una GPU enaplicaciones de prop—sito general a tres niveles:

Software: Permite programar la GPU en C con m’nimaspero potentes extensiones SIMD para lograr una ejecuci—neficiente y escalable.

Firmware: Ofrece un driver para la programaci—n GPGPUque es compatible con el que utiliza para renderizar. Sencillos APIs manejan los dispositivos, la memoria, etc.

Hardware: Habilita el paralelismo de la GPU paraprogramaci—n de prop—sito general a travŽs de un nœmero demultiprocesadores dotados de un conjunto de nœcleoscomputacionales arropados por una jerarqu’a de memoria.

10

Lo esencial de CUDA

En general, es lenguaje C con m’nimas extensiones:El programador escribe el programa para un solo hilo (thread), y el

c—digo se instancia de forma autom‡tica sobre miles de hilos.

CUDA define:Un modelo de arquitectura:

Con multitud de unidades de proceso (cores), agrupadas en multiprocesadores quecomparten una misma unidad de control (ejecuci—n SIMD).

Un modelo de programaci—n:Basado en el paralelismo masivo de datos y en el paralelismo de grano fino.

Escalable: El c—digo se ejecuta sobre cualquier nœmero de cores sin recompilar.

Un modelo de gesti—n de la memoria:M‡s expl’cita al programador, con control expl’cito de la memoria cachŽ.

Objetivos:Construir c—digo escalable a cientos de cores, declarando miles de hilos.

Permitir computaci—n heterogŽnea en CPU y GPU.11

Terminolog’a:Host (el anfitri—n): La CPU y la memoria de la placa base [DDR3].

Device (el dispositivo): La tarjeta gr‡fica [GPU + memoria de v’deo]: GPU: Nvidia GeForce/Tesla.

 Memoria de v’deo: GDDR5 en 2013.

Computaci—n heterogŽnea (1/4)

Host Device12

Page 4: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 4/34

CUDA ejecuta un programa sobre un dispositivo (la GPU), que actœa comocoprocesador de un anfitri—n o host (la CPU).

CUDA puede verse como una librer’a de funciones que contienen 3 tipos decomponentes:

Host: Control y acceso a los dispositivos.

Dispositivos: Funciones espec’ficas para ellos.

Todos: Tipos de datos vectoriales y un conjunto de rutinas soportadas por ambas partes.

Computaci—n heterogŽnea (2/4)

13

CPU (host)

  GPU(device)

Memoria principal

(DDR3)

Memoria dev’deo

(GDDR5)

Cores Caches50 GB/s.

3 canales (192 bits = 24 bytes)

@ 1.333 GHz 32 GB/s.

PCI-e 3.0: 8 GB/s.

384 bits @ 3 GHz 144 GB/s.

Computaci—n heterogŽnea (3/4)

El c—digo reescrito en CUDA puede ser inferior al 5%, peroconsumir m‡s del 50% del tiempo si no migra a la GPU. 14

#include<iostream>#include<algorithm>

usingnamespace  std;

#define N 1024#define RADIUS 3#define BLOCK_SIZE16

 __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 elementsinto 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 thedata isavailable)   __syncthreads();

   //Apply thestencil  int result= 0;

  for (int offset= -RADIUS; offset<= RADIUS; offset++)  result+= temp[lindex+ offset];

   //Store theresult  out[gindex] = result;}

void fill_ints(int *x, int n){  fill_n(x, n, 1);}

int main(void){  int *in, *out;  //host copiesof a, b, c  int *d_in, *d_out;  //device copiesof a, b, c  int size= (N+ 2*RADIUS) * sizeof (int);

   //Alloc spacefor hostcopies and setup values  in = (int *)malloc(size); fill_ints(in, N+ 2*RADIUS);  out= (int *)malloc(size); fill_ints(out, N+ 2*RADIUS); 

 //Alloc spacefor devicecopies  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 resultback to host  cudaMemcpy(out, d_out, size, cudaMemcpyDeviceToHost);

   //Cleanup  free(in); free(out);  cudaFree(d_in); cudaFree(d_out);  return 0;}

Computaci—n heterogŽnea (4/4)

CODIGO DEL HOST:

- C—digo serie.- C—digo paralelo.- C—digo serie.

CODIGO DEL DISPOSITIVO:Funci—n paralela

15

Un sencillo flujo de procesamiento (1/3)

Bus PCI

16

1.Copiar los datos de entrada de lamemoria de la CPU a la memoriade la GPU.

Page 5: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 5/34

Un sencillo flujo de procesamiento (2/3)

PCI Bus

17

1.Copiar los datos de entrada de lamemoria de la CPU a la memoriade la GPU.

2.Cargar el programa en la GPU yejecutarlo, ubicando datos encachŽ para mejorar elrendimiento.

Un sencillo flujo de procesamiento (3/3)

1.Copiar los datos de entrada de lamemoria de la CPU a la memoriade la GPU.

2.Cargar el programa en la GPU yejecutarlo, ubicando datos encachŽ para mejorar elrendimiento.

3.Transferir los resultados de lamemoria de la GPU a la memoriade la CPU.

PCI Bus

18

El cl‡sico ejemplo

Es c—digo C est‡ndar que se ejecuta en el host.

El compilador nvcc de Nvidia puede utilizarse paracompilar programas que no contengan c—digo para la GPU.

19

Salida:

$ nvcc hello.cu$ a.outÁHola mundo!

$

int main(void) {

  printf("ÁHola mundo!\n");

  return 0;

}

ÁHola mundo! con c—digo para la GPU (1/2)

Dos nuevos elementossint‡cticos:

La palabra clave de CUDA __global__  indica una funci—n que se ejecuta en laGPU y se lanza desde la CPU. Por

ejemplo, mikernel<<<1,1>>>.Eso es todo lo que se requiere

para ejecutar una funci—n en GPU.

20

 __global__  void mikernel(void)

{

}

int main(void)

{

  mikernel<<<1,1>>>();

  printf("ÁHola mundo!\n");

  return 0;

}

nvcc separa el c—digo fuente para la CPU y la GPU.

Las funciones que corresponden a la GPU (como mikernel())son procesadas por el compilador de Nvidia.

Las funciones de la CPU (como main()) son procesadas porsu compilador (como gcc para Unix o cl.exe para Windows).

Page 6: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 6/34

ÁHola mundo! con c—digo para la GPU (2/2)

mikernel() no hace nada esta vez.

Los s’mbolos "<<<" y ">>>" delimitan la llamada desde el c—digode la CPU al c—digo de la GPU, tambiŽn denominado Òlanzamiento deun kernelÓ.

Los par‡metros 1,1 describen el paralelismo (bloques e hilos CUDA).21

 __global__  void mikernel(void)

{

}

int main(void) {

  mikernel<<<1,1>>>();

  printf("ÁHola mundo!\n");

  return 0;

}

Salida:

$ nvcc hello.cu$ a.out

ÁHola mundo!$

Computaci—n en GPU

NVIDIA GPU con la arquitectura

de computaci—n paralela CUDA

C OpenCL

tm DirectCompute

Fortran Java yPython

C++

Si tenemos una arquitectura CUDA, podemosprogramarla de muy diversas formas...

... aunque este tutorial se focaliza sobre CUDA C.22

La evoluci—n de CUDA

En los œltimos 5 a–os, Nvidia ha vendido m‡s de 450 millones deGPUs que aceptan CUDA para su programaci—n.

Pero CUDA ha evolucionado en la direcci—n opuesta a la que estamosacostumbrados: Partiendo de los investigadores para poco a poco llegara los usuarios m‡s genŽricos.

23

 Versi—n deCUDA [a–o]

Usuarios

1.0 [2007]

2.0 [2008]

3.0 [2009]

4.0 [2011]

5.0 [2012]

Muchos investigadores y algunos usuarios madrugadores.

Cient’ficos y aplicaciones para computaci—n de altas prestaciones.

L’deres en la innovaci—n de aplicaciones.

 Adopci—n mucho m‡s extensa de desarrolladores.

Optimizada para Kepler e inaugurada en el tercer trimestre de 2012.

2013 finaliza con CUDA 5.5, en transici—n hacia CUDA 6.0.

Page 7: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 7/34

Las generaciones hardware de CUDA

26

El modelo hardware de CUDA:Un conjunto de procesadores SIMD

La GPU consta de:N multiprocesadores, cada uno dotado

de M cores (o procesadores streaming).

Paralelismo masivo: Aplicado sobre miles de hilos.

Compartiendo datos a diferentesniveles de una jerarqu’a de memoria.

Computaci—n heterogŽnea:GPU:

Intensiva en datos.

Paralelismo de grano fino.

CPU:Gesti—n y control.

Paralelismo de grano grueso.

27

GPU

Multiprocesador N

Multiprocesador 2

Multiprocesador 1

Unidad deControl SIMDCore 1

É

Core 2 Core M

G80(Tesla)

GT200(Tesla)

GF100(Fermi)

GK110(Kepler)

Marco temporal

N (multiprocs.)

M (cores/multip.)

Nœmero de cores

2006-07 2008-09 2010-11 2012-13

16 30 14-16 13-15

8 8 32 192

128 240 448-512 2496-2880

Jerarqu’a de memoria

Cada multiprocesador tiene:Su banco de registros.

Memoria compartida.

Una cachŽ de constantes y otrade texturas, ambas de s—lolectura y uso marginal.

La memoria global es lamemoria de v’deo (GDDR5):

Tres veces m‡s r‡pida que lamemoria principal de la CPU,pero... Á500 veces m‡s lenta quela memoria compartida! (que esSRAM en realidad).

2813

GPU

Multiprocesador N

Multiprocesador 2

Multiprocesador 1

Memoria global

Memoria compartida

Unidadde

ControlSIMD

Procesador 1

Registros

ÉProcesador 2

Registros

Procesador M

Registros

CachŽ paraconstantes

CachŽ paratexturas

Page 8: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 8/34

29

Latencia y ancho de bandade la memoria en CUDA

Memoria de la CPU (o memoria principal - DDR3): Ancho de banda con mem. v’deo: 3.2 GB/s.(PCIe) y 5.2 GB/s(PCIe2).

Memoria de v’deo (o memoria global):Gran ancho de banda (80-100 GB/s) y latencia, no pasa por cachŽ.

Memoria compartidaBaja latencia, ancho de banda muy elevado, tama–o reducido. Actœa como una cachŽ gestionada por el usuario (scratchpad).

Memoria de texturas/constantesDe s—lo lectura, alta/baja latencia, pasa por cachŽ.

 

Host

CPU

Chipset

DRAM

Device

DRAM

Global 

Constantes

Texturas

Local 

GPU

Multiprocesador 

Registros y

memoria compartida

CachŽs para constantesy texturas

La primera generaci—n: G80 (GeForce 8800)

31

GPU G80 (en torno a 600 MHz, frecuencia muy inferior a la de sus cores)

Multiprocesador 16

Multiprocesador 2

Multiprocesador 1

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

Memoria compartida (16 KB)

Unidad decontrol

(emiteinstrucciones

SIMD)

Core 1 (1.35 GHz)

Registros

ÉCore 2

Registros

Core 8

Registros

CachŽ de texturas(los kernels se mapean

sobre los cores)

(los bloques de c—digo CUDA se mapean sobre los multipr.)

La primera generaci—n: GT200 (GTX 200)

32

GPU GTX 200 (en torno a 600 MHz)

Multiprocesador 30

Multiprocesador 2

Multiprocesador 1

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

Memoria compartida (16 KB)

Unidad decontrol

(emiteinstrucciones

SIMD)

Core 1 (1.30 GHz)

Registros

ÉCore 2

Registros

Core 8

Registros

CachŽ de texturas(los kernels se mapean

sobre los cores)

(los bloques de c—digo CUDA se mapean sobre los multipr.)

Page 9: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 9/34

Escalabilidad para futuras generaciones: Alternativas para su crecimiento futuro

 Aumentar el nœmero demultiprocesadores por pares(nodo b‡sico), esto es, creceren la dimensi—n Z. Es lo quehizo la 1» gener. (de 16 a 30).

 Aumentar el nœmero deprocesadores de cadamultiprocesador, o crecer en ladimensi—n X. Es el caminotrazado por la 2» y 3» geners.

 Aumentar el tama–o de lamemoria compartida, esto es,crecer en la dimensi—n Y. 33

GPUMultiprocesador 30

Multiprocesador 2

Multiprocesador 1

Memoria global

Memoria compartida

Core 1

Registros

ÉCore 2

Registros

Core 8

Registros

CachŽ de texturas

(escalabilidad en 1» gener.)

(escalabilidad en 2» y 3» gener.)

35

El HW de Fermi comparado con los modelosrepresentativos de la generaci—n anterior

 Arquitectura GPU

Nombre comercial

 A–o de lanzamiento

G80 GT200 GF100 (Fermi)

GeForce 8800 GTX 200 GTX 580

2006 2008 2010

Nœmero de transistores

Nœmero de cores (int y fp32)

Nœmero de cores (fp64) Velocidad de c‡lculo en fp64

Planificadores de warps

Memoria compartida

CachŽ L1

CachŽ L2

Correcci—n de errores(DRAM)

 Anchura del busde direcciones

681 mil lones 1400 mil lones 3000 mi llones

128 240 512

0 30 256Ninguna 30 madds/ciclo 256 madds/ciclo

1 1 2

16 KB 16 KB 16 KB + 48 KB

Ninguna Ninguna

 (o viceversa)

Ninguna Ninguna 768 KB

No No S’  

32 bits 32 bits 64 bits

 Arquitectura global de Fermi

36

Hasta 512 cores (16 SMs dotados de 32 cores cada uno).Los SPs crecen de 240 a 512, los DPs de 30 a 256 y los SFUs de 60 a 64.

Doble planificador de procesos (scheduler) para los hilos que entran a cada SM.

64 KB de SRAM, repartidos entre la cachŽ L1 y la memoria compartida.

Page 10: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 10/34

Mejoras en la aritmŽtica

Unidades aritmŽtico-l—gicas (ALUs):Redise–ada para optimizar operaciones

sobre enteros de 64 bits.

 Admite operaciones de precisi—nextendida.

La instrucci—n ÒmaddÓ (sumay producto simult‡neos):

Est‡ disponible tanto para simplecomo para doble precisi—n.

La FPU (Floating-Point Unit):Implementa el formato IEEE-754 en su

versi—n de 2008, aventajando incluso a lasCPUs m‡s avezadas.

37

Load/Store Units x 16

Special Func Units x 4

FP Unit INT Unit

La jerarqu’a de memoria

3813

Fermi es la primera GPUque ofrece una cachŽ L1 t’picaon-chip, que combina con la

shared memory de CUDA enproporci—n 3:1 o 1:3 para untotal de 64 Kbytes por cadamultiprocesador (32 cores).

TambiŽn incluye una cachŽunificada de 768 Kbytes concoherencia de datos para elconjunto total de cores.

Diagrama de bloques: Kepler GK110

7.100 Mt.

15 SMX multiprocs.

> 1 TFLOP FP64.

1.5 MB L2 Cache.

384-bit GDDR5.PCI Express Gen3.

40

Page 11: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 11/34

Evoluci—n de los multiprocesadores:Del SM de Femi al SMX de Kepler

41

Mejora de recursos en los SMX

42

RecursoKepler GK110

frente a Fermi GF100

Ritmo de c‡lculo con opers. punto flotante

M‡ximo nœmero de bloques por SMX

M‡ximo nœmero de hilos por SMX

 Ancho de banda del banco de registros

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

2-3x

2x

1.3x

2x

2x

2x

1x

2x

2x

Innovaciones de Kepler:Paralelismo din‡mico

43

CPU   GPU   CPU   GPU

La GPU es un procesador aut—nomoLa GPU es un co-procesador

Innovaciones de Kepler:Paralelismo din‡mico (2)

El paralelismo puede depender de los datos, o incluso de lapotencia computacional asociada a las regiones de interŽs.

44

CUDA en 2012 CUDA sobre Kepler

Page 12: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 12/34

46

El modelo de programaci—n CUDA

La GPU (device) ofrece a la CPU (host) la visi—n de uncoprocesador altamente ramificado en hilos.

Que tiene su propia memoria DRAM.

Donde los hilos se ejecutan en paralelo sobre los nœcleos (coreso stream processors) de un multiprocesador.

Los hilos de CUDA son extremadamente ligeros.Se crean en un tiempo muy ef’mero.

La conmutaci—n de contexto es inmediata.

Objetivo del programador: Declarar miles de hilos, quela GPU necesita para lograr rendimiento y escalabilidad.

GPU

Multiprocesador 1 Multiprocesador 2 Multiprocesador N

Cada modelo se diferencia en pocos par‡-metros para generar el cat‡logo comercial

Debemos esperar que estas diferencias crezcan entremodelos asociados a diferentes generaciones.

Las diferencias tambiŽn crecer‡n cuando dos tarjetaspertenezcan a gamas diferentes:

300-500 ! para la gama alta.150-250 ! para la gama media.

60-120 ! para la gama baja.

La memoria de v’deo puede diferir tambiŽn cuandoemerge una nueva tecnolog’a, como el caso de la GDDR5.

Los modelos difieren tambiŽn en sus prestacionesgr‡ficas, aunque este tutorial no cubre las mismas.

47 48

 

Modelo comercial

Generaci—n

 A–o

GTX 285 GTX 480 GTX 465 GTX 460

GT200 GF100 GF100 GF104

2009 2010 2010 2011

Precio de lanzamiento

Nœmero de transistores

Proceso de fabricaci—n

Nœmero de SMs

Cores en cada SM

Nœmero total de cores

Reloj de los cores (MHz)

Tecnolog’a de memoria

Reloj de memoria (MHz)

 Ancho del bus de mem.

Tama–o de la memoria

 ! 300   ! 500   ! 250   ! 230/200

1400 mill. 3000 mill. 3000 mill. 1950 mill.

TSMC 55 nm. TSMC 40 nm. TSMC 40 nm TSMC 40 nm.

30 15 11 7

8 32 32 48

240 480 352 336

1476 1401 1215 1350

GDDR3 GDDR5 GDDR5 GDDR5

2x 1242 4x 924 4x 802 4x 900

512 bits 384 bits 256 bits 256 bits

1 GB. 1.5 GB. 1 GB. 1 GB. / 768 MB.

Ejemplo de cat‡logo comercial

Page 13: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 13/34

49

 

Modelo comercial

Generaci—n

 A–o

GTX 285 GTX 480 GTX 465 GTX 460

GT200 GF100 GF100 GF104

2009 2010 2010 2011

fp64 vs. fp32

Frecuencia GPU (MHz)

Direccionam. a texturas

Filtrado de texturas

Rasterizadores

1 vs. 16 1 vs. 8 1 vs. 8 1 vs. 6

648 700 607 675

80 60 44 56

80 60 44 56

32 48 32 32

Ejemplo de cat‡logo comercial (cont.)

50

Estructura de un programa CUDA

Cada multiprocesador procesa lotes de bloques, unodetr‡s de otro

Bloques activos = los bloques procesados por un multiprocesadoren un lote.

Hilos activos = todos los que provienen de los bloques que seencuentren activos.

Los registros y la memoria compartida de un multi-procesador se reparten entre sus hilos activos. Para unkernel dado, el nœmero de bloques activos depende de:

El nœmero de registros requeridos por el kernel.

La memoria compartida consumida por el kernel.

51

Conceptos b‡sicos

  Los programadores se enfrentan al reto de exponer el paralelismo paramœltiples cores y mœltiples hilos por core. Para ello, deben usar lossiguientes elementos:

 Dispositivo = GPU = Conjunto de multiprocesadores.

Multiprocesador = Conjunto de procesadores y memoria compartida.

 Kernel = Programa listo para ser ejecutado en la GPU. Malla (grid) = Conjunto de bloques cuya compleci—n ejecuta un kernel.

 Bloque [de hilos] = Grupo de hilos SIMD que:

 Ejecutan un kernel delimitando su dominio de datos segœn su threadIDy blockID.

 Pueden comunicarse a travŽs de la memoria compartida delmultiprocesador.

 Tama–o del warp = 32. Esta es la resoluci—n del planificador para emitirhilos por grupos a las unidades de ejecuci—n.

Correspondencia entre los conceptoshardware y software de CUDA

52

GPU

Multiprocesador N

Multiprocesador 2

Multiprocesador 1

Memoria global

Memoria compartida

Unidadde

ControlSIMD

Procesador 1

Registros

ÉProcesador 2

Registros

Procesador M

Registros

CachŽ paraconstantes

CachŽ paratexturas

Page 14: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 14/34

53

Recursos y limitaciones segœn la GPU queutilicemos para programar (CCC)

CUDA C mpute apability (CCC)

1.0, 1.1 1.2, 1.3 2.0, 2.1 3.0, 3.5

Multiprocesadores / GPU

Cores / Multiprocesador

Hilos / Warp

Bloques / Multiprocesador

Hilos / Bloque

Hilos / Multiprocesador

Registros de 32 bits / Multiprocesador

Memoria compartida / Multiprocesador

16 30 14-16 14-16 HardwareEscala-

ili8 8 32 192 Hardware

l

bilidad

32 32 32 32 Software Ritmo deli

8 8 8 16 Softwarelidatos

512 512 1024 1024 Softwarel li

768 1 024 1 536 2048 Softwarel li

8K 16K 32K 64K Hardware Conjunto

16K 16K  16K 48K 

16K,32K, 48K 

Hardware  trabajo

! ! ! !

54

Bloques e hilos en GPU

Los bloques se asignan a los

multiprocesadores

[L’mite en Kepler:16 bloques

concurrentes porcada

multiprocesador]

Bloque 0 Bloque 1 Bloque 2

Malla 0 [L’mite en Kepler: 4G bloques por malla]

L’mite en Kepler: 1024 hilos por bloque, 2048 hilos por multiprocesador

Los hilos se asignan a los multiprocesadores en bloques, queconstituyen la unidad para asignar hilos a cores via warps.Los hilos de un bloque pueden compartir informaci—n a travŽsde la memoria compartida, sincroniz‡ndose impl’citamente (alacabar el bloque) o expl’citamente (con synchthreads()).

Ejemplos de restricciones de paralelismo enKepler al tratar de maximizar concurrencia

 L’mites para un multiprocesador: [1] 16 bloques concu-rrentes, [2] 1024 hilos/bloque y [3] 2048 hilos en total.

 1 bloque de 2048 hilos. No lo permite [2].

 2 bloques de 1024 hilos. Posible en el mismo multiproc.

 4 bloques de 512 hilos. Posible en el mismo multiproc. 4 bloques de 1024 hilos. No lo permite [3] en el mismo

multiprocesador, pero posible usando dos multiprocs.

 8 bloques de 256 hilos. Posible en el mismo multiproc.

 256 bloques de 8 hilos. No lo permite [1] en el mismomultiprocesador, posible usando 16 multiprocesadores.

55

Estos bloquespueden

ejecutarse enun mismo

multiprocesadorsi se cumplen 

ciertas

limitaciones deespacio

Local memory: Off-chip

56

La memoria en la GPU: çmbito y ubicaci—n

! ! ! !

   T  a  m   b   i   Ž  n  e  x   i  s  t  e

  u

  n  a  m  e  m  o  r   i  a   d  e

  c  o  n  s  t  a  n  t  e  s  y

  t  e  x  t  u  r  a  s

Bloque 0 Bloque 1 Bloque 2

Malla 0

Memoria global: GDDR5 (DRAM)

Memoria compartida

 Los hilos pueden usar la memoria compartida para comunicarse entreellos dentro del bloque o realizar tareas de manera cooperativa y veloz.

 La memoria global es la œnica visible a todas las entidades definidas

para un programa CUDA (hilos, bloques, kernels).

BRBRBRBRBRBRBRBR 

ML ML ML ML ML ML ML ML

Significado: BR: Banco de registros. ML = Memoria local

Memoria GPU: On- ch ip Off -c hip

Page 15: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 15/34

Jugando con las restricciones de memoria enKepler para maximizar el uso de los recursos

 L’mites dentro de un multiprocesador (SMX): 64 Kregs. y48 KB. de memoria compartida. As’:

 Para permitir que un segundo bloque se ejecute en el mismomultiprocesador, cada bloque debe usar a lo sumo 32 Kregs. y 24 KB.

de memoria compartida.

 Para permitir que un tercer bloque se ejecute en el mismo multi-procesador, cada bloque debe usar a lo sumo 21.3 Kregs. y 16 KB. dememoria compartida.

 ...y as’ sucesivamente. Cuanto menor cantidad dememoria usemos, mayor concurrencia para la ejecuci—n debloques.

 El programador debe establecer el compromiso adecuadoentre apostar por memoria o paralelismo en cada c—digo. 57

Recordar: Mejor paralelismo de grano fino (asignar un datoa cada hilo). Despliegue de bloques e hilos:

 8 bloques de 8 hilos cada uno. Riesgo en bloques

peque–os: Desperdiciar paralelismo si se alcanza el m‡ximo de8/16 bloques en cada multiprocesador con pocos hilos entotal.

 4 bloques de 16 hilos cada uno. Riesgo en bloques grandes:Estrangular el espacio de cada hilo (la memoria compartida yel banco de registros se comparte entre todos los hilos).

Pensando en peque–o: Particionamiento 1Dde un vector de 64 elementos

58

Pensando a lo grande: Particionamiento 1Dde un vector de 64 millones de elementos

M‡ximo nœmero de hilos por bloque: 1K.

Maximo nœmero de bloques:64K en Fermi y 4G en Kepler.

Tama–os m‡s grandes para las estructuras de datos s—lo

pueden implementarse mediante un nœmero ingente debloques (si queremos preservar el paralelismo fino).

 Posibilidades a elegir:64K bloques de 1K hilos cada uno.

 128K bloques de 512 hilos cada uno (s—lo factible en Kepler).

 256K bloques de 256 hilos cada uno (s—lo factible en Kepler).

 ... y as’ sucesivamente.

59 60

Recopilando sobre kernels,bloques y paralelismo

Los kernels se lanzan en grids.Un bloque se ejecuta en un

multiprocesador (SMX).El bloque no migra.

 Varios bloques pueden residirconcurrentemente en un SMX.

Con las limitaciones de Kepler:Hasta 16 bloques concurrentes.Hasta 1024 hilos en cada bloque.Hasta 2048 hilos en cada SMX.

Otras limitaciones entran en juegodebido al uso conjunto de lamemoria compartida y el banco deregistros segœn hemos visto hace 3diapositivas.

Malla

Bloque (0, 0)

Memoria compartida

Hilo (0, 0)

Regs Regs

Bloque (1, 0)

Memoria compartida

Hilo (0, 0)

Regs

Hilo (1, 0)

Regs

Memoria global

Hilo (1, 0)

Page 16: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 16/34

61

Escalabilidad transparente

 Dado que los bloques no se pueden sincronizar: El hardware tiene libertad para planificar la ejecuci—n de un bloque

en cualquier multiprocesador.

Los bloques pueden ejecutarse secuencialmente o

concurrentemente segœn la disponibilidad de recursos.GPU con 2 SMX

SMX 0 SMX 1

B lo ck 0 B lo ck 1

B lo ck 2 B lo ck 3

B lo ck 4 B lo ck 5

B lo ck 6 B lo ck 7

Malla para el kernel

Block 0 Block 1

Block 2 Block 3

Block 4 Block 5

Block 6 Block 7

GPU con 4 SMX

SMX 0 SMX 1 SMX 2 SMX 3

Block 0 Block 1 Block 2 Block 3

Block 4 Block 5 Block 6 Block 7

! Un kernel se puede expandir a lo largo de cualquiernœmero de procesadores (siempre que hayamosdeclarado un nœmero suficiente de bloques).

Espacios de memoria

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 datosde la CPU de forma similar a como la CPU procede en lenguaje C

(malloc para alojar y free para liberar).

Los punteros son s—lo direcciones:No se puede conocer a travŽs del valor de un puntero si la direcci—n

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 atravŽs de punteros, ya que si un dato de la CPU trata de accedersedesde la GPU o viceversa, el programa se colgar‡.

62

Page 17: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 17/34

CUDA es C con algunas palabras clave m‡s.Un ejemplo preliminar

65

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

__global__ void saxpy_paralelo(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/bloqueint numero_de_bloques = (n + 255) / 256;saxpy_paralelo<<<numero_de_bloques, 256>>>(n, 2.0, x, y);

C—digo C est‡ndar

C—digo CUDA equivalente de ejecuci—n paralela en GPU:

Lista de extensiones sobre el lenguaje C

Modificadores para las variables(type qualifiers):

global, device, shared, local,constant.

Palabras clave (keywords):threadIdx, blockIdx.

Funciones intr’nsecas (intrinsics): __syncthreads

 API en tiempo de ejecuci—n:Memoria, s’mbolos, gesti—n de la

ejecuci—n.

Funciones kernel para lanzar66

__device__ float vector[N];

__global__ void filtro (float *image) {

  __shared__ float region[M];  ...

region[threadIdx.x] = image[i];

__syncthreads()...

imagen[j] = result;}

// Alojar memoria en la GPUvoid *myimage;

cudaMalloc(&myimage, bytes);

// 100 bloques de hilos, 10 hilos por bloqueconvolucion <<<100, 10>>> (myimage);

67

La interacci—n entre la CPU y la GPU

CUDA extiende el lenguaje C con un nuevo tipo de funci—n,kernel, que ejecutan en paralelo los hilos activos en GPU.

El resto del c—digo es C nativo que se ejecuta sobre la CPU deforma convencional.

De esta manera, el t’pico main() de C combina la ejecuci—nsecuencial en CPU y paralela en GPU de kernels CUDA.

Un kernel se lanza siempre de forma as’ncrona, esto es, elcontrol regresa de forma inmediata a la CPU.

Cada kernel GPU tiene una barrera impl’cita a su conclusi—n,esto es, no finaliza hasta que no lo hagan todos sus hilos.

 Aprovecharemos al m‡ximo el biprocesador CPU-GPU si lesvamos intercalando c—digo con similar carga computacional.

68

La interacci—n entre la CPU y la GPU (cont.)

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

ááá

GPU

CPU

GPU

            E            j   

          e           c           u           c 

            i              — 

          n

CPU

CPU

Un kernel no comienza hasta que terminan los anteriores.Podemos emplear streams para definir kernels concurrentes.

Page 18: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 18/34

Partici—n de datos para una matriz 2D [paraun patr—n de acceso paralelo, ver ejemplo 2]

69

 Anchura de la matriz

   A   l   t  u  r  a   d  e   l  a  m  a   t  r   i  z

Malla (0, 0)

Bloque (0,0) Bloque (1,0) (blockDim.x,0)

(0,blockDim.y) (1,blockDim.y) (blockDim.x, blockDim.y)

Malla (gridDim.x, 0)

B lo ck (0,0) B lo ck (1,0) (blockD im.x,0 )

(0,blockDim.y) (1,blockDim.y) (blockDim.x, blockDim.y)

Malla (0, gridDim.y)

Bloque (0,0) Bloque (1,0) (blockDim.x,0)

(0,blockDim.y) (1,blockDim.y)(blockDim.x, blockDim.y)

Malla (gridDim.x, gridDim.y)

Bloque (0,0) Bloque (1,0) (blockDim.x,0)

(0,blockDim.y) (1,blockDim.y)(blockDim.x, blockDim.y)

Dado que queremosparalelismo de grano fino,asignaremos un elemento acada hilo, que puedeconocer sus coordenadasdurante la ejecuci—n as’:

- La posici—n (hor,ver) parael bloque dentro de la mallaes (blockIdx.x, blockIdx.y).- La posici—n (hor,ver) parael hilo dentro del bloque es(threadIdx.x, threadIdx.y)

Para este hilo:- blockIdx.x es 1.- blockIdx.y es 0.- threadIdx.x es 11.- threadIdx.y es 2.

70

Modificadores para las funciones ylanzamiento 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]; // Mem. compartida

 __constant__  ßoat MyConstantArray[32];

Configuraci—n de la ejecuci—n para lanzar kernels:dim2 gridDim(100,50); // 5000 bloques de hilos 

dim3 blockDim(4,8,8); // 256 hilos por bloque

MyKernel <<< gridDim,blockDim >>> (pars.); // Lanzam.

Nota: Opcionalmente, puede haber un tercer par‡metrotras blockDim para indicar la cantidad de memoriacompartida que ser‡ alojada din‡micamente por cadakernel durante su ejecuci—n.

71

dim3 gridDim; // Dimension(es) de la malla

dim3 blockDim; // Dimension(es) del bloque

uint3 blockIdx; // Indice del bloque dentro de la malla

uint3 threadIdx; // Indice del hilo dentro del bloque

void __syncthreads(); // Sincronizaci—n entre hilos

 Variables y funciones intr’nsecas

El programador debe elegir el tama–o del bloquey el nœmero de bloques para explotar al m‡ximoel paralelismo del c—digo durante su ejecuci—n.

Funciones para conocer en tiempo deejecuci—n con quŽ recursos contamos

Cada GPU disponible en la capa hardware recibe unnœmero entero consecutivo que la identifica, comenzando porel 0.

Para conocer el nœmero de GPUs disponibles:cudaGetDeviceCount(int* count);

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 reœne ciertos requisitos:cudaChooseDevice(int* dev, const struct cudaDeviceProp* prop);

Para seleccionar una GPU concreta:cudaSetDevice(int dev);

Para conocer en quŽ GPU estamos ejecutando el c—digo:cudaGetDevice(int* dev);

72

Page 19: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 19/34

Ejemplo: Salida de la funci—ncudaGetDeviceProperties

El programa se encuentra dentro del SDK de nVidia.

73

Para gestionar la memoria de v’deo

Para reservar y liberar memoria en la GPU:cudaMalloc(puntero, tama–o)

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);

El prefijo Òh_Ó suele usarse para punteros en memoriaprincipal. Idem Òd_Ó para punteros en mem. de v’deo.

74

Ejemplo 1: Descripci—ndel c—digo a programar

 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.

76

Page 20: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 20/34

Ejemplo 1: Implementaci—n[c—digo C en rojo, extensiones CUDA en azul]

77

int main(){  int N = 16;  int num_bytes = N*sizeof(int);  int *d_a=0, *h_a=0; // Punteros en dispos. (GPU) y host (CPU)

  h_a = (int*) malloc(num_bytes);  cudaMalloc( (void**)&d_a, num_bytes);

  if( 0==h_a || 0==d_a ) printf("No pude alojar 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 as’ncronas

Las llamadas a cudaMemcpy() son s’ncronas, 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.

 A partir de CUDA Compute Capabilities 1.2 es posibleutilizar la variante cudaMemcpyAsync(), cuyas diferenciasson las siguientes:

El retorno a la CPU tiene lugar de forma inmediata.

Podemos solapar comunicaci—n y computaci—n.

78

7962

Programa C en CPU

(compilado con gcc)

El kernel CUDA que se ejecuta en la GPU

(parte superior), seguido del c—digo host

en CPU. Este archivo se compila con

nvcc (ver m‡s adelante).

void incremento_en_cpu(float *a, float b, int N)

{

  for (int idx = 0; idx<N; idx++)

a[idx] = a[idx] + b;

}

void main()

{

  .....

  incremento_en_cpu(a, b, N);

}

 __global__ void incremento_en_gpu(float *a, float b, int N)

{  int idx = blockIdx.x * blockDim.x + threadIdx.x;  if (idx < N)  a[idx] = a[idx] + b;}

void main()

{

  É..

  dim3 dimBlock (blocksize);

  dim3 dimGrid( ceil( N / (float)blocksize) );

  incremento_en_gpu<<<dimGrid, dimBlock>>>(a, b, N);

}

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

8063

Con N = 16 y blockDim = 4, tenemos 4 bloques de hilos,encarg‡ndose cada hilo de computar un elemento del vector.

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

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

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

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

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

Nota: blockDim deber’a ser >= 32 (warp size) en c—digo real, esto es s—lo un ejemplo

Patr—n de accesocomœn a todoslos hilos

   E  x   t  e  n  s   i  o  n  e  s

  a   l   l  e  n  g  u  a   j  e

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

Page 21: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 21/34

8164

// 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 con un nœmero de bloques y tama–o de bloque

incremento_en_gpu <<< N/blockSize, blockSize >>> (d_A, b);

// Copia los resultados de la GPU a la CPU

cudaMemcpy(h_A, d_A, numBytes, cudaMemcpyDeviceToHost);

// Libera la memoria de v’deo

cudaFree(d_A );

C—digo en CPU para el ejemplo 2[rojo es C, verde son vars., azul es CUDA]

El proceso global de compilaci—n

83

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(..);  ...}

NVCC(Open64)

Compiladorde la CPU

KernelsCUDA 

Ficheros objetoCUDA 

Resto delc—digo C

Ficheros objetode la CPUEnlazador

EjecutableCPU-GPU

Identificarlos kernels

CUDA y rees-cribirlos paraaprovecharparalelismo

en GPU

84

Los diferentes m—dulos de compilaci—n

NVCC

C/C++ CUDAApplication

PTX to TargetCompiler 

G80 É GPU

PTX Code

C—digo CPU

C—digoobjeto

El c—digo fuente CUDA secompila con NVCC.

NVCC separa el c—digo quese ejecuta en CPU del que lohace en GPU.

La compilaci—n se realizaen dos etapas:

 Virtual: Genera c—digo PTX(Parallel Thread eXecution).

F’sica: Genera el binario parauna GPU espec’fica (o inclusopara una CPU multicore).

C—digofuente

Virtual

F’sico

Page 22: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 22/34

85

Compilador NVCC y m‡quina virtual PTX

EDGSepara c—digo GPU y CPU.

Open64Genera ensamblador PTX.

Parallel Thread eXecution(PTX)

M‡quina virtual e ISA.

Modelo de programaci—n.

Recursos y estado de ejecuci—n.

EDG

C/C++ CUDAApplication

CPU Code

Open64

PTX Code

ld.global.v4.f32 {$f1,$f3,$f5,$f7}, [$r9+0];mad.f32 $f1, $f5, $f3, $f1;

float4 me = gx[gtid];me.x += me.y * me.z;

NVCC (NVidia CUDA Compiler)

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

cl, ...

NVCC produce como salida:C—digo C para la CPU, que debe luego

compilarse con el resto de la aplicaci—nutilizando otra herramienta.

C—digo objeto PTX para la GPU.

86

Proceso de compilaci—n Linux:

Proceso decompilaci—nen Windows:

87

Para conocer la utilizaci—n de los recursos

Compilar el c—digo del kernel con el flag -cubin paraconocer c—mo se est‡n usando los registros.

 Alternativa on-line: nvcc --ptxas-options=-v

 Abrir el archivo de texto .cubin y mirar la secci—n ÒcodeÓ.

architecture {sm_10}abiversion {0}modname {cubin}code {  name = myGPUcode  lmem = 0  smem = 68

  reg = 20  bar = 0  bincode {  0xa0004205 0x04200780 0x40024c09 0x00200780

É

Memoria local para cada hilo(usada por el compilador para

volcar contenidos de los registrosen memoria)

Memoria compartida usadapor cada bloque de hilos

Registros usadospor cada hilo

88

CUDA Occupancy Calculator

 Asesora en la selecci—n de los par‡metros de configuraci—nhttp://developer.download.nvidia.com/compute/cuda/CUDA_Occupancy_calculator.xls

Page 23: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 23/34

Para alcanzar el mayor grado de paralelismo,fijarse en el ‡rea naranja del CUDA Occup.(1)

El primer dato es el nœmero de hilos por bloque:El l’mite es 1024 en las generaciones Fermi y Kepler.

Las potencias de dos son las mejores elecciones.

Lista de candidatos: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024.

Pondremos 256 como primera estimaci—n, y el ciclo de desarrollo nosconducir‡ al valor —ptimo aqu’, aunque normalmente:

 Valores peque–os [2, 4, 8, 16] no explotan el tama–o del warp ni los bancos dememoria compartida.

 Valores intermedios [32, 64] comprometen la cooperaci—n entre hilos y laescalabilidad en Kepler, Maxwell y futuras generaciones de GPUs.

 Valores grandes [512, 1024] nos impiden tener suficiente nœmero de bloquesconcurrentes en cada multiprocesador, ya que el m‡ximo de hilos por bloque y SMXson dos valores muy pr—ximos. Adem‡s, la cantidad de registros disponibles paracada hilo es baja.

89

Para alcanzar el mayor grado de paralelismo,fijarse en el ‡rea naranja del CUDA Occup.(2)

El segundo dato es el n¼ de registros que usa cada hilo.Esto lo obtendremos del archivo .cubin.

El l’mite es 8K (G80), 16K (GT200), 32K (Fermi), o 64K (Kepler),as’ que consumiendo 10 registros/hilo es posible ejecutar:

 En G80: 768 hilos/SM, o sea, 3 bloques de 256 hilos [3*256*10=7680] (< 8192).

 En Kepler: Alcanzamos el m‡ximo de 8 bloques por SMX (2048 threads/SMX):8 bloques * 256 hilos/bloque * 10 registros/hilo = 22480 regs. (< 65536 m‡x.).

En el caso de la G80, si hubiŽramos consumido 11 registros/hilo, yano podr’amos acomodar 3 bloques en el SMX, sino s—lo 2, por lo queperder’amos 1/3 del paralelismo => Habr’a que esforzarse en reducirde 11 a 10 el nœmero de registros.

90

Para alcanzar el mayor grado de paralelismo,fijarse en el ‡rea naranja del CUDA Occup.(3)

El tercer dato es el gasto de memoria compartida en cadabloque de hilos:

Esto tambiŽn lo obtenemos del archivo .cubin, aunque podemosllevar una contabilidad manual, ya que todo depende de la declaraci—nde variables __shared__ que elegimos como programador.

L’mites: 16 KB (CCC 1.x), 16/48 KB (CCC 2.x), 16/32/48 KB (CCC3.x).

Para el caso anterior sobre la G80, no gastaremos m‡s de 5 KB dememoria compartida por cada bloque para que podamos ejecutar elm‡ximo de 3 bloques en paralelo en cada multiprocesador:

 3 bloques x 5 KB./bloque = 15 KB (< 16 KB.)

 A partir de 5.33 KB. de memoria compartida usada por cada bloque,estamos sacrificando un 33% del paralelismo, igual que suced’a antessi no Žramos capaces de bajar a 10 registros/kernel. 91

Page 24: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 24/34

93

Pasos para la construcci—ndel c—digo fuente CUDA

1. Identificar las partes del c—digo con mayor potencial parabeneficiarse del paralelismo de datos SIMD de la GPU.

2. Acotar el volumen de datos necesario para realizar dichas

computaciones.3. Transferir los datos a la GPU.

4. Hacer la llamada al kernel.

5. Establecer las sincronizaciones entre la CPU y la GPU.

6. Transferir los resultados desde la GPU a la CPU.

7. Integrarlos en las estructuras de datos de la CPU.

Se requiere cierta coordinaci—nen las tareas paralelas

El paralelismo viene expresado por los bloques e hilos.

Los hilos de un bloque pueden requerir sincronizaci—n siaparecen dependencias, ya que s—lo dentro del warp se

garantiza su progresi—n conjunta (SIMD). Ejemplo:

94

a[i] = b[i] + 7;syncthreads();x[i] = a[i-1]; // El warp 1 lee aqu’ el valor a[31],

// que debe haber sido escrito ANTES por el warp 0

En las fronteras entre kernels hay barreras impl’citas:

Kernel1 <<<nblocks,nthreads>>> (a,b,c);

Kernel2 <<<nblocks,nthreads>>> (a,b);

Bloques pueden coordinarse usando operaciones at—micas:

C—digo para GPU y su invocaci—n desde CPU

El prefijo __global__  indica que vecAdd() se ejecuta enla GPU (device) y ser‡ llamado desde la CPU (host).

A , B y C son punteros a la memoria de v’deo de la GPU, porlo que necesitamos:

 Alojar/liberar memoria en GPU, usando cudaMalloc /cudaFree.

Estos punteros no pueden ser utilizados desde el c—digo de la CPU. 96

// Suma de dos vectores de tama–o N: C[1..N] = A[1..N] + B[1..N]

// Cada hilo computa un solo componente del vector resultado C __global__ void vecAdd(ßoat* A, ßoat* B, ßoat* C) {  int tid = threadIdx.x + (blockDim.x* blockIdx.x);  C[tid] = A[tid] + B[tid];}

C—digo GPU

int main() { // Lanza N/256 bloques de 256 hilos cada uno

 

vecAdd<<< N/256, 256>>>(d_A, d_B, d_C);}

C—digo CPU

Page 25: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 25/34

9764

unsigned int numBytes = N * sizeof(float);

// Aloja memoria en la CPU

float* h_A = (float*) malloc(numBytes);

float* h_B = (float*) malloc(numBytes);

... inicializa h_A y h_B ...

// Aloja memoria en la GPU

float* d_A = 0; cudaMalloc((void **)&d_A, numBytes);

float* d_B = 0; cudaMalloc((void **)&d_B, numBytes);

float* d_C = 0; cudaMalloc((void **)&d_C, numBytes);

// Copiar los datos de entrada desde la CPU a la GPU

cudaMemcpy(d_A, h_A, numBytes, cudaMemcpyHostToDevice);

cudaMemcpy(d_B, h_B, numBytes, cudaMemcpyHostToDevice);

(aqu’ colocamos la llamada al kernel VecAdd de la p‡g. anterior)

// Copiar los resultados desde la GPU a la CPU

float* h_C = (float*) malloc(numBytes);

cudaMemcpy(h_C, d_C, numBytes, cudaMemcpyDeviceToHost);

// Liberar la memoria de v’deo

cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);

C—digo CPU para manejar la memoria yrecoger los resultados de GPU

Ejecutando en paralelo(independientemente de la generaci—n HW)

vecAdd<<< 1, 1 >>>():Ejecuta 1 bloque de 1 hilo. Nohay paralelismo.

vecAdd<<< B, 1 >>>():Ejecuta B bloques de 1 hilo. Hayparalelismo entremultiprocesadores.

vecAdd<<< B, M >>>():Ejecuta B bloques compuestos deM hilos. Hay paralelismo entremultiprocesadores y entre loscores del multiprocesador.

98

GPUMultiprocesador 30

Multiprocesador 2

Multiprocesador 1

Memoria global

Memoria compartida

Core 1

Registros

ÉCore 2

Registros

Core 8

Registros

CachŽ de texturas

(escalabilidad en 1» gener.)

(escalabilidad en 2» y 3» gener.)

Con M hilos por bloque, un ’ndice un’voco viene dado por:

tid = threadIdx.x+ blockDim.x* blockIdx.x;

Para acceder a un vector en el que cada hilo computa unsolo elemento (queremos paralelismo de grano fino), B=4

bloques de M=8 hilos cada uno:

ÀQuŽ hilo computar‡ el vigŽsimo segundo elemento delvector?

gridDim.x es 4. blockDim.x es 8. blockIdx.x = 2. threadIdx.x = 5.

tid = 5 + (8 * 2) = 21 (al comenzar en 0, Žste es el elemento 22).

Calculando el ’ndice de acceso a un vectorsegœn el bloque y el hilo dentro del mismo

99

0

threadIdx.x threadIdx.x threadIdx.x threadIdx.x

 blockIdx.x = 0 blockIdx.x = 1 blockIdx.x = 2 blockIdx.x = 3

Manejando vectores de tama–o genŽrico

Los algoritmos reales no suelen tener dimensiones quesean mœltiplos exactos de blockDim.x, as’ que debemosdesactivar los hilos que computan fuera de rango:

 Y ahora, hay que incluir el bloque "incompleto" de hilosen el lanzamiento del kernel (redondeo al alza en la div.):

100

// Suma dos vectores de tama–o N: C[1..N] = A[1..N] + B[1..N] __global__ void vecAdd(ßoat* A, ßoat* B, ßoat* C, N) {

int tid = threadIdx.x + (blockDim.x * blockIdx.x);if (tid < N)

  C[tid] = A[tid] + B[tid];}

  vecAdd<<< (N + M-1)/256, 256>>>(d_A, d_B, d_C, N);

Page 26: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 26/34

Por quŽ hemos seleccionado este c—digo

En el ejemplo anterior, los hilos a–aden complejidad sinrealizar contribuciones rese–ables.

Sin embargo, los hilos pueden hacer cosas que no est‡n al

alcance de los bloques paralelos:Comunicarse (a travŽs de la memoria compartida).

Sincronizarse (por ejemplo, para salvar los conflictos pordependencias de datos).

Para ilustrarlo, necesitamos un ejemplo m‡s sofisticado ...

102

Patr—n unidimensional (1D)

 Apliquemos un patr—n 1D a un vector 1D. Al finalizar el algoritmo, cada elemento contendr‡ la suma de todos

los elementos dentro de un radio pr—ximo al elemento dado.

Por ejemplo, si el radio es 3, cada elemento agrupar‡ lasuma de 7:

De nuevo aplicamos paralelismo de grano fino, por lo quecada hilo se encargar‡ de obtener el resultado de un soloelemento del vector resultado.

Los valores del vector de entrada deben leerse varias veces:Por ejemplo, para un radio de 3, cada elemento se leer‡ 7 veces. 103

radio radio

Compartiendo datos entre hilos. Ventajas

Los hilos de un mismo bloque pueden compartir datos atravŽs de memoria compartida.

La memoria compartida es gestionada de forma expl’citapor el programador insertando el prefijo __shared__  en ladeclaraci—n de la variable.

Los datos se alojan para cada uno de los bloques.

La memoria compartida es extremadamente r‡pida:500 veces m‡s r‡pida que la memoria global (que es memoria de video GDDR5).

La diferencia es tecnol—gica: est‡tica (construida con transistores) frente a din‡mica(construida con mini-condensadores).

El programador puede ver la memoria compartida como una extensi—n del bancode registros.

Pero la memoria compartida es m‡s vers‡til que los registros, yaque Žstos son privados para cada hilo. 104

Page 27: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 27/34

Compartiendo datos entre hilos. Limitaciones

El uso de los registros y la memoria compartida limita elparalelismo.

Si dejamos espacio para un segundo bloque en cada multiprocesador,el banco de registros y la memoria compartida se particionan (aunque

la ejecuci—n no es simult‡nea, el cambio de contexto es inmediato).

 Ya mostramos ejemplos para Kepler anteriormente. Con unm‡ximo de 64K registros y 48 Kbytes de memoria compartidapor cada multiprocesador SMX, tenemos:

Para 2 bl. /SMX: No superar 32 Kregs. ni 24 KB. de memoria comp.

Para 3 bl. /SMX: No superar 21.33 Kregs. ni 16 KB. de memoria comp.

Para 4 bl. /SMX: No superar 16 Kregs. ni 12 KB. de memoria comp.

... y as’ sucesivamente. Usar el CUDA Occupancy Calculator paraasesorarse en la selecci—n de la configuraci—n m‡s adecuada. 105

Utilizando la memoria compartida

Pasos para cachear los datos en memoria compartida:Leer (blockDim.x + 2 * radio) elementos de entrada desde la

memoria global a la memoria compartida.

Computar blockDim.x elementos de salida.

Escribir blockDim.x elementos de salida a memoria global.

Cada bloque necesita una extensi—n de radio elementosen los extremos del vector.

106

blockDim.x elementos de salida

extensi—n porla izquierda

extensi—n porla derecha

Kernel patr—n

107

 __global__ void stencil_1d(int *d_in, int *d_out,

int RADIO)

{

  __shared__  int temp[BLOCK_SIZE + 2 * RADIO];

  int gindex = threadIdx.x

+ blockIdx.x * blockDim.x;

  int lindex = threadIdx.x + RADIO;

  // Pasar los elementos a memoria compartida

  temp[lindex] = d_in[gindex];  if (threadIdx.x < RADIO) {

  temp[lindex-RADIO] = d_in[gindex-RADIO];

  temp[lindex+BLOCK_SIZE] = d_in[gindex

+BLOCK_SIZE];

  }

  // Aplicar el patr—n

  int result = 0;

  for (int offset=-RADIO; offset<=RADIO; offset++)

  result += temp[lindex + offset];

  // Almacenar el resultado

  d_out[gindex] = result;

}

Pero hay que prevenircondiciones de carrera. Porejemplo, el œltimo hilo lee laextensi—n antes del que elprimer hilo haya cargadoesos valores (en otro warp).Es necesaria unasincronizaci—n entre hilos.

Sincronizaci—n entre hilos

Usar __synchthreads(); para sincronizar todos los hilos deun bloque:

La barrera tiene que ser alcanzadapor todos los hilos antes de que Žstospuedan continuar.

Puede utilizarse para prevenirriesgos del tipo RAW / WAR / WAW.

En sentencias condicionales, lacodici—n debe ser uniforme a lo largode todo el bloque.

108

 __global__ void stencil_1d(...) {

  < Declarar variables e ’ndices>

  < Pasar el vector a mem. compartida >

 

 __synchthreads();

  < Aplicar el patr—n >

  < Almacenar el resultado >

}

Page 28: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 28/34

Recopilando los conceptos puestosen pr‡ctica en este ejemplo

Lanzar N bloques con M hilos por bloque para ejecutar los hilosen paralelo. Emplear:

kernel<<<N,M>>>();

 Acceder al ’ndice del bloque dentro de la malla y al ’ndice del hilo

dentro del bloque. Emplear:blockIdx.x  y threadIdx.x;

Calcular los ’ndices globales donde cada hilo tenga que trabajaren un ‡rea de datos diferente segœn la partici—n. Emplear:

int index = threadIdx.x + blockIdx.x * blockDim.x;

Declarar el vector en memoria compartida. Emplear: __shared__  (como prefijo antecediendo al tipo de dato en la declaraci—n).

Sincronizar los hilos para prevenir los riesgos de datos. Emplear: __synchthreads();

109

C—digo en GPU para el kernel ReverseArray(1) utilizando un œnico bloque

111

 __global__ void reverseArray(int *in, int *out) {

  int index_in = threadIdx.x;

  int index_out = blockDim.x Ð 1 Ð threadIdx.x;

  // Invertir los contenidos del vector con un solo bloque

  out[index_out] = in[index_in];

}

Es una soluci—n demasiado simplista: No aspira a aplicarparalelismo masivo porque el m‡ximo tama–o de bloquees 1024 hilos, con lo que Žse ser’a el mayor vector queeste c—digo podr’a aceptar como entrada.

C—digo en GPU para el kernel ReverseArray(2) con mœltiples bloques

112

 __global__ void reverseArray(int *in, int *out) {

  int in_offset = blockIdx.x * blockDim.x;

  int out_offset = (gridDim.x Ð 1 Ð blockIdx.x) * blockDim.x;

int index_in = in_offset + threadIdx.x;

  int index_out = out_offset + (blockDim.x Ð 1 Ð threadIdx.x);

  // Invertir los contenidos en fragmentos de bloques enteros

  out[index_out] = in[index_in];

}

Page 29: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 29/34

 Versi—n utilizando la memoria compartida

113

C—digo en GPU para el kernel ReverseArray(3) con mœltiples bloques y mem. compartida

114

 __global__ void reverseArray(int *in, int *out) {

  __shared__  int temp[BLOCK_SIZE];

  int gindex = threadIdx.x + blockIdx.x * blockDim.x;

  int lindex = threadIdx.x;

  // Colocar los elementos de entrada en memoria compartida

  temp[lindex] = in[gindex];

  synchthreads();

  // Invertir los elementos del vector local a cada bloque

  temp[lindex] = temp[blockDim.x-lindex-1];

  synchthreads();

  // Invertir los contenidos en fragmentos de bloques enteros

  out[threadIdx.x + ((N/blockDim.x)-blockIdx.x-1) * blockDim.x] = temp[lindex];

}

 Versi—n de c—digo CPU escrita en lenguaje C

 C = A * B.

 Matrices cuadradas de tama–o N * N.

 Linearizadas en vectores para simplificarel alojamiento de memoria din‡mica.

116

void MxMonCPU(float* A, float* B, float* C, int N);{  for (int i=0; i<N; i++)  for (int j=0; j<N; j++)  {  float sum=0;  for (int k=0; k<N; k++)

{  float a = A[i*N + k];  float b = B[k*N + j];  sum += a*b;  }  C[i*N + j] = sum;  }}

A

B

C

      N

N N

Page 30: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 30/34

 Versi—n CUDA para el producto de matrices:Un primer borrador para el c—digo paralelo

117

__global__ MxMonGPU(float* A, float* B, float* C, int N);{  float sum=0;  int i, j;  i = threadIdx.x + blockIdx.x * blockDim.x;  j = threadIdx.y + blockIdx.y * blockDim.y;  for (int k=0; k<N; k++){

  float a = A[i*N + k];  float b = B[k*N + j];  sum += a*b;  }  C[i*N + j] = sum;}

A

B

C

    N

N   N

118

 Versi—n CUDA para el producto de matrices:Descripci—n de la paralelizaci—n

 Cada hilo computa un elemento de la matriz resultado C.Las matrices A y B se cargan N veces desde memoria de v’deo.

 Los bloques acomodan los hilos en grupos de 1024(limitaci—n interna en arquitecturas Fermi y Kepler). As’podemos usar bloques 2D de 32x32 hilos cada uno.

    N

    N

N   N

X=C(x, y)

Anchura A

   A   l   t  u  r  a   A

Anchura B

AlturaA

Anchura B

C A B! ! ! ! ! ! ! ! ! ! ! ! ! ! !

! ! ! ! ! ! !! ! ! !

! ! ! !! ! ! ! ! !

! ! ! !

! ! ! !! ! ! ! ! !

! ! ! ! ! ! !! ! ! !

! ! ! !! ! ! ! ! !

! ! ! !

! ! ! !! ! ! ! ! !

MallaBloque

Hilo(x,y) dim2  dimBlock(BLOCKSIZE, BLOCKSIZE);dim2  dimGrid(AnchuraB/BLOCKSIZE, AlturaA/BLOCKSIZE);...MxMonGPU <<<dimGrid,dimBlock>>> (A, B, C, N);

   A   l   t  u  r  a   B

 Versi—n CUDA para el producto de matrices: An‡lisis

 Cada hilo utiliza 10 registros, lo que nos permite alcanzarel mayor grado de paralelismo en Kepler:

2 bloques de 1024 hilos (32x32) en cada SMX. [2x1024x10 =20480 registros, que es inferior a la cota de 65536 registrosdisponibles].

 Problemas:Baja intensidad aritmŽtica.

Exigente en el ancho de banda a memoria, que termina erigiŽndosecomo el cuello de botella para el rendimiento.

 Soluci—n:Utilizar la memoria compartida de cada multiprocesador.

119 120

 La submatriz de Csub de 32x32 datos

computada por cada bloque de hilosutiliza mosaicos de 32x32 elementos de Ay B que se alojan de forma reiterativa enmemoria compartida.

 A y B se cargan s—lo (N/32) vecesdesde memoria global.

 Logros:Menos exigente en el

ancho de banda a memoria.

M‡s intensidad aritmŽtica.

A

B

C

Csub

MM M M

    M

    M

    M

    M

    N

    N

N   N

Utilizando la memoria compartida: Versi—n con mosaicos (tiling)

Page 31: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 31/34

Tiling: Detalles de la implementaci—n

 Tenemos que gestionar todos los mosaicos pertenecientes acada bloque de hilos:

Cargar en paralelo (contribuyen todos los hilos) los mosaicos de entrada(A y B) desde memoria global a memoria compartida. Estos mosaicos

reutilizan el espacio de memoria compartida.  __syncthreads() (para asegurarnos que hemos cargado las matrices

completamente antes de comenzar la computaci—n).

Computar todos los productos y sumas para C utilizando los mosaicos dememoria compartida.

Cada hilo puede ahora iterar eficientemente sobre los elementos del mosaico.

  __syncthreads() (para asegurarnos que la computaci—n con elmosaico ha acabado antes de cargar dos nuevos mosaicos para A y B en lasiguiente iteraci—n).

Transferir los resultados de C alojamos en memoria global.121

Un truco para evitar conflictos en el accesoa los bancos de memoria compartida

 Recordemos algunos rasgos de CUDA:La memoria compartida consta de 16 (pre-Fermi) — 32 bancos.

Los hilos de un bloque se enumeran en orden "column major", estoes, la dimensi—n x es la que m‡s r‡pidamente var’a.

Si accedemos de la forma habitual a los vectores enmemoria compartida: shData[threadIdx.x][threadIdx.y], loshilos de un mismo warp leer‡n de la misma columna, estoes, del mismo banco en memoria compartida.

 En cambio, utilizando shData[threadIdx.y][threadIdx.x],leer‡n de la misma fila, accediendo a un banco diferente.

 Por tanto, los mosaicos se almacenan y acceden enmemoria compartida de forma invertida o traspuesta.

122

Tiling: El c—digo CUDA para el kernel en GPU

123

__global__ void MxMonGPU(float *A, float *B, float *C, int N){  int sum=0, tx, ty, i, j;  tx = threadIdx.x; ty = threadIdx.y;  i = tx + blockIdx.x * blockDim.x; j = ty + blockIdx.y * blockDim.y;  __shared__  float As[32][32], float Bs[32][32];

  // Recorre los mosaicos de A y B necesarios para computar la submatriz de C  for (int tile=0; tile<(N/32); tile++)

{ // Carga los mosaicos (32x32) de A y B en paralelo (y de forma traspuesta)

  As[ty][tx]= A[(i*N) + (ty+(tile*32))];Bs[ty][tx]= B[((tx+(tile*32))*N) + j];

   __syncthreads();  // Computa los resultados para la submatriz de C 

  for (int k=0; k<32; k++) // Los datos tambiŽn se leer‡n de forma traspuesta  sum += As[k][tx] * Bs[ty][k];  __syncthreads();  }  // Escribe en paralelo todos los resultados obtenidos por el bloque  C[i*N+j] = sum;}

124

Una optimizaci—n gracias al compilador:Desenrrollado de bucles (loop unrolling)

Sin desenrrollar el bucle: Desenrrollando el bucle:

  ...   __syncthreads();

  // Computar la parte de ese mosaico

  for (k=0; k<32; k++)  sum += As[tx][k]*Bs[k][ty];

   __syncthreads();}C[indexC] = sum;

  ...   __syncthreads();

  // Computar la parte de ese mosaico

  sum += As[tx][0]*Bs[0][ty];  sum += As[tx][1]*Bs[1][ty];

  sum += As[tx][2]*Bs[2][ty];  sum += As[tx][3]*Bs[3][ty];  sum += As[tx][4]*Bs[4][ty];  sum += As[tx][5]*Bs[5][ty];  sum += As[tx][6]*Bs[6][ty];  sum += As[tx][7]*Bs[7][ty];  sum += As[tx][8]*Bs[8][ty];

  !!!!

  sum += As[tx][31]*Bs[31][ty];  __syncthreads();}C[indexC] = sum;

Page 32: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 32/34

125

Rendimiento con tiling & unrolling en la G80

0

25

50

75

100

 GF L  OP  S 

4x4 8x8 12x12 16x16

Tama–o del mosaico (32x32 no es factible en la G80)

S—lo tilingTiling & Unrolling

CUDA Zone:La web b‡sica del programador CUDA

[http://developer.nvidia.com/category/zone/cuda-zone]:127

- Lenguajes (C/C++, Python).- Librer’as (cuBLAS, cuFFT).- Directivas (OpenACC).- Templates (thrust).

- Compilador (NVCC).

- Depurador (GDB).- Profiler (cudaprof y Visual).- Entorno de desarrollo (Nsight).- C—digos de ejemplo.

- Eclipse.- Matlab.- CUDA Fortran.- GPUDirect.- SDK para el compilador LLVM.

CUDA 5.5 Toolkit de descarga gratuita paratodas las plataformas y sistemas operativos

128

Page 33: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 33/34

Libros sobre CUDA: Desde 2007 hasta 2013

La serie GPU Gems [http://developer.vidia.com/content/GPUGems3/gpugems3_part01.html] 

Una lista de libros de CUDA en [http://www.nvidia.com/object/cuda_books.html]

129

Sep'07 Feb'10 Jul'10 Abr'11 Oct'11

Nov'11 Dic'12 Jun'13 Oct'13

Gu’as de desarrollo y otros documentos

Para iniciarse con CUDA C: La gu’a del programador.

http://docs.nvidia.com/cuda/cuda-c-programming-guide

Para optimizadores de c—digo: La gu’a con los mejores trucos.

http://docs.nvidia.com/cuda/cuda-c-best-practices-guide

La web ra’z que aglutina todos los documentos ligados a CUDA:http://docs.nvidia.com/cuda

donde encontramos, adem‡s de las gu’as anteriores, otras para:Instalar CUDA en Linux, MacOS y Windows.

Optimizar y mejorar los programas CUDA sobre plataformas Kepler.

Consultar la sintaxis del API de CUDA (runtime, driver y math).

 Aprender a usar librer’as como cuBLAS, cuFFT, cuRAND, cuSPARSE, ...

Manejarse con las herramientas b‡sicas (compilador, depurador, optimizador).

130

Cursos on-line, tutoriales y webinarios

Cursos on-line de acceso gratuito:Introducci—n a la programaci—n paralela: [www.udacity.com]

Programaci—n paralela heterogŽnea: [www.coursera.org]

Los han realizado m‡s de 50.000 programadores de 127 pa’ses en losœltimos seis meses. Impartidos por reputados pedagogos:

 Prof. Wen-mei W. Hwu (Univ. of Illinois). Prof. John Owens (Univ. of California at Davis).

 Dr. David Luebke (Nvidia Research).

Webinarios sobre computaci—n en GPU:Listado hist—rico de charlas en v’deo (mp4/wmv) y diapositivas (PDF).

Listado de pr—ximas charlas on-line a las que poder apuntarse.[http://developer.nvidia.com/gpu-computing-webinars]

383 charlas de la GTC'13 (Graphics Technology Conference).[http://www.gputechconf.com/gtcnew/on-demand-gtc.php]

131

Desarrolladores

 Para firmar como desarrollador registrado:http://developer.nvidia.com/user/register

 Sitio de encuentro con otros muchos desarrolladores: http://www.gpucomputing.net

 Noticias y eventos de GPGPU: http://www.gpgpu.org

 Lista oficial de GPUs que soportan CUDA: http://developer.nvidia.com/cuda-gpus

 Y una œltima herramienta: El CUDA Occupancy Calculatorhttp://developer.download.nvidia.com/compute/cuda/

CUDA_Occupancy_calculator.xls

132

Page 34: Teoria acerca de CUDA

7/24/2019 Teoria acerca de CUDA

http://slidepdf.com/reader/full/teoria-acerca-de-cuda 34/34

Muchas gracias por vuestra atenci—n

Siempre a vuestra disposici—n en elDepartamento de Arquitectura deComputadores de la Universidad de M‡laga

e-mail: [email protected]

TelŽfono: +34 952 13 28 24.

P‡gina Web: http://manuel.ujaldon.es(versiones en castellano e inglŽs).

O de forma m‡s espec’fica sobre GPUs,visita mi p‡gina Web como CUDA Fellow:

http://research.nvidia.com/users/manuel-ujaldon

133