Aula 10 - Streams (Parte I)



Documentos relacionados
Arquitetura e Programação de GPU. Leandro Zanotto RA: Anselmo Ferreira RA: Marcelo Matsumoto RA:

Sistemas Operacionais

Processos e Threads (partes I e II)

SISTEMAS OPERACIONAIS CAPÍTULO 3 CONCORRÊNCIA

Estruturas do Sistema de Computação

Processamento de Alto Desempenho utilizando Unidade de Processamento Gráfico - GPU

Arquitetura de Computadores II

Experimentos com a memória cache do CPU

Busca. Pesquisa sequencial

Programação Paralela e Distribuída (DCC/UFRJ)

A memória é um recurso fundamental e de extrema importância para a operação de qualquer Sistema Computacional; A memória trata-se de uma grande

Notas da Aula 17 - Fundamentos de Sistemas Operacionais

Fundamentos de Sistemas Operacionais

Procedimentos para Reinstalação do Sisloc

Estruturas de Armazenamento e Indexação. Rafael Lage Moreira Barbosa

Introdução a CUDA. Esteban Walter Gonzalez Clua. Medialab - Instituto de Computação Universidade Federal Fluminense NVIDIA CUDA Research Center START

Procedimentos para Instalação do SISLOC

Procedimentos para Instalação do Sisloc

Orientação a Objetos

Programação em Paralelo. N. Cardoso & P. Bicudo. Física Computacional - MEFT 2012/2013

BUSCA EM LISTAS LISTAS SEQÜENCIAIS, LISTAS SIMPLESMENTE E DUPLAMENTE ENCADEADAS E LISTAS CIRCULARES

Crash recovery é similar ao instance recovery, onde o primeiro referencia ambientes de instância exclusiva e o segundo ambientes parallel server.

SISTEMAS DISTRIBUÍDOS

IFPE. Disciplina: Sistemas Operacionais. Prof. Anderson Luiz Moreira

Aula 3. Sistemas Operacionais. Prof: Carlos Eduardo de Carvalho Dantas

Introdução a Programação. Ponteiros e Strings, Alocação Dinâmica

DAS5102 Fundamentos da Estrutura da Informação

Disciplina: Sistemas Operacionais - CAFW-UFSM Professor: Roberto Franciscatto

CAPÍTULO 2 CARACTERÍSTICAS DE E/S E PORTA PARALELA

Resumo da Matéria de Linguagem de Programação. Linguagem C

Arquitetura de Computadores. Sistemas Operacionais IV

Sistemas Distribuídos

MANUAL DO GERENCIADOR ESCOLAR WEB

discos impressora CPU memória AULA 04 - Estruturas de Sistemas Computacionais Operação dos sistemas de computação Controlador de disco

MANUAL DE INSTALAÇÃO 1) ORACLE VIRTUALBOX ; 2) MICROSOFT WINDOWS ; 3) SUMÁRIOS GENEPLUS.

Integração de Sistemas Embebidos MECom :: 5º ano

Comparativo de desempenho do Pervasive PSQL v11

Gerência de Entrada/Saída

*O RDBMS Oracle é um sistema de gerenciamento de banco de dados relacional.

Algoritmos de Busca em Tabelas

Sistemas Operacionais Gerência de Dispositivos

Software. Gerenciamento de Manutenção

Backup. Permitir a recuperação de sistemas de arquivo inteiros de uma só vez. Backup é somente uma cópia idêntica de todos os dados do computador?

UNIVERSIDADE FEDERAL DO RIO GRANDE DO SUL INSTITUTO DE INFORMÁTICA INFORMÁTICA APLICADA

Atualização De Mapas GPS Apontador. 1º Acessar site: 2º Selecione o Idioma para Português no seu canto direito.

EAD Árvore árvore binária

PAINEL GERENCIADOR DE S

Consultório On-line. Tudo o que você precisa em um só lugar.

Ministério da Educação Secretaria de Educação Profissional e Tecnológica Instituto Federal de Educação, Ciência e Tecnologia do Rio Grande do Sul

