You are on page 1of 7

Acelerando a compressao de arquivos utilizando GPU

Carlos Henrique Lima Braz1 , Mayara Marques1


1

Instituto Multidisciplinar Universidade Federal Rural do Rio de Janeiro (UFRRJ)


Departamento de Ciencia da Computaca o
Av. Governador Roberto Silveira S/N - CEP: 26020-740 Nova Iguacu RJ
Abstract. This article describes the experience found when using the GPU using
the CUDA architecture to parallelize the Huffman coding to try to improve its
running time . The Huffman coding is a compression method that uses the probabilities of occurrence of symbols in the data set to be compressed to determine
variable length codes for each symbol.
Resumo. Este artigo descreve a experiencia encontrada ao utilizar a GPU
usando a arquitetura CUDA para paralelizar a codificaca o de Huffman para
tentar melhorar seu tempo de execuca o. A codificaca o de Huffman e um metodo
de compactaca o que usa as probabilidades de ocorrencia dos smbolos no conjunto de dados a ser compactado para determinar codigos de tamanho variavel
para cada smbolo.

1. Introduca o
O crescente desempenho dos processadores de computadores modernos deve-se ao uso de
numero cada vez maior de cores, unidades de processamento vetorial e GPUs. Desenvolver programas que explorem os recursos de processamento paralelo dessas arquiteturas
de modo a obter maior desempenho e uma tarefa bastante complexa. Nesse artigo exploraremos o uso da GPU para acelerar o processo de compressao de arquivos de texto
utilizando a codificaca o de Huffman.
A codificaca o de Huffman e um metodo de compressao que usa as probabilidades de ocorrencia dos smbolos no conjunto de dados a ser comprimido para determinar
codigos de tamanho variavel para cada smbolo. Ele foi desenvolvido em 1952 por David
A. Huffman que era, na e poca, estudante de doutorado no MIT, e foi publicado no artigo
A Method for the Construction of Minimum-Redundancy Codes.

2. A codificaca o
ASCII (American Standard Code for Information Interchange) e um padrao de
codificaca o de caracteres usado por muitas linguagens de programaca o. Neste padrao,
cada caracter e codificado com o mesmo numero de bits por caracter (e.x., 8 bits).
Desta maneira, ha 256 possveis combinaco es para representar os caracteres em ASCII
[Costa 2009]. A codificaca o de Huffman compacta os dados usando um numero menor
de bits para codificar caracteres que ocorrem mais frequentemente de maneira que nem
todos os caracteres precisem ser codificados com 8 bits. Considerando a cadeia de caracteres papaie usando a codificaca o ASCII (8 bits por caracter), esses 5 caracteres seriam
escritos no arquivo utilizando 40 bits (5 x 8 bits). Utilizando a codificaca o de Huffman
os mesmos 5 caracteres seriam escritos utilizando apenas 8 bits (Desconsiderando outros

bits de controle do algoritmo), ou seja para esses exemplo o arquivo teria seu tamanho
reduzido 5 vezes, essa codificaca o sera explicada mais a frente.
A codificaca o de Huffman pode ser subdividida em:
Histograma;

Construca o da Arvore
de Huffman;
Construca o da Tabela de Codigo Variavel;
2.1. Histograma
Para construca o do Histograma e necessario a contagem da quantidade dos caracteres
do arquivo original. O algoritmo sequencial para a contagem de frequencia e relativamente simples, cada caracter e contabilizado e armazenado em um vetor de 256 posico es
(quantidade de caracteres da Tabela ASCII), basta entao percorrer o arquivo e a cada
caracter encontrado e necessario acrescentar +1 na posica o correspondente do vetor de
frequencias. Ao final da execuca o do algoritmo teremos um vetor com as frequencias de
cada caracter, o Histograma.

2.2. Construca o da Arvore


de Huffman
Uma a rvore binaria, chamada de a rvore de Huffman e construda recursivamente a partir
da junca o dos dois smbolos de menor probabilidade, que sao entao somados em smbolos
auxiliares e estes smbolos auxiliares recolocados no conjunto de smbolos. O processo
termina quando todos os smbolos foram unidos em smbolos auxiliares, formando uma
a rvore binaria. O algortimo de construca o da a rvore de Huffman utiliza entao o histograma como entrada para gerar a a rvore. Para a cadeia de caracter de exemplo papaitemos como entrada as frequencias da imagem abaixo:

