Streams, Memoria Unificada, etc. · 2016. 4. 13. · Streams – Hardware de Fermi 20! Los streams...

Preview:

Citation preview

1

Streams, Memoria Unificada, etc.

Clase 15

(thanks: Carlos Bederián bc@famaf.unc.edu.ar

IFEG-CONICET, FaMAF-UNC)

Motivación

2

! La GPU queda muy “lejos” del resto del sistema

– PCI Express 3.0 16X: 15.75 GB/s, bidireccional – Necesidad de ocultar esta latencia

! La GPU tiene poca memoria

– Necesidad de subir→procesar→bajar partes de sistemas grandes ! Si el kernel es computacionalmente intensivo (ej: MM),

puede operarse sin pérdida de performance

Motivación (cont.)

3

! Necesidad de concurrencia entre kernels dentro de una GPU

– A veces una GPU tiene demasiados recursos de ejecución para correr un único kernel

– Queremos poder correr múltiples kernels en paralelo

Kernels concurrentes

4

! Secuencial

! Cuatro en paralelo

cudaMemcpy(d_a,a,numBytes,cudaMemcpyHostToDevice);

some_kernel<<<1,N>>>(d_a)

cudaMemcpy(a,d_a,numBytes,cudaMemcpyDeviceToHost);

CPU - GPU Blocking

CPU - NON Blocking

Hasta ahora…

5

cudaMemcpy(d_a,a,numBytes,cudaMemcpyHostToDevice);some_kernel<<<1,N>>>(d_a)

some_cpu_work(a)cudaMemcpy(a,d_a,numBytes,cudaMemcpyDeviceToHost);

Overlaping CPU - GPU

6

Ocultamiento de latencia

7

Copia y cómputo (GeForce)

Secuencial

Doble copia y cómputo (>Tesla)

Streams

8

! Modelo de concurrencia dentro de la GPU

! Colas de tareas que se ejecutan secuencialmente en el orden que se despachan

! Todos los streams se ejecutan concurrentemente (si pueden)

Streams – Creación/Destrucción

9

cudaError_tcudaStreamCreate(cudaStream_t*s)

– Crea un nuevo stream y lo devuelve en *s

– Devuelve un cudaError_t como todo CUDA

! ¡Verificar el resultado! (nunca está de más recordarlo)

cudaStreamDestroy(cudaStream_tstream)

– Espera que stream termine su trabajo y lo destruye.

Streams – Ejecución

10

Encolar un kernel en un stream: kernel<<<grid,block,shared,stream>>>(...)

¡Parámetros de configuración nuevos!

shared: Memoria compartida definida en tiempo de ejecución

! Declaración en el kernel: extern__shared__Tbuf[];

! Por defecto: 0 bytes

stream: Stream donde encolar el kernel. ! Por defecto: Stream 0 (“null stream”)

– Creado automáticamente – Semántica distinta a la del resto de los streams

Streams – Memoria

11

! Encolar un memset: cudaMemset*Async(...,cudaStream_tstream)

! Encolar una transferencia: cudaMemcpy*Async(...,cudaStream_tstream)

OJO: La memoria del host debe ser pinned para que funcione

Pinned memory

12

Las copias asíncronas son manejadas por un DMA engine en la GPU ¡Pero el sistema operativo puede mudar las páginas de memoria!

CUDA provee funciones para usar memoria no paginable:

cudaMallocHost(void**ptr,size_tsize)

cudaFreeHost(void*ptr)

- Uso similar a cudaMalloc/cudaFree pero para memoria del host

- Copias más rápidas

¡Usar con precaución!

Streams – Sincronización

13

Esperar que termine un stream (como cudaDeviceSynchronize):

cudaStreamSynchronize(cudaStream_ts)

Consultar si un stream terminó (sin bloquear):

cudaError_tcudaStreamQuery(cudaStream_ts)

Streams – Eventos

14