Sistemas Operativos. Threads. 3º ano - ESI e IGE (2011/2012) Engenheiro Anilton Silva Fernandes (afernandes@unipiaget.cv)

Organização de Arquivos

Uso do Netkit no Ensino de Roteamento Estático

Manual de digitação de contas Portal AFPERGS

Integração ADMRH com AGROSYS

ENGENHARIA DE SOFTWARE I

30 ANOS DE SOCIALISMO

Ajuda do Sistema Aquarius.

1 REQUISITOS BÁSICOS PARA INSTALAR O SMS PC REMOTO

Organização e Arquitetura de Computadores

Diminui o gargalo existente entre processador e memória principal; 5 a 10 vezes mais rápidas que a memória principal; Ligada diretamente à MP;

GUIA RÁPIDO SISTEMA ANTIFURTO THEFT DETERRENT

Java. Marcio de Carvalho Victorino

DIFERENÇAS ENTRE FUNÇÃO E BLOCO FUNCIONAL; CRIAÇÃO DE FUNÇÃO / BLOCO FUNCIONAL; UTILIZAÇÃO NO LADDER; EXEMPLO DE BLOCO FUNCIONAL;

Sistemas Operacionais

Organização e Arquitetura de Computadores

Visão Geral de Sistemas Operacionais

5 Entrada e Saída de Dados:

Transferência de Dados entre Computadores

Guia. PDA e SmartPhones. Windows Mobile, Pocket PC e CE.

José Romildo Malaquias

Aula 06 - Funções. O que é uma Função - Comando return - Protótipos de Funções - Tipos de Funções - Escopo de Variáveis - Passagem de parâmetros

Notas da Aula 15 - Fundamentos de Sistemas Operacionais

Sistemas Operacionais

Na medida em que se cria um produto, o sistema de software, que será usado e mantido, nos aproximamos da engenharia.

Usar o Office 365 no iphone ou ipad

Escalonamento no Linux e no Windows NT/2000/XP

Manual Administrador - Mídia System

Prof. Yandre Maldonado - 1 PONTEIROS. Prof. Yandre Maldonado e Gomes da Costa

Guia de instalação UEG Linux LTS

Manual do usuário. Mobile Auto Download

Dadas a base e a altura de um triangulo, determinar sua área.

Na disciplina de Cálculo Numérico, vamos trabalhar com a linguagem C++ e o compilador que vamos usar é o Dev C++.

MANUAL DO USUÁRIO SORE Sistema Online de Reservas de Equipamento. Toledo PR. Versão Atualização 26/01/2009 Depto de TI - FASUL Página 1

Sistemas Operacionais

A Camada de Transporte

- Configuração de Ambiente para Aplicação de Patch

ISO/IEC 12207: Gerência de Configuração

THREADS EM JAVA. George Gomes Cabral

Comandos Sequenciais if else, e Switch

Persistência de Dados

Escritório Virtual Administrativo

Transcrição:

Disciplina de TICs 1 - Introdução a Programação em GPGPU Aula 10 - Streams (Parte I)

Introdução Até então foi visto como engenho de processamento paralelo massivo de dados nas GPUs pode aumentar assombrosamente o ganho em tempo quando comparado com as CPUs Contudo ainda há outro tipo de paralelismo a ser explorado Esta outra forma é semelhante as tarefas multithreads nas aplicações em CPU Ao invés de realizar a mesma função sobre vários dados de forma paralela, esta nova forma de paralelismo é realizar duas ou mais tarefas completamente diferentes ao mesmo tempo. Neste contexto, uma tarefa poderia ser qualquer número de coisas Exemplo: uma aplicação que uma thread redesenha a GUI e uma segunda thread realiza um download de uma atualização sobre a rede.

Alocações de Memória Como já visto até então, quando é desejado alocar memória, Memória na GPU: utiliza-se a função cudamalloc() Memória no Host: utiliza-se a função malloc()

