Computação de Alto Desempenho
Capítulo 4 CUDA-MMP
4.2 Ambiente Computacional
4.2.1 Recolha de Resultados
Os resultados apresentados ao longo deste documento são obtidos efetuando uma média arit- mética de 5 execuções por forma a minimizar o impacto de desvios que possam existir pon- tualmente. Todos os testes foram executados sobre um conjunto de parâmetros referência. O valor de referência do lambda foi de 10, sendo este um valor onde a perda de qualidade não é detetável a olho nu, como se pode depreender da comparação entre as imagens da Figura 4.1 (original) e da Figura 4.2 (descompactada após compressão MMP). O número máximo de
blocos do dicionário foi fixado nos 1024 elementos por nível. Nos testes, todos os modos de predição estão ativos e a segmentação da predição é efetuada até ao nível 8.
Figura 4.1: Imagem Lena (original)
Figura 4.2: Imagem Lena (codificada com lambda 10)
A imagem de referência na recolha dos tempos foi a imagem Lena apresentada na Figura 4.1.
4.3
Profiling
Para verificar se existiria alguma vantagem na migração do algoritmo MMP sequencial para uma implementação paralela, o processo de migração começou por uma fase de estudo. Este começou pelo profiling do algoritmo MMP-Intra sequencial com o objetivo de detetar zonas
de grandes consumos computacionais. Para isso foi utilizada uma ferramenta de profiling que recorrem a métodos de sampling, o GProf [60].
Função Taxa de poder computacional utilizado (%)
quad_err 76.3310 optimize_block 11.6854 least_sq 10.8378 optimize_block_and_pred_mode 3.4878 getClosestPixels 2.0842 rate 1.8398 rate_dicseg 1.0820
Tabela 4.5: Distribuição do poder computacional das funções com maior consumo A Tabela 4.5 mostra a distribuição de poder computacional utilizado de entre as funções com um maior consumo. Através desta análise foram identificadas três funções críticas que se distinguiam das restantes com um consumo de poder computacional bastante superior: (1) a função quad_err, (2) a função optimize_block e (3) a função least_sq.
• quad_err: A função quad_err é responsável pelo cálculo da distorção entre dois blocos. A sua complexidade origina apenas do cálculo dos erros quadráticos entre os píxeis de ambos os blocos.
• optimize_block: A função optimize_block é responsável pela construção da árvore de segmentação de resíduo. Para cada bloco de resíduo gerado, o algoritmo efe- tua uma chamada à função optimize_block. Esta por sua vez efetua um número de chamadas à função quad_err igual ao número de blocos existentes no dicionário que sejam do mesmo nível do bloco de resíduo recebido como parâmetro por forma a calcular a distorção entre ambos. Entre cada chamada à função quad_err, a fun- ção optimize_block calcula o custo de representar o bloco de resíduo utilizando o bloco do dicionário enviado para a função quad_err, guardando sempre o bloco com menor custo.
Com a pesquisa terminada para todos os padrões do dicionário, a função optimize_block verifica se é possível segmentar o bloco de resíduo em uma das direções (vertical ou horizontal). Caso o bloco possa ser segmentado, a função é invocada recursivamente para cada um dos blocos originados da sua segmentação. Uma vez finalizado o pro- cesso de segmentação, a função compara o custo de representar o bloco de resíduo
utilizando um único bloco do dicionário ou utilizando a concatenação de blocos, retor- nando uma árvore de segmentação do resíduo podada com a representação do resíduo cujo custo foi inferior.
A sua complexidade computacional está relacionada com os constantes cálculos dos custos para cada nó das árvores de segmentação do resíduo.
• least_sq: a função least_sq é responsável pelo cálculo dos coeficientes utiliza- dos no modo de predição LSP para gerar o melhor valor em cada píxel. Para o efeito, o modo de predição LSP utiliza um método de fatorização LU em conjunto com uma técnica de forward factorization, sendo essas operações a razão para a sua elevada complexidade computacional.
Utilizando a lei de Amdahl definida na Equação 3.1 podemos estimar o ganho obtido para- lelizando estas três funções utilizando a seguinte equação:
G(N)= 1
0.011522+ 0.988478N (4.1) onde N é o número de núcleos de execução paralelos. Assim podemos concluir que para- lelizando as três funções referidas anteriormente o limite teórico para o speedup atingível será de aproximadamente 86.79, isto para quando o valor de N é tão elevado que se pode desprezar o tempo de execução do fluxo paralelo.
4.4
Implementação
Após a análise dos dados encontrados durante o profiling da versão sequencial, foi definido como objetivo inicial a migração das funções quad_err e optimize_block para a GPU. Esta decisão teve como fundamento três fatores importantes: (1) em primeiro lugar o impacto computacional que ambas estas funções têm sobre o desempenho do algoritmo, pois são as duas funções com maior consumo de poder computacional do algoritmo, (2) em segundo lugar o facto de ambas as funções estarem relacionadas entre si, ou seja, a função optimize_blockestá dependente dos resultados da função quad_err para que consiga calcular os custos associados a cada bloco permitindo assim uma mais fácil sincronização de dados entre a CPU e a GPU e (3) o facto de ambas as funções terem a sua complexidade computacional relacionada com cálculos matemáticos, pois as operações aritméticas são o tipo de operação de execução mais rápida em GPUs.
Com a migração destas funções para a GPU, o objetivo é delegar para a GPU o cálculo tanto dos erros quadráticos como dos custos de cada bloco, utilizando posteriormente uma função de redução que permita encontrar os melhores blocos para todos os níveis da ár- vore. Isto permite que a função quad_err seja eliminada por completo e que a função optimize_block seja utilizada apenas para efetuar a construção da árvore de segmen- tação do resíduo podada, utilizando para isso os valores previamente calculados pelos três passos descritos anteriormente.