! Esperar que termine un stream o todo el trabajo encolado en la GPU no es suficientemente fino

! Los eventos se introducen para cubrir esta necesidad – Marcadores que se insertan entre operaciones de un stream – Funciones de sincronización de eventos

Streams – Eventos (cont.)

15

Creación

cudaEvent_tevent;cudaEventCreate(&event);

Destrucción

cudaEventDestroy(event);

Inserción en un stream

cudaEventRecord(event,stream);

Streams – Eventos (cont.)

16

Ver si ocurrió un evento

cudaError_tstatus=cudaEventQuery(event);

Esperar que ocurra un evento

cudaEventSynchronize(event);

Hacer que un stream espere que se dé un evento antes de seguir:

cudaStreamWaitEvent(stream,event,0);

– Esto ocurre sin intervención del host

Streams – Cosas a evitar

17

Stream nulo

Sincroniza el device después de cada operación

Sincroniza el host después de cada operación, salvo algunas excepciones

Sincronización implícita cudaMallocHost/cudaHostAlloccudaMalloccudaMemcpy*/cudaMemset*(noAsync)

...

OJO: Estamos acostumbrados a esta falta de concurrencia

Streams – Ejemplo

18

cudaStream_tstream[3];

for(inti=0;i<3;++i)

cudaStreamCreate(&stream[i]);

for(inti=0;i<3;++i){

kernel_A<<<grid,block,0,stream[i]>>>(...);

kernel_B<<<grid,block,0,stream[i]>>>(...);

kernel_C<<<grid,block,0,stream[i]>>>(...);

}

cudaDeviceSynchronize();

for(inti=0;i<3;++i)

cudaStreamDestroy(stream[i]);

Streams – Ejemplo en Fermi

19

Streams – Hardware de Fermi

20

! Los streams son una abstracción del hardware subyacente

! Los streams se multiplexan a 3 colas en el hardware:

– Compute engine

– Device → Host engine

– Host → Device engine

! Las operaciones se ejecutan:

– En el orden que fueron despachadas

– Concurrentemente si pertenecen a distintos streams

KA1 < KA2 < KA3 = KB1 < KB2 < KB3 = KC1 < KC2 < KC3

! Fermi soporta hasta 16 grids activos concurrentes

Streams – Ejemplo (de nuevo)

21

cudaStream_tstream[3];

for(inti=0;i<3;++i)

cudaStreamCreate(&stream[i]);for(inti=0;i<3;++i)

kernel_A<<<grid,block,0,stream[i]>>>(...);

for(inti=0;i<3;++i)

kernel_B<<<grid,block,0,stream[i]>>>(...);

for(inti=0;i<3;++i)

kernel_C<<<grid,block,0,stream[i]>>>(...);

cudaDeviceSynchronize();

for(inti=0;i<3;++i)

cudaStreamDestroy(stream[i]);

Streams – Ejemplo (de nuevo)

22

Streams – Estrategias

23

No hay una solución buena para todos los problemas

Ver qué pasa en el profiler

Streams – Kepler

24

! SM 3.5 (GK110 / GK207)

– Una cola en hardware por stream

– Hasta 32 grids activos concurrentemente

– No hay dependencias ocultas entre streams

Fermi vs. Kepler

25

  

Hyper‐Q offers significant benefits for use in MPI‐based parallel computer systems. Legacy MPI‐based algorithms were often created to run on multi‐core CPU systems, with the amount of work assigned to each MPI process scaled accordingly. This can lead to a single MPI process having insufficient work to fully occupy the GPU. While it has always been possible for multiple MPI processes to share a GPU, these processes could become bottlenecked by false dependencies. Hyper‐Q removes those false dependencies, dramatically increasing the efficiency of GPU sharing across MPI processes.  

 

 

Hyper‐Q working with CUDA Streams: In the Fermi model shown on the left, only (C,P) & (R,X) can run concurrently due to intra‐stream dependencies caused by the single hardware work queue. The Kepler Hyper‐Q model allows all streams to run concurrently using separate work queues.  

 

 

 

 

 