Alocações de Memória Como já visto até então, quando é desejado alocar memória, Memória na GPU: utiliza-se a função cudamalloc() Memória no Host: utiliza-se a função malloc() Contudo ainda há uma outra forma de alocar memória no device, O runtime CUDA oferece um mecanismo de alocação de memória no Host, utilizando-se a função cudahostalloc()

Alocações de Memória Como já visto até então, quando é desejado alocar memória, Memória na GPU: utiliza-se a função cudamalloc() Memória no Host: utiliza-se a função malloc() Contudo ainda há uma outra forma de alocar memória no device, O runtime CUDA oferece um mecanismo de alocação de memória no Host, utilizando-se a função cudahostalloc() Contudo, porque utiliza a função cudaalloc() ao invés de malloc()?

Alocações de Memória Na realidade há uma diferença entra a memória alocada pela função Malloc() e a função cudahostalloc() A função Malocc() alocará uma memória padrão, uma memória paginável no host A função cudahostalloc() irá alocar uma memória com página travada (memória presa)

Alocações de Memória Na realidade há uma diferença entra a memória alocada pela função Malloc() e a função cudahostalloc() A função Malocc() alocará uma memória padrão, uma memória paginável no host A função cudahostalloc() irá alocar uma memória com página travada (memória presa) Um buffer com página travada tem uma importante propriedade: o S.O. nunca irá paginar para o disco uma memória presa, garantindo sua permanência na memória. Assim, um corolário é que este se transforma em uma região segura de memória para o S.O. permitir o acesso de uma aplicação ao endereço físico da memória.

DMA - Acesso Direto a Memória Uma vez que o endereço físico (real) é conhecido, a GPU pode utilizá-lo para acessar diretamente a memória (DMA) para copiar dados para ou a partir do host.

DMA - Acesso Direto a Memória Uma vez que o endereço físico (real) é conhecido, a GPU pode utilizá-lo para acessar diretamente a memória (DMA) para copiar dados para ou a partir do host. Com o Acesso Direto da Memória, a cópia DMA procede sem a intervenção da CPU. Caso a memória acessada diretamente não foce preza, significaria que a CPU estaria podendo realizar um paginamento no endereço que a GPU estaria acessando. Observe que toda vez que se deseja realizar uma cópia de memória paginável, o CUDA está utilizando uma cópia DMA. Assim, esta cópia ocorre em duas etapas: primeiro a partir de um buffer paginável um buffer da página travada é criado, e depois deste último buffer é transferido para a GPU.

Uso de Memória com Página Travada Contudo, deve-se resistir a tentação de se trocar todos os malloc()s por cudahostalloc() Utilizar uma memória preza é uma faca de dois gumes! A paginação de memória não foi desenvolvida a toa! A memória virtual é de suma importância para a grande maioria das aplicações poderem rodar em uma configuração mínima de memória bem abaixo do espaço que esta mesma aplicação necessitaria para rodar sem a memória virtual ou paginação de memória.

Uso de Memória com Página Travada Contudo, deve-se resistir a tentação de se trocar todos os malloc()s por cudahostalloc() Utilizar uma memória preza é uma faca de dois gumes! A paginação de memória não foi desenvolvida a toa! A memória virtual é de suma importância para a grande maioria das aplicações poderem rodar em uma configuração mínima de memória bem abaixo do espaço que esta mesma aplicação necessitaria para rodar sem a memória virtual ou paginação de memória. Outro ponto é que deve existir memória para garantir todos os buffers de página travada, visto que estes não sofrerão swap como disco.

Uso de Memória com Página Travada Contudo, deve-se resistir a tentação de se trocar todos os malloc()s por cudahostalloc() Utilizar uma memória preza é uma faca de dois gumes! A paginação de memória não foi desenvolvida a toa! A memória virtual é de suma importância para a grande maioria das aplicações poderem rodar em uma configuração mínima de memória bem abaixo do espaço que esta mesma aplicação necessitaria para rodar sem a memória virtual ou paginação de memória. Outro ponto é que deve existir memória para garantir todos os buffers de página travada, visto que estes não sofrerão swap como disco. Assim, o uso de memória preza implica em um alto consumo efetivo de memória, podendo influenciar no desempenho de outras aplicações.

