Disciplina de TICs 1 - Introdução a Programação em GPGPU

Transcrição

Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Disciplina de TICs 1 - Introdução a Programação
em GPGPU
Prof. Tiago A. E. Ferreira
Aula 11 - Streams (Parte II)
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Introdução
Foi visto na aula passada que é possı́vel que a GPU controle
uma certa porção de memória do Host.
Para tanto é necessário utilizar uma memória não paginável no
Host
Também foi visto que é possı́vel gerar um stream em cuda
como uso das memórias não pagináveis.
Cada stream pode ser visto como uma fila processos a serem
executados na GPU (de forma paralela)
O que ainda não foi visto foi o uso de múltiplos stream.
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Múltiplos Stream
Na aula passada foi vista uma aplicação que utilizava o
conceito de stream para a realização de processamento sobre
a GPU
Contudo, esta aplicação utilizava apenas um único stram.
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Múltiplos Stream
Na aula passada foi vista uma aplicação que utilizava o
conceito de stream para a realização de processamento sobre
a GPU
Contudo, esta aplicação utilizava apenas um único stram.
A ideia agora é adaptar tal aplicação para que sejam aplicados
mais de um stream na realização do processamento sobre a
GPU
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
A Aplicação
Inicialmente ainda se verifica se a GPU tem suporte a avorlap
Uma vez a GPU tenha este suporte, então a computação é
quebrada em pedaços
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
A Aplicação
Inicialmente ainda se verifica se a GPU tem suporte a avorlap
Uma vez a GPU tenha este suporte, então a computação é
quebrada em pedaços
A ideia para se se melhorar a versão desta aplicação é baseada
em dois pontos:
A computação dos pedaços e a sobreposição de memória são
reproduzidas com a execução do kernel
Será realizado um esforço de tal forma que o stream 1 copie os
buffers de entrada para a GPU e o stream 0 execute o kernel.
Então, o stream 1 executa o kernel enquanto o stream 0
executa a cópia dos resultados para o host.
Então, o sream 1 irá copiar seus resultados para o host
enquanto o stream 0 começa a executar seu kernel sobre o
próximo pedaço dos dados.
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Linha do Tempo
É assumido que cada caixa tem
a mesma duração temporal
Assim, é assumido que a GPU
pode executar um kernel ao
mesmo tempo que realiza cópia
de memória
memcpy =
cudaMemcpyAscyn()
As caixas vazias representa a
espera de um stream de uma
operação que não aceita
sobreposição
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
A Sobreposição
Dependendo a placa que se esteja trabalhando a capacidade
de sobreposição pode ser alterada.
Quando uma placa permite sobreposição, tem-se
minimamente,
Execução de um Kernel
Uma Cópia de Memória
Em placa mais modernas é possı́vel ter a sobreposição de,
Execução de um Kernel
Duas Cópias de Memória
Uma cópia do Host para o Devide
Uma cópia do Device para o Host
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Código do Kernel
O código do Kernel fica inalterado
#define N (1024*1024)
#define FULL_DATA_SIZE (N*20)
__global__ void kernel( int *a, int *b, int *c ) {
int idx = threadIdx.x + blockIdx.x * blockDim.x;
if (idx < N) {
int idx1 = (idx + 1) % 256;
int idx2 = (idx + 2) % 256;
float as = (a[idx] + a[idx1] + a[idx2]) / 3.0f;
float bs = (b[idx] + b[idx1] + b[idx2]) / 3.0f;
c[idx] = (as + bs) / 2;
}
}
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Código do Kernel
O código do Kernel fica inalterado
#define N (1024*1024)
#define FULL_DATA_SIZE (N*20)
__global__ void kernel( int *a, int *b, int *c ) {
int idx = threadIdx.x + blockIdx.x * blockDim.x;
if (idx < N) {
int idx1 = (idx + 1) % 256;
int idx2 = (idx + 2) % 256;
float as = (a[idx] + a[idx1] + a[idx2]) / 3.0f;
float bs = (b[idx] + b[idx1] + b[idx2]) / 3.0f;
c[idx] = (as + bs) / 2;
}
}
O próximo passo é verificar se exite suporte a sobreposição
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Verificando a Sobreposição
int main( void ) {
cudaDeviceProp prop;
int whichDevice;
HANDLE_ERROR( cudaGetDevice( &whichDevice ) );
HANDLE_ERROR( cudaGetDeviceProperties( &prop, whichDevice ) );
if (!prop.deviceOverlap) {
printf( "Device will not handle overlaps, so no "
"speed up from streams\n" );
return 0;
}
cudaEvent_t start, stop;
float elapsedTime;
// start the timers
HANDLE_ERROR( cudaEventCreate( &start ) );
HANDLE_ERROR( cudaEventCreate( &stop ) );
HANDLE_ERROR( cudaEventRecord( start, 0 ) );
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Criação dos Streams
O próximo passo é criar os dois streams da mesma forma
como foi criado um único stream
// initialize the streams
cudaStream_t stream0, stream1;
HANDLE_ERROR( cudaStreamCreate( &stream0 ) );
HANDLE_ERROR( cudaStreamCreate( &stream1 ) );
Ainda será assumido que se tem dois buffers de entrada e um
único buffer de saı́da.
Contudo, agora serão alocados dois conjuntos idênticos de
buffers na GPU, de forma que cada stream possa
independentemente trabalhar sobre os pedaços da entrada
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Alocações
int *host_a, *host_b, *host_c;
int *dev_a0, *dev_b0, *dev_c0; //GPU buffers for stream0
int *dev_a1, *dev_b1, *dev_c1; //GPU buffers for stream1
// allocate the memory on
HANDLE_ERROR( cudaMalloc(
HANDLE_ERROR( cudaMalloc(
HANDLE_ERROR( cudaMalloc(
HANDLE_ERROR( cudaMalloc(
HANDLE_ERROR( cudaMalloc(
HANDLE_ERROR( cudaMalloc(
the GPU
(void**)&dev_a0,N
(void**)&dev_b0,N
(void**)&dev_c0,N
(void**)&dev_a1,N
(void**)&dev_b1,N
(void**)&dev_c1,N
*
*
*
*
*
*
sizeof(int)
sizeof(int)
sizeof(int)
sizeof(int)
sizeof(int)
sizeof(int)
)
)
)
)
)
)
);
);
);
);
);
);
// allocate page-locked memory, used to stream
HANDLE_ERROR(cudaHostAlloc( (void**)&host_a,FULL_DATA_SIZE*sizeof(int),cudaHostAllocDefault ));
HANDLE_ERROR(cudaHostAlloc( (void**)&host_b,FULL_DATA_SIZE*sizeof(int),cudaHostAllocDefault ));
HANDLE_ERROR(cudaHostAlloc( (void**)&host_c,FULL_DATA_SIZE*sizeof(int),cudaHostAllocDefault ));
for (int i=0; i<FULL_DATA_SIZE; i++) {
host_a[i] = rand();
host_b[i] = rand();
}
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Próximos Passos...
Agora têm-se que correr o laço sobre os pedaços das entrada.
Porém, agora estamos utilizando dois streams
Logo se processa duas vezes mais dados por iteração do laço
No stream0 são enfileirados as cópias assı́ncronas dos buffers
de entrada a e b para a GPU, uma execução do kernel e uma
cópia de volta para o buffer de saı́da c
Após as operações no stream0, são enfileiradas operações
idênticas para o próximo pedaço de dados, mas agora no
stream1
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Stream0
// now loop over full data, in bite-sized chunks
for (int i=0; i<FULL_DATA_SIZE; i+= N*2) {
// copy the locked memory to the device, async
HANDLE_ERROR( cudaMemcpyAsync( dev_a0, host_a+i,
N * sizeof(int),
cudaMemcpyHostToDevice,
stream0 ) );
HANDLE_ERROR( cudaMemcpyAsync( dev_b0, host_b+i,
N * sizeof(int),
cudaMemcpyHostToDevice,
stream0 ) );
kernel<<<N/256,256,0,stream0>>>( dev_a0, dev_b0, dev_c0 );
// copy the data from device to locked memory
HANDLE_ERROR( cudaMemcpyAsync( host_c+i, dev_c0,
N * sizeof(int),
cudaMemcpyDeviceToHost,
stream0 ) );
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Stream1
// copy the locked memory to the device, async
HANDLE_ERROR( cudaMemcpyAsync( dev_a1, host_a+i+N,
N * sizeof(int),
cudaMemcpyHostToDevice,
stream1 ) );
HANDLE_ERROR( cudaMemcpyAsync( dev_b1, host_b+i+N,
N * sizeof(int),
cudaMemcpyHostToDevice,
stream1 ) );
kernel<<<N/256,256,0,stream1>>>( dev_a1, dev_b1, dev_c1 );
// copy the data from device to locked memory
HANDLE_ERROR( cudaMemcpyAsync( host_c+i+N, dev_c1,
N * sizeof(int),
cudaMemcpyDeviceToHost,
stream1 ) );
}
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Procedimentos
O laço for () procede alternando os streams
Após o término do laço for (), sincroniza-se a GPU com a
CPU antes de se parar o cronômetro
Desde que estamos trabalhando com dois streams, é necessário
sincronizar ambos,
HANDLE_ERROR( cudaStreamSynchronize( stream0 ) );
HANDLE_ERROR( cudaStreamSynchronize( stream1 ) );
Agora, para-se o cronômetro, mostra-se o tempo gasto e
desaloca-se as memórias
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Finalizando
HANDLE_ERROR( cudaEventRecord( stop, 0 ) );
HANDLE_ERROR( cudaEventSynchronize( stop ) );
HANDLE_ERROR( cudaEventElapsedTime( &elapsedTime,
start, stop ) );
printf( "Time taken: %3.1f ms\n", elapsedTime );
// cleanup the streams and memory
HANDLE_ERROR( cudaFreeHost( host_a ) );
HANDLE_ERROR( cudaFreeHost( host_b ) );
HANDLE_ERROR( cudaFreeHost( host_c ) );
HANDLE_ERROR( cudaFree( dev_a0 ) );
HANDLE_ERROR( cudaFree( dev_b0 ) );
HANDLE_ERROR( cudaFree( dev_c0 ) );
HANDLE_ERROR( cudaFree( dev_a1 ) );
HANDLE_ERROR( cudaFree( dev_b1 ) );
HANDLE_ERROR( cudaFree( dev_c1 ) );
HANDLE_ERROR( cudaStreamDestroy( stream0 ) );
HANDLE_ERROR( cudaStreamDestroy( stream1 ) );
return 0;
}
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Testes
Utilizando-se uma GeForce GTX 285, foi observado
Processo com uma única stream
Tempo gasto: 62 ms
Processo com dois streams
Tempo gasto: 61 ms
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Testes
Utilizando-se uma GeForce GTX 285, foi observado
Processo com uma única stream
Tempo gasto: 62 ms
Processo com dois streams
Tempo gasto: 61 ms
UH-oh!!!!!!!!!!!!!!!!!!!!!!!!!
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Testes
Utilizando-se uma GeForce GTX 285, foi observado
Processo com uma única stream
Tempo gasto: 62 ms
Processo com dois streams
Tempo gasto: 61 ms
UH-oh!!!!!!!!!!!!!!!!!!!!!!!!!
Calma.... é preciso entendermos mais a respeito dos streams e
como o drive e o hardware CUDA os encaram
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Agendamento de Trabalho na GPU
Como programadores, temos uma noç ao de Stream como
uma fila ordenada de invocações de processos.
Contudo o hardware CUDA não tem noção dos Streams, sendo
estes uma abstração!
O hardware tem um ou mais engenhos para desempenhar as
cópias de memória e um engenho para executar os kernels
Tais engenhos enfileiram comandos independentemente um
dos outros, resultando em um cenário do tipo agendamento de
trabalhos.
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Agendamento de Trabalhos
A figura abaixo mostra como as operações têm sido agendadas
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Abstração vs. Hardware
Assim, as noções do usuário programador e o hardware são
ortogonais
Cabe ao driver CUDA tentar agradar aos dois lados: o
programador e o hardware
Primeiramente, existe uma dependência importante na
especificação da ordem na qual os operadores são adicionados
no Stream
Por exemplo, a cópia da memória a deve ocorrer antes da
cópia da memória b que deve ocorrer antes da execução do
kernel!
Como como estas operações vão para engenhos diferentes do
hardware, esta dependência é perdida.
Este procedimento deve ser gerenciado pelo driver CUDA.
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
O Driver CUDA
O driver CUDA irá garantir a ordem de execução das tarefas,
e no caso fará,
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Linha do Tempo
Observando a linha do tempo dos processos!
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Otimizando o Processo
Observe que o programador tem que saber como o hardware
funciona para tentar tirar o maior proveito possı́vel.
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Otimizando o Processo
Observe que o programador tem que saber como o hardware
funciona para tentar tirar o maior proveito possı́vel.
Assim, seria possı́vel otimizar o processo de tal forma que a
linha do tempo ficasse,
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Código Otimizado
for (int i=0; i<FULL_DATA_SIZE; i+= N*2) {
// enqueue copies of a in stream0 and stream1
HANDLE_ERROR( cudaMemcpyAsync( dev_a0, host_a+i,N * sizeof(int),cudaMemcpyHostToDevice,
stream0 ) );
HANDLE_ERROR( cudaMemcpyAsync( dev_a1, host_a+i+N,N * sizeof(int),cudaMemcpyHostToDevice,
stream1 ) );
// enqueue copies of b in stream0 and stream1
HANDLE_ERROR( cudaMemcpyAsync( dev_b0, host_b+i,N * sizeof(int),cudaMemcpyHostToDevice,
stream0 ) );
HANDLE_ERROR( cudaMemcpyAsync( dev_b1, host_b+i+N,N * sizeof(int),cudaMemcpyHostToDevice,
stream1 ) );
// enqueue kernels in stream0 and stream1
kernel<<<N/256,256,0,stream0>>>( dev_a0, dev_b0, dev_c0 );
kernel<<<N/256,256,0,stream1>>>( dev_a1, dev_b1, dev_c1 );
// enqueue copies of c from device to locked memory
HANDLE_ERROR( cudaMemcpyAsync( host_c+i, dev_c0,N * sizeof(int),cudaMemcpyDeviceToHost,
stream0 ) );
HANDLE_ERROR( cudaMemcpyAsync( host_c+i+N, dev_c1,N * sizeof(int),cudaMemcpyDeviceToHost,
stream1 ) );
}
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Teste
Com esta nova implementação o tempo gasto caio para 48 ms.
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Exercı́cio
Implemente o problema apresentado com dois stream, tanto
na versão naive como na versão otmizada
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU
Introdução
Múltiplos Stream
Agendamento de Trabalho
Referências
Bibliografia
Jason Sandres and Edward Kandrot. CUDA by exemple. An
Introduction to General-Purpose GPU Programming.
Addison-Wesley, 2011.
Capı́tulo 10
Prof. Tiago A. E. Ferreira
Disciplina de TICs 1 - Introdução a Programação em GPGPU