Streams – Bibliotecas

26

Encolan sus kernels en el stream que se les configure

CUFFT cufftResultcufftSetStream(cufftHandleplan,cudaStream_tstream);

CUBLAS cublasStatus_tcublasSetStream(cublasHandle_thandle,cudaStream_tstream);

CUSPARSE cusparseStatus_tcusparseSetStream(cusparseHandle_th,cudaStream_tstream);

Unified Virtual Memory

27

Unified Virtual Memory

28

//allocomemoria

a=(float*)malloc(numBytes);

cudaMalloc(&d_a,numBytes);

//copiaH2D

cudaMemcpy(d_a,a,numBytes,cudaMemcpyHostToDevice);

//llamadoakernel

some_kernel<<<1,N>>>(d_a)

//copiaD2H

cudaMemcpy(a,d_a,numBytes,cudaMemcpyDeviceToHost);

//sigueelcódigo…

//allocomemoria

cudaMallocManaged(&a,numBytes);

//llamadoakernel

some_kernel<<<1,N>>>(a);

cudaDeviceSynchronize();

//sigueelcódigo…

Unified Virtual Memory

29

//variablesglobales'managed'

__device____managed__a[10];

__device____managed__x;

intmain(){

a[2]=3;

x=a[4];

//llamadoakernel

some_kernel<<<1,N>>>()

//sigueelcódigo…

}

Unified Virtual Memory

30

! GPU tiene acceso exclusivo durante la ejecución de un kernel.

! CPU y GPU no pueden acceder a memoria managed concurrentemente, ni siquiera si son variables distintas.

! Apéndice J de la CUDA C Programming Guide para más detalles.

Unified Virtual Memory: Streams

31

//variablesglobales'managed'

__device____managed__x;

__device____managed__intx,y=2;

__global__voidkernel(){

x=10;}

intmain(){

cudaStream_tstream1;

cudaStreamCreate(&stream1);

cudaStreamAttachMemAsync(stream1,&y,0,cudaMemAttachHost);

cudaDeviceSynchronize();//WaitforHostattachmenttooccur.

kernel<<<1,1,0,stream1>>>();//Note:Launchesintostream1.

y=20;//Success–akernelisrunningbut“y”

//hasbeenassociatedwithnostream.

return0;

}

Unified Virtual Memory: Deep copies

32

voidlaunch(dataElem*elem){

dataElem*d_elem;

char*d_name;

intnamelen=strlen(elem->name)+1;

//Allocatestorageforstructandname

cudaMalloc(&d_elem,sizeof(dataElem));

cudaMalloc(&d_name,namelen);

//Copyupeachpieceseparately,includingnew“name”pointervalue

cudaMemcpy(d_elem,elem,sizeof(dataElem),cudaMemcpyHostToDevice);

cudaMemcpy(d_name,elem->name,namelen,cudaMemcpyHostToDevice);

cudaMemcpy(&(d_elem->name),&d_name,sizeof(char*),cudaMemcpyHostToDevice);

//Finallywecanlaunchourkernel,butCPU&GPUusedifferentcopiesof“elem”

Kernel<<<...>>>(d_elem);

}

structdataElem{

intprop1;

intprop2;

char*name;

}

Unified Virtual Memory: Deep copies

33

voidlaunch(dataElem*elem){

Kernel<<<...>>>(elem);

}

structdataElem{

intprop1;

intprop2;

char*name;

}

Bibliografía

34

Justin Luitjens : Cuda Streams: Best Practices and common pitfalls- NVIDIA , GTC 2014

Steve Rennich, CUDA C/C++ Streams and Concurrency, GTC 2011

L. Nyland, S. Jones, Inside Kepler, GTC 2012

35

¿Che, y Thrust?

36

Thrust soporta streams

Recommended