Uso de Memória com Página Travada De forma geral, o uso de memória com paginamento travado é muito mais raro do que os outros acessos de memória já estudados. Contudo, vejamos um exemplo como uso de memória com página travada. O exemplo é muito simples e serve primariamente como um benckmark para o desempenho da função cudamemcpy() com ambas as memórias pagináveis e não pagináveis. A ideia é alocar um buffer na GPU e um na CPU de mesmo tamanho, e então executar uma certa quantidade de cópias entre estes dois buffers Será permitido ao usuário definir a direção destas cópias, se up (do host para o device) ou down (do device para o host) Para se obter uma cronometragem acurada, serão executados os eventos CUDA para o início e o fim da sequência de cópias

Exemplo Introdução float cuda_malloc_test( int size, bool up ) { cudaevent_t start, stop; int *a, *dev_a; float elapsedtime; HANDLE_ERROR( cudaeventcreate( &start ) ); HANDLE_ERROR( cudaeventcreate( &stop ) ); a = (int*)malloc( size * sizeof( *a ) ); HANDLE_NULL( a ); HANDLE_ERROR( cudamalloc( (void**)&dev_a, size * sizeof( *dev_a ) ) );

Exemplo Introdução HANDLE_ERROR( cudaeventrecord( start, 0 ) ); for (int i=0; i<100; i++) { if (up) HANDLE_ERROR( cudamemcpy( dev_a, a, size * sizeof( *dev_a ), cudamemcpyhosttodevice ) ); else HANDLE_ERROR( cudamemcpy( a, dev_a, size * sizeof( *dev_a ), cudamemcpydevicetohost ) ); } HANDLE_ERROR( cudaeventrecord( stop, 0 ) ); HANDLE_ERROR( cudaeventsynchronize( stop ) ); HANDLE_ERROR( cudaeventelapsedtime( &elapsedtime, start, stop) );

Exemplo Introdução free( a ); HANDLE_ERROR( cudafree( dev_a ) ); HANDLE_ERROR( cudaeventdestroy( start ) ); HANDLE_ERROR( cudaeventdestroy( stop ) ); } return elapsedtime;

Exemplo Introdução No exemplo dado, independente da direção da cópia, começa-se com a alocação de um buffer no host e na GPU de tamanho inteiro size. São geradas 100 cópias na direção especificada pelo argumento up, parando o tempo depois do término do processo das 100 cópias. E por fim as memórias do host e GPU são liberadas.

Exemplo Introdução No exemplo dado, independente da direção da cópia, começa-se com a alocação de um buffer no host e na GPU de tamanho inteiro size. São geradas 100 cópias na direção especificada pelo argumento up, parando o tempo depois do término do processo das 100 cópias. E por fim as memórias do host e GPU são liberadas. Mas o ponto importante da função cuda malloc test() é que esta aloca memória paginável, com o uso da função malloc()

Exemplo - Page-Locked float cuda_host_alloc_test( int size, bool up ) { cudaevent_t start, stop; int *a, *dev_a; float elapsedtime; HANDLE_ERROR( cudaeventcreate( &start ) ); HANDLE_ERROR( cudaeventcreate( &stop ) ); HANDLE_ERROR( cudahostalloc( (void**)&a, size * sizeof( *a ), cudahostallocdefault ) ); HANDLE_ERROR( cudamalloc( (void**)&dev_a, size * sizeof( *dev_a ) ) );

Exemplo - Page-Locked HANDLE_ERROR( cudaeventrecord( start, 0 ) ); for (int i=0; i<100; i++) { if (up) HANDLE_ERROR( cudamemcpy( dev_a, a, size * sizeof( *a ), cudamemcpyhosttodevice ) ); else HANDLE_ERROR( cudamemcpy( a, dev_a, size * sizeof( *a ), cudamemcpydevicetohost ) ); } HANDLE_ERROR( cudaeventrecord( stop, 0 ) ); HANDLE_ERROR( cudaeventsynchronize( stop ) ); HANDLE_ERROR( cudaeventelapsedtime( &elapsedtime, start, stop ) );

Exemplo - Page-Locked HANDLE_ERROR( cudafreehost( a ) ); HANDLE_ERROR( cudafree( dev_a ) ); HANDLE_ERROR( cudaeventdestroy( start ) ); HANDLE_ERROR( cudaeventdestroy( stop ) ); } return elapsedtime;