Figura 1. Exemplo de contagem de frequencia

Utilizando as frequencias da figura 1 como entrada o algoritmo de construca o da


a rvore de Huffman a constroi recursivamente juntando os smbolos de menor frequencia,
que sao somados em smbolos auxiliares. Temos como sada a a rvore ilustrada na figura
2.
2.3. Construca o da tabela de codigo variavel
Para construca o da tabela de codigo variavel e utilizada a arvore como entrada. Para
cada aresta da a rvore e associado um dos dgitos binarios (0 ou 1) de acordo com sua


Figura 2. Arvore
de Huffman

posica o na a rvore, 0 para esquerda e 1 para direita. O codigo correspondente a cada


smbolo e entao determinado percorrendo-se a a rvore e anotando-se os dgitos das arestas
percorridas desde a raiz ate a folha que corresponde ao smbolo desejado. Os codigos de
cada smbolos sao armazenados gerando assim a tabela de codigo variavel. O algoritmo
para construca o da a rvore e geraca o da tabela de codigo variavel e o seguinte:

da tabela de codigo

Figura 3. pseudo-codigo
da construcao
variavel

3. Paralelismo
Entendido o funcionamento do algoritmo de codificaca o de Huffman passamos agora para
parte na qual tentaremos otimiza-lo utilizando paralelismo com GPU usando a arquitetura
CUDA. Nesse artigo paralelizamos uma parte do algoritmo, a construca o do histograma.
Embora simples de descrever e compreender, histogramas de dados, aparece em
muitos problemas da computaca o. Alem da compressao de dados ele e usado em algo-

ritmos de processamento de imagem, visao computacional, aprendizagem de maquina,


codificaca o de a udio , e muitos outros.
3.1. Histograma em CPU
Para entender melhor o histograma multi-core em GPU mostraremos o funcionamento de
um histograma sequencial utilizando a CPU. O algoritmo e relativamente simples.
Considere o vetor buffer[] contendo os caracteres do arquivo original que desejamos contar as frequencias. A ideia do algoritmo e que sempre que vemos algum caracter
no vetor buffer[], queremos incrementar o valor do caracter no nosso histograma. Desta
forma , estamos contando o numero de vezes vimos o caracter, veja na figura 3.

do histograma em CPU
Figura 4. Construcao

3.2. Histograma na GPU


