Arquivo

Arquivo para a categoria ‘cookbook’

Comparação GPU x sistemas paralelos

15 15UTC dezembro 15UTC 2009 Labaki Deixe um comentário

Monografia de conclusão de curso de A. V. Grammelsbacher e J. C. C. Medrado.

O trabalho visa fornecer um estudo comparando a execução de algoritmos paralelos na CPU e na GPU, elucidando se é mais vantajoso executar determinados algoritmos na CPU ou na GPU. Para realizar as comparações são feitos benchmarks da execução de algoritmos com a finalidade de medir a capacidade de processamento em FLOPS , a cópia de dados na memória entre outras características do sistema.

Comparacao de desempenho entre GPGPU e Sistemas Paralelos

Categoriasacadêmico, cookbook

Medição de tempo

2 02UTC outubro 02UTC 2009 Labaki Deixe um comentário

Freqüentemente precisamos medir o tempo de execução de um programa, especialmente quando queremos mostrar que um programa em GPU é mais rápido que um em CPU. A maioria das funções que a linguagem C oferece não servem a esse propósito, já que medem o tempo em segundos, e um programa para GPU raramente completa um segundo de execução. As bibliotecas CUDA oferecem um recurso simples para fazer essa medição, em unidades de milissegundos. Eis um exemplo de código:

# include <stdlib.h>

# include <stdio.h>

# include <cutil.h>

int main()

{

float tempo;

unsigned int timer = 0;

CUT_SAFE_CALL( cutCreateTimer( &timer));

CUT_SAFE_CALL( cutStartTimer( timer));

// Região de execução: aqui vai o trecho de código cujo

// tempo de execução queremos medir.

tempo = CUT_SAFE_CALL( cutGetTimerValue(timer));

printf(“A execução durou %5.3f milissegundos\n”, tempo);

CUT_SAFE_CALL(cutDeleteTimer(timer));

}

Parte ou todo esse roteiro não funcionou pra você? Deixe comentário!

Categoriascookbook, dica

Cuidados na medição de tempo

2 02UTC outubro 02UTC 2009 Labaki Deixe um comentário

Adaptado da mensagem enviada por Daniel Medina à lista GPUBrasil.

Pessoal, já faz algum tempo que estou para reviver este tópico e sempre esqueço. Quero compartilhar com vocês um detalhe que me consumiu alguns dias quebrando a cabeça em cima disso.

Ao implementar em meu programa o algoritmo sugerido pelo Labaki para realizar a análise do tempo de execução, notei que o desempenho da GPU estava muito ruim comparado ao da CPU (com volume pequeno de dados estava até maior). Após procurar muito a causa, descobri que o vilão da história é a API do CUDA. Esta camada de software não é inicializada durante a inicialização do programa principal. Ela só é instanciada no momento da primeira chamada a alguma função da API (no meu caso ocorria quando eu chamava a função cuda_malloc pela primeira vez).

# include <stdlib.h>

# include <stdio.h>

# include <cutil.h>

int main()

{

float tempo;

unsigned int timer = 0;

CUT_SAFE_CALL( cutCreateTimer( &timer));

CUT_SAFE_CALL( cutStartTimer( timer));

// Primeiro comando para a GPU (cudaMalloc, por exemplo).

// …

// Região de execução: aqui vai o trecho de código cujo

// tempo de execução queremos medir.

tempo = CUT_SAFE_CALL( cutGetTimerValue(timer));

printf(“A execução durou %5.3f milissegundos\n”, tempo);

CUT_SAFE_CALL(cutDeleteTimer(timer));

}

Ao receber a primeira instrução, a API se inicializa e isto leva um tempo considerável. Empiricamente levantei que esse tempo é de aproximadamente 40ms em uma GTX  280 e 70ms em uma GT 8800.

Para aplicações que duram dias ou semanas, este tempo não chega a ser um problema, mas para aplicações como a minha, cujo tempo de execução é da ordem de milissegundos, este delay é algo altamente indesejável.

Como solução, eu coloquei uma instrução “dummy” (malloc de 0 bytes) no início do algoritmo só para inicializar a placa, e desconsiderei este tempo do cálculo utilizado para comparação entre CPU e GPU. Por ser um custo de tempo intrínseco e que acontece somente uma vez em toda a execução do software, ele pode ser desconsiderado do benchmark. Veja um exemplo.


# include <stdlib.h>

# include <stdio.h>

# include <cutil.h>

int main()

{

float tempo;

unsigned int timer = 0;

CUT_SAFE_CALL( cutCreateTimer( &timer));

// Um comando dummy, sem efeito no programa, só para

// inicializar a placa. Esse passa a ser então o primeiro

// comando de GPU do programa.

float* dummy;

CUDA_SAFE_CALL( cudaMalloc((void**) &dummy, sizeof(float)));

// Só então o cronômetro é ligado:

CUT_SAFE_CALL( cutStartTimer( timer));

// Região de execução: aqui vai o trecho de código cujo

// tempo de execução queremos medir.

tempo = CUT_SAFE_CALL( cutGetTimerValue(timer));

printf(“A execução durou %5.3f milissegundos\n”, tempo);

CUT_SAFE_CALL(cutDeleteTimer(timer));

}

Parte ou todo esse roteiro não funcionou pra você? Deixe comentário!

Categoriascookbook, dica