Exemplo - Page-Locked Neste segundo exemplo, é utilizada a alocação de uma memória preza. O buffer alocado pela função cudahostalloc() é usado da mesma forma que o buffer alocado poe malloc() Um ponto de diferença é o argumento cudahostallocdefault utilizado na função cudahostalloc() Este argumento pode receber uma coleção de flags que podem ser utilizadas para modificar o comportamento da função cudahostalloc() de forma a alocar outras variáveis da memória preza do host No próximo capítulo serão abordadas outras possibilidades para este argumento. Aqui esta se querendo uma memória não paginável padrão, assim se utiliza cudahostallocdefault Para desalocar um buffer alocado com cudahostalloc() utiliza-se cudafreehost()

Testes Introdução Utilizando-se uma GeForce GTX 285, foi observado Cópia do Hosto para o Device Memória paginável: 2.77GB/s Memória não paginável: 5.11GB/s Cópia do Device para o Hosto Memória paginável: 2.43GB/s Memória não paginável: 5.46GB/s

Streams no CUDA Introdução Um Stream CUDA Simples Já foi introduzida a ideia de eventos em CUDA, contudo ainda não foi discutido a função do segundo argumento da função cudaeventrecird() Este segundo argumento especifica o stream ao qual está sendo inserido o evento. cudaevent_t start; cudaeventcreate(&start); cudaeventrecord( start, 0 ); Um stream CUDA representa uma fila de operações de GPU a serem executadas em uma ordem específica É possível adicionar operações em um stream tais como, Lancamentos de kernels Cópia de Memória Início e finalização de eventos

Stream CUDA Introdução Um Stream CUDA Simples O verdadeiro poder do uso dos streams torna-se aparente quando se utiliza mais de um deles por vez, mas verificaremos o uso de um stream inicialmente.

Stream CUDA Introdução Um Stream CUDA Simples O verdadeiro poder do uso dos streams torna-se aparente quando se utiliza mais de um deles por vez, mas verificaremos o uso de um stream inicialmente. Imagine um kernal CUDA tomando dois buffers de entrada, a e b Será realizada alguma computação com a e b, gerando o buffer de saída c (média de três observações) #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; } }

Stream CUDA Introdução Um Stream CUDA Simples Seja o código main() 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; } Aqui, o primeiro passa é escolher um device e verificar se este suporta uma característica conhecida como device overlap A característica device overlap permite a GPU simultaneamente executar um kernel CUDA C e uma cópia de memória entre o Device e o Host

Stream CUDA Introdução Um Stream CUDA Simples Vamos começar criando e inicializando um evento timer, cudaevent_t start, stop; float elapsedtime; // start the timers HANDLE_ERROR( cudaeventcreate( &start ) ); HANDLE_ERROR( cudaeventcreate( &stop ) ); HANDLE_ERROR( cudaeventrecord( start, 0 ) );

Stream CUDA Introdução Um Stream CUDA Simples Vamos começar criando e inicializando um evento timer, cudaevent_t start, stop; float elapsedtime; // start the timers HANDLE_ERROR( cudaeventcreate( &start ) ); HANDLE_ERROR( cudaeventcreate( &stop ) ); HANDLE_ERROR( cudaeventrecord( start, 0 ) ); Depois de inicializar o timer, cria-se o stream, // initialize the stream cudastream_t stream; HANDLE_ERROR( cudastreamcreate( &stream ) );