Nos gostaramos de adaptar o exemplo histograma na CPU para executar na GPU. Se o
arquivo original e muito grande o algoritmo sequencial em CPU pode levar muito tempo
para contar de forma sequencial todos os seus caracteres. Tendo threads lendo diferentes
partes da entrada isso se torna mais eficiente. O problema com o calculo de um histograma
a partir dos dados de entrada surge a partir do fato de que varias threads podem querer
incrementar a mesma posica o do histograma de sada, ao mesmo tempo. Nesta situaca o,
teremos de usar incrementos atomicas para evitar esta situaca o [Gupta 2016].
3.2.1. Operaco es Atomicas
Felizmente, as condico es de corrida sao faceis de evitar, em CUDA. Uma operaca o
atomica e capaz de ler, modificar e escrever um valor de volta a` memoria sem a interferencia de qualquer outra thread, que garante que uma condica o de corrida nao ira
ocorrer. Operaco es atomicas em CUDA trabalham tanto para memoria compartilhada e
memoria global. Operaco es atomicas na memoria compartilhada sao geralmente utilizadas para evitar condico es de corrida entre diferentes threads dentro do mesmo bloco.
Operaco es atomicas em memoria global sao usados para evitar as condico es de corrida
entre duas threads de mesmo bloco ou de blocos diferentes [SuperComputingBlog 2009].
3.2.2. Primeira Implementaca o
Levando em consideraca o a necessidade de utilizar a operaca o atomica tentamos adaptar
o codigo em CPU para ser utilizado em GPU. Vejamos na figura a seguir.
Um problema claro dessa implementaca o inicial e que caso tenhamos mais de
1024 caracteres na entrada de dados nao sera possvel contabilizar de forma correta as
frequencias. Por esse motivo tivemos que melhorar essa implementaca o

do histograma em CUDA
Figura 5. Primeira implementacao

3.2.3. Segunda Implementaca o


Vimos a necessidade de dividir as threads em blocos para que pudessemos processar entradas de dados maiores. Segue abaixo o codigo da segunda implementaca o.

do histograma em CUDA
Figura 6. Segunda implementacao

Logo apos a segunda implementaca o fizemos um comparativo com o a codificaca o


de Huffman sequencial e infelizmente notamos que apesar da execuca o em paralela
a implementaca o em GPU demonstrou-se muito ineficiente em termos de tempo de
execuca o. Notamos entao que provavelmente a operaca o atomica em memoria global
estava causando o problema. Para garantir atomicidade das operaco es de incremento, e
necessario que o hardware serialize as operaco es para a mesma posica o de memoria. Isso
pode resultar em uma longa fila de operaco es pendentes e qualquer ganho de desempenho
que poderia ter tido ira desaparecer.
3.2.4. Terceira Implementaca o
Ironicamente, apesar de que as operaco es atomicas causar essa degradaca o do desempenho, a soluca o proposta envolver usar mais operaco es atomicas, nao menos. O problema
central nao era o uso de atomicos tanto como o fato de que milhares de topicos estavam
competindo para o acesso a uma parte relativamente pequena numero de enderecos de
memoria. Para essa fase da implementaca o dividimos em duas partes, explicaremos cada
parte a seguir:
Fase 1
Na primeira fase, cada bloco paralelo ira calcular um histograma separado dos
demais. Uma vez que cada bloco faz isso de forma independente, podemos calcular estes
histogramas na memoria compartilhada, poupando-nos da concorrencia na memoria global. Para isso e necessario primeiro a alocar e zerar um buffer de memoria compartilhada

para armazenar histograma intermediario de cada bloco.

Figura 7. Alocar e zerar um buffer de memoria


compartilhada

Depois de zerar o histograma, o proximo passo e muito similar ao nosso histograma GPU original. As u nicas diferencas aqui sao que usamos a buffer temporario de
memoria compartilhada em vez de um buffer de memoria global e que precisamos de
uma chamada subsequente para syncthreads() para garantir a u ltima das nossas gravaco es
foram cometidos.

Figura 8. Armazenando histograma na memoria


compartilhada

Fase 2
O u ltimo passo no nosso exemplo histograma com memoria compartilhada requer
a fusao dos histogramas temporarios de cada bloco com o histograma da memoria global.

Figura 9. Histograma na memoria


compartilhada

Ao utilizar a memoria compartilhada notamos uma grande melhora no desempenho em relaca o ao tempo de execuca o tanto para implementaca o sequencial tanto para as
outras implementaco es utilizando a GPU.

4. Resultados Obtidos
Os testes foram realizados em uma maquina com placa grafica Nvidia Tesla K40c, velocidade de clock do processador de 3.0 GHz e memoria 12GB. As configuraco es da CPU:
processador i5-4590, velocidade clock 3.3GHz , 4 nucleos e 8GB de memoria RAM.
Foram feitos 3 testes e seguem abaixo em tabela:

Figura 10. Resultados Obtidos

5. Conclusao
O resultados obtidos em nossa implementaca o foram mais do que satisfatorios, para um
arquivo com tamanho 2,6MB a implementaca o utilizando GPU conseguiu obter um tempo
de execuca o de 0,007639 s enquanto a implementaca o de CPU obteve 0.205299 s, ou seja,
o codigo de GPU mostrou um speedup de 26,87x sobre o de CPU.

Referencias
Costa, P. D. (2009).
A codificaca o de Huffman.
Disponvel
em:
http://www.inf.ufes.br/
pdcosta/ensino/2009-1-estruturas-dedados/material/CodificacaoHuffman.pdf.
Gupta, N. (2016). Computing Histogram on CUDA. Disponvel em: http://cudaprogramming.blogspot.com.br/2013/03/computing-histogram-on-cuda-cudacode8 .html.
SuperComputingBlog (2009).
Atomic Operations.
Disponvel
http://supercomputingblog.com/cuda/cuda-tutorial-4-atomic-operations/.

em:

You might also like