Stream CUDA Alocação de Dados Um Stream CUDA Simples int *host_a, *host_b, *host_c; int *dev_a, *dev_b, *dev_c; // allocate the memory on the GPU HANDLE_ERROR( cudamalloc( (void**)&dev_a, N * sizeof(int) ) ); HANDLE_ERROR( cudamalloc( (void**)&dev_b, N * sizeof(int) ) ); HANDLE_ERROR( cudamalloc( (void**)&dev_c, N * 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(); }

Stream CUDA Introdução Um Stream CUDA Simples Foram alocados as entradas e saídas tanto da GPU com no Host. Observe que foi escolhido utilizar a memória não paginável na Host, utilizando-se a função cudahostalloc() para realizar as alocações Há mais do que realizar cópias mais rápido para o uso de memória não paginável. Estará sendo utilizada um novo tipo da função cudamemcpy(), e esta requer memória não paginável.

Stream CUDA Introdução Um Stream CUDA Simples Foram alocados as entradas e saídas tanto da GPU com no Host. Observe que foi escolhido utilizar a memória não paginável na Host, utilizando-se a função cudahostalloc() para realizar as alocações Há mais do que realizar cópias mais rápido para o uso de memória não paginável. Estará sendo utilizada um novo tipo da função cudamemcpy(), e esta requer memória não paginável. Depois da alocação das entradas, as alocações do host são preenchidas com números aleatórios

Stream CUDA Introdução Um Stream CUDA Simples Foram alocados as entradas e saídas tanto da GPU com no Host. Observe que foi escolhido utilizar a memória não paginável na Host, utilizando-se a função cudahostalloc() para realizar as alocações Há mais do que realizar cópias mais rápido para o uso de memória não paginável. Estará sendo utilizada um novo tipo da função cudamemcpy(), e esta requer memória não paginável. Depois da alocação das entradas, as alocações do host são preenchidas com números aleatórios Com o stream, os eventos de tempo, os buffers do host e device alocados, estamos prontos para desempenhar as computações O procedimento geral ainda é copia-se os buffers de entrada para a GPU, levanta-se um kernel e copia-se um buffer de saída de volta para o host

Stream CUDA Introdução Um Stream CUDA Simples Serão tomadas algumas pequenas alterações do procedimento genérico, Os buffers de entrada não serão copiados em sua integridade para a GPU As entradas serão divididos em pedaços, e cada pedaço será aplicado um processo em três etapas Dada uma fração do buffer de entrada, copia-se para a GPU e se executa o kernel sobre a fração do buffer, copiando-se de volta para o host a fração do buffer de saída gerado. Imagine que este procedimento é utilizado por que o buffer do host simplesmente não cabe na memória da GPU No próximo slide será apresentado o código para desempenhar a sequência de computação

Stream CUDA Introdução Um Stream CUDA Simples // now loop over full data, in bite-sized chunks for (int i=0; i<full_data_size; i+= N) { // copy the locked memory to the device, async HANDLE_ERROR( cudamemcpyasync(dev_a,host_a+i,n*sizeof(int), cudamemcpyhosttodevice, stream ) ); HANDLE_ERROR( cudamemcpyasync(dev_b,host_b+i,n*sizeof(int), cudamemcpyhosttodevice, stream ) ); kernel<<<n/256,256,0,stream>>>( dev_a, dev_b, dev_c ); } // copy the data from device to locked memory HANDLE_ERROR( cudamemcpyasync(host_c+i,dev_c,n*sizeof(int), cudamemcpydevicetohost, stream ) );

Modificações Utilizadas Um Stream CUDA Simples Ao invés de se utilizar a função cudamemcpy() foi utilizada a função cudamemcpyasync() A diferença entre as duas funções é sutil, porém significante. A função cudamemcpy() se comporta da mesma forma que memcpy() do C padrão Esta função funciona de forma síncrona, significando que quando a função retorna um valor, a cópia tem sido completada.

Modificações Utilizadas Um Stream CUDA Simples Ao invés de se utilizar a função cudamemcpy() foi utilizada a função cudamemcpyasync() A diferença entre as duas funções é sutil, porém significante. A função cudamemcpy() se comporta da mesma forma que memcpy() do C padrão Esta função funciona de forma síncrona, significando que quando a função retorna um valor, a cópia tem sido completada. Em oposição a uma função síncrona existe uma função assíncrona. No caso, a função cudamemcpyasync()

Modificações Utilizadas Um Stream CUDA Simples A chamada da função cudamemcpyasync() simplesmente recoloca o pedido de cópia de memória em um stream especificado pelo argumento stream Quando a chamada da função cudamemcpyasync() retorna, não há nenhuma garantia que a cópia tenha sequer inicializado, muito menos tenha sido finalizada. A única garantia é que a cópia será finalizada antes que a próxima operação localizada no mesmo stream É requerido que qualquer ponteiro da memória Host passado para a função cudamemcpyasync() tenha sido alocado por cudahostalloc() Ou seja, todo agendamento de cópia assíncrona só é possível de ou para uma região de memória não paginável.

Modificações Utilizadas Um Stream CUDA Simples Observe também que o lançamento do kernel também foi modificado Agora há um maior número de argumentos sendo passados para o kernel. O lançamento deste kernel também é assíncrono. Tecnicamente, é possível concluir uma iteração do laço for sem ter começado qualquer alocação de memória e/ou execução do kernel A única garantia é que a primeira cópia lançada no stream será executada antes da segunda cópia. A segunda cópia será realizada antes do ínicio do kernel, e o kernel irá ser finalizado antes da terceira cópia

Modificações Utilizadas Um Stream CUDA Simples Observe também que o lançamento do kernel também foi modificado Agora há um maior número de argumentos sendo passados para o kernel. O lançamento deste kernel também é assíncrono. Tecnicamente, é possível concluir uma iteração do laço for sem ter começado qualquer alocação de memória e/ou execução do kernel A única garantia é que a primeira cópia lançada no stream será executada antes da segunda cópia. A segunda cópia será realizada antes do ínicio do kernel, e o kernel irá ser finalizado antes da terceira cópia O stream funciona como uma lista ordenada de trabalhos a serem realizados na GPU.

Modificações Utilizadas Um Stream CUDA Simples Observe que depois do lação for() ter sido concluído, ainda há a possibilidade da GPU ainda está processando.

Modificações Utilizadas Um Stream CUDA Simples Observe que depois do lação for() ter sido concluído, ainda há a possibilidade da GPU ainda está processando. Caso haja o desejo de que a CPU espera a computação da GPU, há a necessidade de se realizar uma sincronização. Este sincronização pode ser realizada por meio da função cudastreamsynchronaze() e o stream que se deseja esperar, // copy result chunk from locked to full buffer HANDLE_ERROR( cudastreamsynchronize( stream ) );

Modificações Utilizadas Um Stream CUDA Simples Observe que depois do lação for() ter sido concluído, ainda há a possibilidade da GPU ainda está processando. Caso haja o desejo de que a CPU espera a computação da GPU, há a necessidade de se realizar uma sincronização. Este sincronização pode ser realizada por meio da função cudastreamsynchronaze() e o stream que se deseja esperar, // copy result chunk from locked to full buffer HANDLE_ERROR( cudastreamsynchronize( stream ) ); Uma vez as computações e cópias tenham sido completadas depois da sincronização da stream como o host, falta para o timer, coletar os dados e liberar a memória.

o Restante do Código Um Stream CUDA Simples 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_a ) ); HANDLE_ERROR( cudafree( dev_b ) ); HANDLE_ERROR( cudafree( dev_c ) );

Restante do Código Um Stream CUDA Simples E por fim, antes de sair da aplicação, é necessário destruir o stream, HANDLE_ERROR( cudastreamdestroy( stream ) ); } return 0;

Exercício Introdução Um Stream CUDA Simples Implemente os códigos apresentados, realizando as comparações de desempenho.

Bibliografia Jason Sandres and Edward Kandrot. CUDA by exemple. An Introduction to General-Purpose GPU Programming. Addison-Wesley, 2011. Capítulo 10