0
UNIJUI - UNIVERSIDADE REGIONAL DO NOROESTE DO ESTADO DO RIO
GRANDE DO SUL
DCEEng – DEPARTAMENTO DE CIÊNCIAS EXATAS E ENGENHARIAS
PROCESSAMENTO PARALELO COM ACELERADORES GRÁFICOS
RODRIGO SCHIECK
Santa Rosa, RS - Brasil
2012
1
RODRIGO SCHIECK
PROCESSAMENTO PARALELO COM ACELERADORES GRÁFICOS
Projeto apresentado na disciplina de
Trabalho de Conclusão de Curso do curso
de Ciência da Computação da Universidade
do Noroeste do Estado do RS como requisito
básico para apresentação do Trabalho de
Conclusão de Curso.
Orientador: Edson Luiz Padoin
Santa Rosa – RS
2012
2
PROCESSAMENTO PARALELO COM ACELERADORES GRÁFICOS
RODRIGO SCHIECK
Projeto apresentado na disciplina de
Trabalho de Conclusão de Curso do curso
de Ciência da Computação da Universidade
do Noroeste do Estado do RS como requisito
básico para apresentação do Trabalho de
Conclusão de Curso.
____________________________________
Orientador: Prof. Me. Edson Luiz Padoin
BANCA EXAMINADORA
____________________________________
Prof. Me. Rogério Samuel de Moura Martins
Santa Rosa – RS
2012
3
“A mente que se abre a uma nova ideia
jamais voltará ao seu tamanho original.”
Albert Einstein
4
Dedicatória
Aos meus pais Armindo e
Alda, e a minha esposa
Elenice, pessoas que amo
muito e que me apoiaram e
incentivaram em toda esta
trajetória, tornando mais este
sonho uma realidade, mas que
não seja o último, e sim apenas
mais um dos muitos outros que
virão. Na indisponibilidade de
tempo, o qual não pude estar
com eles, pois tive que mediar
entre o trabalho e o estudo,
mas que daqui pra frente
pretendo compensar.
5
AGRADECIMENTOS
Agradeço à Unijuí como um todo, pelo ótimo ambiente de ensino e corpo
docente disponibilizado.
Agradeço em especial aos meus pais, que além do auxílio financeiro me
deram todo apoio e compreensão, sem o qual teria sido muito difícil superar algumas
etapas desta longa trajetória.
Agradeço à minha esposa, companheira compreensiva, que sempre esteve
ao meu lado me dando apoio e incentivo amenizando o desgaste físico e mental
decorrente das diversas horas consecutivas de estudo.
Agradeço ao meu orientador, professor Edson Luiz Padoin que levo comigo
não apenas como professor ou orientador, mas como amigo, que mesmo com muito
trabalho pode me dar muita atenção, sem contar seus conselhos técnicos e
científicos, sempre muito bem elaborados com uma ótima fundamentação. Soube
ser muito compreensivo, mas também cobrou quando necessário. Mesmo não
estando
sempre
disponível
fisicamente,
respondeu
sempre
quase
que
instantaneamente meus e-mails. Sua motivação e seu otimismo foram de grande
apoio para que eu não abaixasse a cabeça perante as dificuldades. Muito obrigado
pela parceria no desenvolvimento deste trabalho.
Aos professores Gerson Battisti, Rogério Martins e Marcos Cavalheiro que
serviram de base na construção do meu conhecimento, o qual foi de suma
importância, pois foi empregado no desenvolvimento deste trabalho.
Muito Obrigado a Todos!
6
RESUMO
As GPUs estão se tornando cada vez mais presentes no cenário da
computação de alto desempenho. Elas são processadores massivamente paralelos,
inicialmente usados para processamento gráfico e jogos. Desde o surgimento da
NVIDIA GeForce série 8, e a introdução do CUDA e outras ferramentas, as GPUs se
tornaram programáveis, sendo capazes de executar aplicativos comuns e, com isso,
através da modificação de algumas aplicações para algoritmos paralelos,
conseguiram uma maior performance e escalabilidade dessas aplicações. As GPUs
programáveis com suas arquiteturas massivamente paralelas expandiram o
horizonte da computação de alto desempenho, tornando possível executar mais
rapidamente algoritmos paralelos e com menor consumo energético.
O objetivo deste trabalho é comprovar e demonstrar a melhor eficiência das
GPUs quanto as CPUs em aplicações paralelas. Para isto, foram desenvolvidas
aplicações utilizando CUDA e APARAPI para mostrar o desempenho da GPU assim
como também aplicações que exigissem desempenho da CPU para fazer a
comparação. Foi implementado um conjunto de oito algoritmos que utilizam técnicas
diferentes de “stress” de ambas as arquiteturas.
Os resultados dos testes foram submetidos a um processo de avaliação
quanto à corretude e ao tempo de execução. Gráficos foram elaborados no intuito de
analisar melhor e descrever o comportamento do sistema diante de diferentes
recursos, como número de threads, números de processos, dimensões das matrizes,
etc. A principal conclusão deste projeto foi que a definição da estratégia é decisiva
para obtenção do menor custo de tempo, onde aplicações altamente paralelizáveis
que executam uma única instrução sobre múltiplos dados, podem obter um ganho
exponencial de desempenho utilizando-se de GPUs.
Palavras-chave: Processamento Paralelo, Arquitetura Heterogênea,
Acelerados Gráficas, Alto Desempenho, CUDA, APARAPI, GPU, GPGPU.
7
ABSTRACT
GPUs are becoming increasingly present in the scenario of high performance
computing. They are massively parallel processors, initially used for graphics
processing and games. Since the emergence of the NVIDIA GeForce 8 series and
the introduction of CUDA and other tools, GPUs became programmable, being able
to run common applications and, thus, by modifying some applications for parallel
algorithms, achieved a higher performance and scalability these applications. The
programmable GPUs with their massively parallel architectures expanded the horizon
of high performance computing by making it possible parallel algorithms run faster
and with less energy consumption.
The objective of this work is to prove and demonstrate the improved
efficiency of GPUs as CPUs in parallel applications. For this, applications have been
developed using CUDA and APARAPI to show the performance of the GPU as well
as applications that require CPU performance to make the comparison. We have
implemented a set of eight algorithms that use different techniques “stress” of both
architectures.
The test results were submitted to a review process regarding the
correctness and runtime. Charts were developed in order to better analyze and
describe the system behavior before different features, such as number of threads,
number of processes, dimensions of arrays, etc. The main conclusion of this project
was that the definition of the strategy is crucial to obtaining the lowest cost of time,
where highly parallelizable applications running on a single instruction multiple data,
may obtain a gain exponential performance using GPUs.
Keywords:
Parallel
Processing,
Heterogeneous
Architecture,
Accelerated Graphics, High Performance, CUDA, APARAPI, GPU, GPGPU.
8
LISTA DE SIGLAS
API
Application Programming Interface
APARAPI
A PARallel API
APU
Accelerated Processing Unit
ARM
Advanced RISC Machine
CMP
Chip Level Multithreading
CPU
Central Processing Unit
CPD
Centro de Processamento de Dados
CUDA
Compute Unified Device Architecture
FLOPS
Floating point Operations Per Second
FPS
Frames Por Segundo
GPU
Graphics Processing Unit
GPGPU
General Purpose on Graphics Processing Units
ILP
Instruction Level Parallelism
IT
Information Technology
JVM
Java Virtual Machine
PC
Personal Computer
PCI
Peripheral Component Interconnect
SDK
Software Development Kit
SIMD
Single Instruction Multiple Data
SM
Streaming Multiprocessor
SP
Streaming Processors
TI
Tecnologia da Informação
TLP
Thread Level Parallelism
9
LISTA DE IMAGENS
Figura 1: Supercomputador TITAN ..................................................................................... 21
Figura 2: Novo design SM. ................................................................................................... 30
Figura 3: Diferença de cores entre uma CPU e uma GPU. ............................................ 33
Figura 4: Comparação entre chips CPU e GPU. .............................................................. 35
Figura 5: NVIDIA tesla M2090. ............................................................................................ 37
Figura 6: Desempenho NVIDIA Tesla. ............................................................................... 38
Figura 7: Evolução Das GPUs. ............................................................................................ 39
Figura 8: NVIDIA gpu Roadmap.......................................................................................... 40
Figura 9: Tabela De Especificações. .................................................................................. 41
Figura 10: Arquitetura de uma APU.................................................................................... 43
Figura 11: Plataforma Java. ................................................................................................. 46
Figura 12: HelloWord Java. .................................................................................................. 47
Figura 13: Comparação de desempenho. ......................................................................... 49
Figura 14: Modelo de Navier-Stokes. ................................................................................. 50
Figura 15: Método de Lattice Boltzman. ............................................................................ 50
Figura 16: Modelo de Programação CUDA. ...................................................................... 51
Figura 17: Modelo de Memória CUDA. .............................................................................. 52
Figura 18: Memórias CUDA. ................................................................................................ 53
Figura 19: Paralelismo Dinamico. ....................................................................................... 55
Figura 20: Mandelbrot. .......................................................................................................... 55
Figura 21: OpenCL. ............................................................................................................... 58
Figura 22: GTX 465 ............................................................................................................... 61
Figura 23: Conjunto de Mandelbrot .................................................................................... 62
Figura 24: Simulação de Corpos. ........................................................................................ 63
10
LISTA DE TABELAS
Tabela 1: Unidades de Medida de Desempenho ............................................................. 27
Tabela 2: Hardware ............................................................................................................... 60
Tabela 3: Detalhes da GPU ................................................................................................. 60
Tabela 4: Software................................................................................................................. 61
11
SUMÁRIO
INTRODUÇÃO .......................................................................................................... 13
1.1 JUSTIFICATIVA .................................................................................................. 16
1.2 OBJETIVO GERAL ............................................................................................. 17
1.3 OBJETIVOS ESPECÍFICOS ............................................................................... 18
1.4 METODOLOGIA E PROCEDIMENTOS .............................................................. 18
1.5 ORGANIZAÇÃO DO TRABALHO ....................................................................... 19
1.6 TRABALHOS RELACIONADOS ......................................................................... 20
2 COMPUTAÇÃO DE ALTO DESEMPENHO .......................................................... 21
2.1 PROCESSAMENTO PARALELO ........................................................................ 23
2.2 ARQUITETURAS DE FLYNN .............................................................................. 24
2.2.1 SIMD ................................................................................................................ 24
2.3 MEDIDAS DE DESEMPENHO ............................................................................ 25
2.3.1 FLOPS ............................................................................................................. 27
2.4 STREAM PROCESSORS ................................................................................... 28
2.5 GREEN IT ........................................................................................................... 28
3 HARDWARE .......................................................................................................... 32
3.1 CPU ..................................................................................................................... 32
3.2 GPU .................................................................................................................... 33
3.2.1 GPGPU ............................................................................................................ 35
3.2.2 Projeto Denver ................................................................................................ 38
3.2.3 Evolução das Placas Gráficas NVIDIA.......................................................... 39
3.4 APU ..................................................................................................................... 42
3.5 COMPARAÇÃO .................................................................................................. 43
4 SOFTWARE ........................................................................................................... 45
4.1 LINGUAGENS DE PROGRAMAÇÃO ................................................................. 45
12
4.1.1 C/C++ ............................................................................................................... 45
4.1.2 Java ................................................................................................................. 46
4.2 DESENVOLVIMENTO PARA GPU ..................................................................... 47
4.2.1 CUDA ............................................................................................................... 47
4.2.2 CUDA 5 ............................................................................................................ 54
4.2.3 APARAPI ......................................................................................................... 56
4.2.4 OpenCL ........................................................................................................... 58
5 AMBIENTE DE TESTES ........................................................................................ 60
5.1 HARDWARE ....................................................................................................... 60
5.2 SOFTWARE ........................................................................................................ 61
5.3 Benchmarks ........................................................................................................ 62
5.3.1 O Conjunto de Mandelbrot ............................................................................ 62
5.3.2 Simulação de Corpos ..................................................................................... 63
6 RESULTADOS ....................................................................................................... 64
6.1 CUDA .................................................................................................................. 64
6.1.1 Cálculo de Números Perfeitos ...................................................................... 64
6.1.2 Cálculo de Números Primos .......................................................................... 65
6.1.3 Multiplicação de Matrizes .............................................................................. 65
6.1.4 Conjunto de Mandelbrot ................................................................................ 66
6.1.5 Simulação de Corpos ..................................................................................... 67
6.2 APARAPI ............................................................................................................. 67
6.2.1 Cálculo de Números Perfeitos ...................................................................... 67
6.2.2 Cálculo de Números Primos .......................................................................... 68
6.2.3 Multiplicação de Matrizes .............................................................................. 69
6.2.4 Conjunto de Mandelbrot ................................................................................ 69
6.3 Comparação entre as APIs ................................................................................. 70
6.3.1 Cálculo de Números Perfeitos ...................................................................... 70
6.3.2 Cálculo de Números Primos .......................................................................... 71
6.3.3 Multiplicação de Matrizes .............................................................................. 71
CONCLUSÃO ........................................................................................................... 72
REFERÊNCIAS BIBLIOGRAFICAS ......................................................................... 74
OBRAS CONSULTADAS ......................................................................................... 80
ANEXOS ................................................................................................................... 81
13
INTRODUÇÃO
Maior velocidade e maior precisão são, e sempre serão proporcionados por
cientistas e engenheiros da computação. Para alcançar essas metas, processadores
single-core foram montados para chegar à "computação paralela". Este paralelismo
recentemente se estendeu ao nível do chip, com o surgimento de arquiteturas multicore, que é, a grosso modo, a adição de mais núcleos em um único chip em ambas
as unidades centrais de processamento (CPUs) e unidades de processamento
gráfico (GPUs). O último e mais recente é um candidato possivelmente mais forte do
que o anterior para resolver os problemas computacionalmente exigentes. Isto é
devido ao fato de a capacidade de ponto flutuante computacional das GPUs torna-se
geralmente 10 vezes mais elevado do que CPUs. Como resultado, a computação
científica está se movendo rapidamente em direção ao paralelismo multi-core.
Modelagem e simulação em ciência e engenharia nucleares dependem muito de
poder computacional. Engenheiros nucleares se aproveitaram da paralelização para
avançar teste e simulações de neutrônica, hidráulica térmica, materiais, física de
plasma, fluídos, química quântica, física espacial, etc.
Todos os sistemas de computação, de móveis a supercomputadores, estão
se tornando computadores paralelos heterogêneos utilizando ambos os CPUs multicore e as GPUs multi-thread para maior eficiência de energia e taxa de computação.
Enquanto a comunidade de computação está correndo para construir ferramentas e
bibliotecas para facilitar a utilização destes sistemas heterogêneos de computação
paralela,
eficaz
e
utilização
segura
desses
sistemas
vai
sempre
exigir
conhecimentos sobre as interfaces de programação de baixo nível nestes sistemas.
A evolução do hardware tem sua teoria da evolução que é relativa às
necessidades humanas e mais ainda as necessidades de softwares. Portanto
partindo deste entendimento o hardware deve estar em uma constante
transformação para que supra as necessidades dos softwares, onde o inverso
também é valido, pois o software também deve atender as necessidades do
hardware, sendo assim estas duas tecnologias devem andar paralelamente. Desse
modo, para que tenhamos um hardware que atenda um software de alta
complexidade computacional, se faz necessário que ele seja escalável e agregue
um alto poder de processamento.
14
Está ficando cada vez mais difícil fazer com que os computadores funcionem
com maior velocidade apenas aumentando a frequência do relógio(clock) por causa
de problemas com a maior dissipação de calor e outros fatores, mas também
principalmente respeitando a eficiência energética do equipamento, Em vez disso,
projetistas estão buscando o paralelismo para conseguir grandes ganhos de
velocidade aproveitando os milhares de núcleos de processamento disponíveis nos
ambientes de supercomputação atuais.
Primeiro foi o paralelismo em nível de instrução(ILP) e como exemplo temos
o pipeline, depois o paralelismo em nível de threads(TLP), capaz de executar mais
de uma thread (processo) simultaneamente e por fim surge o paralelismo em nível
de chip que possibilita a execução simultânea de mais de um fluxo de instruções.
O paralelismo é um paradigma que visa extrair o máximo
desempenho possível durante a realização de uma tarefa, através da
exploração das características de simultaneidade presentes na carga de
trabalho e no ambiente em que se está trabalhando (EL-REWINI, 2005;
ABD-EL-BARR, 2005).
O paralelismo pode ser introduzido em muitos níveis diferentes, desde o
muito baixo, onde os elementos de processamento são muito fortemente acoplados,
até o muito alto, onde eles são fracamente acoplados, a seguir será abordado os 3
principais níveis de paralelismo conforme define Volpato (2011) em seu projeto de
graduação.
No nível baixo está o paralelismo no chip, no qual as atividades paralelas
ocorrem em um único chip. Uma forma de paralelismo no chip é o paralelismo no
nível de instrução, no qual uma instrução, ou uma sequência de instruções, emite
múltiplas operações que podem ser executadas em paralelo por diferentes unidades
funcionais. Uma segunda forma de paralelismo no chip é o multithreading, no qual
uma CPU pode comutar como quiser entre múltiplos threads, instrução por instrução,
criando um multiprocessador virtual. Uma terceira forma de paralelismo no chip é o
multiprocessador de chip único(CMT), ou seja, dois ou mais núcleos são colocados
no mesmo chip o que permite que eles executem operações simultaneamente.
Em um nível acima encontramos os co-processadores, ou aceleradores, que
normalmente são placas de expansão que agregam capacidade de processamento
extra em alguma área especializada tal como processamento gráfico. Este será o
15
foco desta pesquisa, avaliando o quão benéfico se torna a utilização deste tipo de
co-processamento.
Em um próximo nível encontramos os multiprocessadores de memória
compartilhada. Estes tipos de sistemas fazem uso de duas ou mais CPUs totalmente
desenvolvidas que compartilham uma memória em comum.
Nesta breve explanação sobre os tipos de paralelismo, é possível observar a
flexibilidade da computação e o quão mutável ela é, onde quem quiser acompanhála não poderá prender-se a nada específico. Pensando nas arquiteturas para
computação de alto desempenho, como o próprio nome sugere se entende que o
foco é alto desempenho. Para isto, há muito tempo vem-se usando arquiteturas
homogêneas,
onde
a
unidade
de
processamento
era
exclusivamente
o
processador(CPU), mas com os avanços da tecnologia e a evolução das
aceleradores gráficos, foi se pensando nelas como uma grande auxiliar ao
processamento paralelo, o co-processamento, devido a sua escalabilidade e em
muitos casos com maior poder de processamento. Assim surgiram as arquiteturas
heterogêneas que fazem uso de CPUs, GPUs e as APUs, para realizarem o
processamento das tarefas.
A GPU (Graphics Processing Unit) é uma placa aceleradora de imagem
otimizada para paralelismo e renderização de imagens. Devido a essa otimização, a
GPU atinge picos de velocidade que supera qualquer CPU existente. Por exemplo,
uma GPU NVIDIA Fermi pode ser cerca 250 vezes mais veloz que um Intel Core i7
utilizada de forma sequencial.
Como exemplo, no TOP500, a classificação dos 500 computadores mais
rápidos do mundo, onde vários dos computadores mais bem colocados já fazem uso
desta arquitetura revolucionária, são 62 na 40ª lista.
Em sua classificação, Michael J. Flynn(1966-1972), definiu que tais
arquiteturas paralelas fazem uso da classe SIMD, onde se trabalha com uma
instrução sobre múltiplos conjuntos de dados.
Para que possamos usufruir de todo o poder do hardware que temos em
mãos precisamos de algoritmos específicos. Estes algoritmos farão a divisão entre
as tarefas da CPU e GPU ou da APU conforme a arquitetura utilizada. Utilizando
uma GPU NVIDIA dispomos da API CUDA que requer programação em C.
Recentemente chegou as mãos do desenvolvedores uma nova API de código aberto
16
disponibilizada pela AMD, a APARAPI, ele nada mais é do que uma biblioteca Java
para paralelismo com GPUs e APUs.
É nesta base que se dará esta pesquisa focando exclusivamente a esta
arquitetura heterogênea, mais especificamente de como fazer total uso das
aceleradores gráficos.
1.1 JUSTIFICATIVA
O desempenho de processamento paralelo de uma GPU é relativamente
maior comparado a CPU devido à sua arquitetura massivamente paralela, logo, já
está otimizada para atividades que demandem de processamento paralelo. Holz
(2010), em seu trabalho de conclusão de curso, realizou testes sobre uma
plataforma com uma GPU e mesmo com uma arquitetura relativamente inferior,
conseguiu um ganho de performance de 78% nos algoritmos processados pela
GPU. Há relatos de testes onde se obteve ganhos muito maiores, claro que não na
mesma arquitetura.
Jack Dongarra, especialista em supercomputadores e professor da
Universidade do Tennessee, em seu relato sobre estas arquiteturas heterogêneas,
disse:
As GPUs evoluíram ao ponto de permitirem que muitos aplicativos
do mundo real sejam facilmente implementados e executados de forma
significativamente mais rápida do que em sistemas com vários núcleos. As
futuras arquiteturas de computação serão sistemas híbridos, com GPUs de
núcleo paralelo operando em conjunto com CPUs de vários núcleos
(DONGARRA).
Por se tratar de um processador vetorial, onde geralmente existe um número
expressivo de núcleos se comparando com as CPUs convencionais, claro que são
processadores de arquiteturas diferentes, mas ela pode facilmente substituir uma
quantidade significativa de CPUs.
Uma arquitetura paralela usando GPU tem um melhor aproveitamento de
memória pelo fato de cada GPU possuir sua própria memória, mas o que não a
impede de usar a memória principal do sistema.
GPUs possuem maior desempenho em operações com ponto flutuante, não
apenas com ponto flutuante e sim também nas operações gerais que envolvem
17
cálculos numéricos(vetoriais), assim, possibilitando execução em tempo polinomial
de algoritmos relativamente custosos desde que tenham pouca dependência de
dados.
Em 2010 subiu ao primeiro lugar do TOP500 um novo supercomputador, o
Tianhe-1A produzido pela empresa NUDT, ele é composto por GPUs NVIDIA e
CPUs Xeon, totalizava 186368 cores oque gerava um consumo de 4 megawatts. A
NVIDIA afirma que se o mesmo número de cores fosse atingido utilizando apenas
CPUs, o consumo dele ultrapassaria os 12 megawatts e que com o processamento
paralelo fornecido pelas GPUs ele consegue ser três vezes mais eficiente e com um
consumo três vezes menor.
Segundo a NVIDIA a versão mais recente da CUDA Toolkit (4.1) oferece
diversos novos recursos e melhorias que visam ser explorados. A AMD, não
querendo ficar atrás, também lançou a APARAPI para o uso de paralelismo em
aplicações Java e promete aos desenvolvedores um código mais simplista e fácil de
usar. Somente os testes práticos dirão se a mais nova concorrente da CUDA tem
capacidade o suficiente para atuar entre as grandes nesta área.
Arquiteturas
heterogêneas
têm
maior
custo
benefício
e
melhor
aproveitamento de espaço interno físico porque podem ser usadas diversas GPUs
em uma mesma placa-mãe.
Levando em conta o fato de que é possível usar diversas GPUs sobre uma
mesma placa-mãe, somamos o número de núcleos de todas elas mais os núcleos
da(s) CPU(s) locais, logo, teremos um número 'x' de núcleos locais para os quais
são possíveis atribuir tarefas, onde estes estão interligados por barramentos de alta
velocidade. Porém não seria o mesmo se usássemos apenas CPUs, já que para
atingir este mesmo número de núcleos teríamos que interliga-las através de um
barramento de rede o qual não atingiria a mesma velocidade.
Além de tudo isso ainda possuímos o fato de que o mercado atual,
principalmente o de computadores portáteis, está apostando nas APUs, elas são
processadores híbridos que contém em si uma CPU e uma GPU.
1.2 OBJETIVO GERAL
Mostrar que arquiteturas heterogêneas podem atingir maior performance
comparadas a arquiteturas convencionais ou tradicionais.
18
1.3 OBJETIVOS ESPECÍFICOS

Usar a arquitetura heterogênea de co-processamento com uma GPU
como co-processador. Esta prática deverá reduzir a carga da CPU e
transferi-la para a GPU na execução dos algoritmos de testes.

Executar algoritmos e benchmarks, que demandam de um alto poder
computacional, paralelamente processados, exibindo, através dos
resultados, que o co-processamento com GPU é uma ótima alternativa
ao processamento paralelo, pois espera-se reduzir o tempo de
execução dos mesmos.

Realizar os testes sobre a última versão da API de desenvolvimento
CUDA da NVIDIA, que apresenta diversas melhorias e novos recursos.

Comparar a performance entre a última versão da CUDA e da
APARAPI.

Mostrar que a arquitetura heterogênea juntamente com o ganho de
desempenho também proporciona um melhor aproveitamento de
espaço físico estrutural, ou seja, mais performance em um menor
espaço. Em segundo plano existem alguns objetivos indiretos que
podem ser alcançados com o uso de uma arquitetura heterogênea,
como por exemplo algoritmos hoje ainda não solucionáveis em um
espaço de tempo relativamente baixo.
1.4 METODOLOGIA E PROCEDIMENTOS
Através de pesquisas bibliográficas, foram coletar dados sobre as
arquiteturas heterogêneas e homogêneas que serviram de base para a realização
do projeto. De base desta pesquisa, o próximo passo foi montar o hardware
necessário, sobre o qual foram realizados os testes de performance para a coleta de
dados. Com o hardware funcionando devidamente, foi instalado o ambiente de
desenvolvimento C, JAVA e CUDA.
Implantada a arquitetura, está tudo pronto para começar os testes práticos
de desempenho da arquitetura com algoritmos relativamente custosos que
19
causaram um “stress” na CPU e na GPU elevando ao máximo o processamento de
ambas. Os dados resultantes dos testes efetuados foram registrados e analisados
minuciosamente, pois serviram de base para comparar as duas arquiteturas.
Os mesmos testes foram utilizados com a mais nova alternativa ao
processamento em GPU, a APARAPI. Os dados dos testes foram coletados de
ambas APIs para fazer uma análise comparativa.
Por fim, a proposta é apresentar os resultados obtidos nesta pesquisa em
eventos específicos da área.
1.5 ORGANIZAÇÃO DO TRABALHO
Esta pesquisa está subdividida em 5 capítulos, além do capítulo introdutório,
que melhor descrevem a tecnologia e os recursos utilizados.
O capítulo 2, Computação de Alto Desempenho, descreve o mundo da
supercomputação, as supermáquinas que são utilizadas nos grandes CPDs para
pesquisas e simulações, suas demandas e suas limitações.
Seguindo para o capítulo 3, o tema tratado passa a ser o HARDWARE, o
equipamento físico aqui é abordado como um fator decisivo para se atingir os
resultados esperados. São detalhadas as caraterísticas das CPUs e das GPUs, com
o intuito de demonstrar o que a GPU oferece de melhor em termos de computação
paralela.
Em contra partida temos o SOFTWARE, abordado no capítulo 4. Esta seção
trata do software como uma ferramenta lógica, pois na verdade ele é uma API em si
que provê meios de se programar o hardware.
Não ficando apenas na teoria, a pesquisa põe em prática a teoria estudada,
almejando comprovar a eficiência da estrutura. Para que isto seja possível, foram
implantados os ambientes de hardware e software descritos no capítulo 5, Ambiente
de Testes.
Por fim, sobre o ambiente implantado realizou-se diversos testes com
algoritmos de benchmark, onde os resultados coletados encontram-se reunidos no
capítulo 6, Resultados.
20
1.6 TRABALHOS RELACIONADOS
Diversos trabalhos e pesquisas já foram feitas nesta área, alguns deles mais
especializados com maiores resultados e outros mais abrangentes, mas todos com o
mesmo intuito de mostrar os benefícios das GPUs. Vale destacar alguns destes
trabalhos, como o do Lucas Holz de 2010, aplicado no contexto da irrigação e de
absorção da água pelo solo, onde ele conseguiu uma performance 5 vezes maior
apesar de ter utilizado uma GPU de notebook.
Com a utilização desta tecnologia e do grande poder de
processamento paralelo das GPUs conseguiu-se resolver o algoritmo em
menores tempos de execução, aumentando assim a precisão dos
resultados, utilizando malhas compostas de um maior número de
pontos(HOLZ, 2005).
Outro trabalho como o de Sérgio Ricardo Dos Santos Moraes também
merece uma atenção especial, devido aos ótimos resultados obtidos e ao uso de
múltiplas GPUs.
Reescrever um código sequencial em paralelo nem sempre é
possível e fácil; o problema tem de ser paralelizável. O objeto do presente
trabalho – problema do transporte de nêutron resolvido por simulação
através do método de Monte Carlo – teve uma excelente performance em
relação à solução sequencial. Tomando-se a água como exemplo, a
solução paralela para um número de histórias de nêutrons igual a 1010 foi
mais de 300 vezes mais rápida para uma GPU e mais de 2.000 vezes para
oito GPUs, em relação à solução sequencial. As diferenças encontradas nas
soluções de uma para duas GPUs se mantiveram também para múltiplas
GPUs (MORAES, 2012).
Como é possível notar, ambos trabalhos focam em uso de algoritmos de
paralelismo, mas também reforçam que o ganho de performance só é possível
quando o contexto do problema possibilita a paralelização da tarefa.
21
2 COMPUTAÇÃO DE ALTO DESEMPENHO
Supercomputação
é
um
serviço
de
combinação
dos
recursos
computacionais disponíveis para atingir o mais alto nível de desempenho em
computação.
Os primeiros supercomputadores foram criados na década de 1960 pelo
engenheiro Seymour Cray para a extinta Control Data Corporation. Seymour Cray
posteriormente fundou uma empresa que leva o seu nome, a Cray Research, e
dominou o mercado da supercomputação durante cerca de 25 anos, até o início dos
anos de 1990. Os Cray ainda são estrelas no mundo da supercomputação, mas hoje
a concorrência é grande, com empresas como HP, Oracle, IBM, entre outras.
(GEEK.COM.BR, 2011).
Figura 1: Supercomputador TITAN
Fonte: http://creep.ru/1161053125-cray-titan-moschneyshiy-superkompyuter-mira.html
Um supercomputador, como na figura 1, é um computador cujo desempenho
é extremamente alto. A velocidade de processamento destas máquinas supera em
milhões de vezes a de um computador doméstico. Ele também precisa ter uma
capacidade de memória absurdamente grande para dar conta da enorme quantidade
de dados apresentados na entrada e depois produzidos na saída. A maioria dos
supercomputadores usa um tipo de processamento de informação chamado de
22
processamento paralelo, o que quer dizer que pode calcular várias coisas ao mesmo
tempo.
Embora essa capacidade de processamento possa parecer fútil, os
supercomputadores são bastante úteis. Cálculos e simulações científicas que
poderiam levar anos podem ser feitas em questão de dias ou mesmo horas. Por
conta disso, os supercomputadores têm lugar cativo em centros de pesquisa, sejam
elas aeroespaciais, militares, de física, química e medicina.
Titan, o supercomputador que está ilustrado na figura 1, deverá se tornar o
mais rápido do mundo, construído pelo Laboratório Nacional de Oak Ridge,
localizado
nos Estados
Unidos,
seu
desempenho
de
20
petaflops sendo
aproximadamente 90% oriundos dos 18.688 aceleradores da Tesla K20. O TITAN é
a evolução do antigo JAGUAR que possui 2,3 petaflops, ele é 10 vezes mais rápido
e cinco vezes mais eficiente do ponto de vista energético do que seu predecessor.
Caso o Oak Ridge tivesse atualizado o JAGUAR através da simples expansão de
sua arquitetura baseada em CPU, o sistema teria mais de quatro vezes seu tamanho
atual e consumiria mais de 30 megawatts de energia (SUPERCOMPUTADOR
Titan...,2012).
As previsões de tempo hoje são muito mais precisas do que há trinta ou
mesmo vinte anos atrás. Isso ocorre porque os institutos de meteorologia e clima
como o INPE do Brasil, não mais tentam “adivinhar” o que vai acontecer pela
configuração prévia da atmosfera. Em vez disso, um supercomputador simula,
baseado nas condições atuais e nas tendências anotadas, como o clima realmente
estará.
A quantidade de cálculos por segundo para essas simulações é muito alta, e
deve acontecer em poucas horas, caso contrário, não seria possível anunciar a
tempo um alerta de furação ou de maremoto, por exemplo. Da mesma forma,
apenas o imenso poder dos supercomputadores tornam práticas e possíveis as
pesquisas científicas e simulações de armas nucleares, modelagem química e
molecular e projeto de aeronaves e carros de corrida.
Com os supercomputadores cada vez mais poderosos, no caso da previsão
do tempo, é possível fazê-la com uma maior precisão.
23
2.1 PROCESSAMENTO PARALELO
O grande segredo dos supercomputadores é o seu alto poder de
processamento. Além de muita memória, eles possuem inúmeros processadores
que operam juntos. O processamento paralelo possibilita ao computador fazer várias
tarefas simultaneamente, mas isto só é possível se a arquitetura possuir mais de um
processador que realize as tarefas em paralelo.
Para que todos esses processadores trabalhem em cooperação, é
necessário que, em primeiro lugar, tenham algum tipo de comunicação entre si.
Essa comunicação pode ser direta como se fosse um computador só contendo todos
os processadores ou através de uma conexão de rede entre vários computadores.
Mas não basta se interligar computadores para ter um supercomputador. É preciso
que o sistema operacional em cada um deles seja preparado para isso, e ainda que
haja um software central para distribuir as tarefas entre todos eles. Ou seja, é
preciso ter um software preparado para operar sobre uma arquitetura de
multiprocessadores. (GEEK, 2011).
Um processo é definido como um programa em execução. Todo processo
possui um fluxo de execução. Por sua vez, uma thread nada mais é do que um fluxo
de execução. Na maior parte das vezes, cada processo é formado por um conjunto
de recursos mais uma única thread e é justamente este paradigma que deve ser
quebrado para que o software usufrua de todo poder computacional da arquitetura,
acrescentando mais threads para dividir o processamento em pequenas partes, as
quais serão processadas pelos diversos processadores.
O paralelismo pode ser implementado em diversos níveis, são eles:
Paralelismo em nível de instrução(ILP) - é um conjunto de técnicas de
desenvolvimento de processadores e compiladores que aumentam o desempenho
das aplicações, fazendo com que operações sejam executadas em paralelo. No
nível de instruções o paralelismo é explorado por arquiteturas de processadores
capazes de executar instruções ou operar dados em paralelo, como o pipeline de
instruções. (YAMASHIRO E CORREA).
Paralelismo em nível de thread(TLP) – é a utilização de várias linhas de execução
simultâneas (multithreading). Com ele é possível ter várias threads compartilhando,
de forma intercalada, as mesmas unidades funcionais de um processador, onde o
processador duplica o estado independente de cada thread. (MOREIRA et al).
24
Paralelismo em nível de chip(CMP) - o processador possibilita a execução
simultânea de mais de um fluxo de instruções, que é o caso dos processadores
multicore que possuem 2 ou mais núcleos de processamento, lhes atribuindo a
capacidade de executarem blocos de código simultaneamente. Logo, em teoria,
quanto mais núcleos um processador tiver, mais tarefas em paralelo ele será capaz
de executar. Por outro lado, este nível não acelera a execução quando possuímos
apenas 1 tarefa a processar.
A utilização do paralelismo nos projetos de arquitetura de
computadores tem possibilitado um aumento significativo na velocidade de
processamento devido à execução simultânea de diversas tarefas. Contudo,
os aspectos relacionados ao software paralelo e à paralelização dos
programas são essenciais para o desempenho do sistema paralelo.
(FERLIN, 2011).
2.2 ARQUITETURAS DE FLYNN
Segundo a taxonomia de Michael J. Flynn(Taxonomia de Flynn), introduzida
entre 1966 a 197, onde foi definido um dos primeiros sistemas de classificação para
computadores e programas paralelos e sequenciais. Em sua classificação ele
separou as arquiteturas de computadores em 4 classes, que são elas:

SISD (Single Instruction Single Data): Fluxo único de instruções sobre um
único conjunto de dados.

SIMD (Single Instruction Multiple Data): Fluxo único de instruções em
múltiplos conjuntos de dados.

MISD (Multiple Instruction Single Data): Fluxo múltiplo de instruções em um
único conjunto de dados.

MIMD (Multiple Instruction Multiple Data): Fluxo múltiplo de instruções sobre
múltiplos conjuntos de dados. (TANENBAUM, 2001).
2.2.1 SIMD
A sigla SIMD (Single Instruction, Multiple Data), que, em português, quer
dizer Uma Instrução, Múltiplos Dados, descreve um método de operação de
computadores com várias unidades de processamento em paralelo. Neste método, a
25
mesma instrução é aplicada simultaneamente a diversos dados para produzir mais
resultados.
Computadores SIMD são utilizados para a resolução de problemas
computacionalmente intensivos da área científica e de engenharia, em que existem
estruturas de dados regulares como vetores e matrizes. Os mesmos também estão
em uma categoria importante, devido a fatores como: simplicidade de conceitos e
programação, regularidade da estrutura, facilidade de escalabilidade em tamanho e
desempenho, aplicação direta em uma série de aplicações que requerem
paralelismo para obter o desempenho necessário.
2.3 MEDIDAS DE DESEMPENHO
O ponto principal da construção de um computador paralelo é fazer com que
ele execute mais rapidamente do que uma máquina com um único processador. Se
ele não cumprir esse objetivo, não vale a pena tê-lo. Além disso, ele deve cumprir o
objetivo de maneira efetiva em relação ao custo. Uma máquina que é duas vezes
mais rápida do que um uniprocessador a 50 vezes o custo muito provavelmente não
será um sucesso de vendas.
O modo mais direto de adicionar desempenho é adicionar Unidades de
Processamento ao sistema. Contudo, essa adição deve ser feita de um modo tal que
evite a criação de gargalos. Um sistema no qual se pode adicionar mais Unidades
de Processamento e obter mais capacidade de computação correspondente é
denominado escalável. Porém o mais conhecido é o aumento da frequência do
relógio(clock), esta prática produz resultados consideráveis no ganho de
desempenho, porém tem algumas implicações onde se destaca principalmente a
grande dissipação de calor gerado pela unidade de processamento, calor o qual
pode danificar o hardware se não controlado. Sendo este difícil de controlar, logo,
parte-se para outras alternativas.
Vale mencionar que, na teoria, quanto mais núcleos tem uma
CPU, maior o número de transístores e, por consequência, melhor sua
performance. Mas, na prática, isto não acontece pelo principal motivo: o
software está anos atrás do hardware. Uma CPU com 4 núcleos pode
perder em performance nos games pelo fato do software ser otimizados
para 2 núcleos. A programação em paralelo para 4 núcleos significa
aumentar o problema, sem contar na otimização dos compiladores para
fazer uso do paralelismo (CUDA PROGRAME..., 2009).
26
A definição de alto desempenho em computação é característica
fundamental de um Supercomputador. Em 2004 foi dada pela IBM a seguinte
definição a respeito dos supercomputadores:
"Supercomputadores são máquinas construídas sob encomenda, com
capacidade de processamento grande o suficiente para resolver complexos
problemas referentes às aplicações para as quais foram desenvolvidos".
Os supercomputadores são construídos para obter alto desempenho, são
máquinas que possuem alto poder de processamento e sua arquitetura totalmente
voltada para a obtenção de resultados específicos ao tipo de tarefa a ser realizada.
Nesta categoria de computadores encontramos:
 Processadores vetoriais paralelos (PVP);
 Multiprocessadores simétricos (SMP);
 Máquinas massivamente paralelas (MPP);
 Máquinas com memória compartilhada distribuída (DSM);
 Redes de estações de trabalho (NOW);
 E as máquinas agregadas (COW/Cluster).
Estas super máquinas, por assim dizer, se fazem cada vez mais
necessárias, principalmente pelos constantes avanços tecnológicos e do mundo da
pesquisa, onde é indispensável um cenário computacional para que realize as
tarefas que demandem de um maior poder computacional. Mas não basta apenas
ter um supercomputador, pois o que se espera destas “máquinas” é um altíssimo
poder de processamento, onde são capazes de serem realizados milhares de
cálculos de ponto flutuante por segundo, desempenho o qual não costumamos ver
em qualquer computador, principalmente naqueles que usamos em casa, os
conhecidos PCs.
O poder de processamento dos supercomputadores sempre se deu ao
grande número de processadores empregados na arquitetura. Tal metodologia já fez
muitas pessoas pensarem, e movidos pelo desejo de um desempenho sempre
maior, chegaram a uma simples palavra, GPU.
Existem alguns supercomputadores com desempenho espantoso, mas
costumam ocupar salas inteiras de grandes laboratórios, além de representarem
investimentos milionários.
27
Em 2009, a equipe do grupo de pesquisa ASTRA, do Vision Lab, na
Universidade da Antuérpia, na Bélgica, anunciou a criação do FASTRA II, um
supercomputador de mesa, equipado com um processador Intel Core i7 e nada
menos que sete placas de vídeo NVIDIA. Como resultado, o equipamento atinge um
desempenho de 12 teraflops (UOL, 2009).
O hardware foi montado dentro de um gabinete de PC comum e suas
especificações incluem um processador Intel Core i7 920, 12 GB de RAM, 1 TB de
espaço em disco, uma placa de vídeo GeForce GTX 275 e seis "dual GPU" GeForce
GTX 295, totalizando 13 GPUs. Tudo isso ligado a uma placa-mãe ASUS P6T7 WS
em um gabinete Lian-Li PC-P80. (UOL, 2009).
Para aguentar todo este hardware, os cuidados com a energia não ficaram
de fora. O supercomputador conta com quatro fontes de alimentação. Além disso, o
sistema ainda precisou de uma BIOS especial, desenvolvida em colaboração com a
ASUS. (UOL, 2009).
Para quem pensa que o supercomputador custou um valor extraordinário,
está enganado, o valor total dele não ultrapassou os R$18 mil e todo design da
arquitetura do projeto é aberto, ou seja, quem dispor deste valor pode recriar o
projeto em casa, sem contar que a equipe fornece todo o esquema do hardware e a
cópia do software.
2.3.1 FLOPS
FLOPS é um acrônimo na computação que significa FLoating-point
Operations Per Second, que, em português, quer dizer operações de ponto flutuante
por segundo. Esta é uma medida de desempenho que indica o número de
operações e cálculos de ponto flutuante que um computador é capaz de executar
por segundo, mais especificamente no campo de cálculos científicos, que fazem
grande uso de cálculos com ponto flutuante. Por exemplo, um teraflop corresponde a
um trilhão de operações por segundo, hoje já estamos ultrapassando esta marca e
chegando a escala de petaflops, 1015 instruções por segundo. Mais unidades de
medidas de FLOPS estão listadas na tabela 1.
Tabela 1: Unidades de Medida de Desempenho
Computador Desempenho
28
Nome
FLOPS
megaflop
105
gigaflop
109
teraflop
10¹²
petaflop
1015
exaflop
1018
zettaflop
1021
yottaflop
1024
2.4 STREAM PROCESSORS
Stream Processors é o resultado da unificação de duas arquiteturas, o Pixel
Shader e o Vertex Shader. Antigamente os processos de Pixel Shader e Vertex
Shader eram feito de modo separado. O Pipeline ou processava somente Pixel
Shader, ou somente Vertex Shader. As unidades de Vertex só processavam
vértices, as de pixel apenas aplicavam shaders em pixels. O problema deste modelo
é que as aplicações podem exigir mais operações de Pixel Shader do que de Vertex
Shader e/ou vice-versa, logo, ocorre ociosidade do conjunto para o processo das
instruções. (Tudo sobre Arquitetura..., 2009).
Com o surgimento dos Stream Processors este conceito mudou, pois em vez
de fazer o processamento separadamente, ele unificou em paralelo o tratamento dos
vértices e pixels, ou seja, um stream processor pode então processar tanto Pixel
Shader como também Vertex Shader.
Basicamente, cada uma destas unidades age como um pequeno
processador de cálculos de ponto flutuante independente, que pode ser programado
para executar praticamente qualquer tipo de operação. Isso abriu as portas para o
uso da GPU como processador auxiliar. (MORIMOTO, 2010).
2.5 GREEN IT
Cada vez mais, empresas de todo o mundo estão se preocupando com o
desenvolvimento sustentável da sociedade e não a enxergam como uma boa ação,
mas como obrigação que é cobrada pelos consumidores de seus produtos e, na
29
computação, isso não é diferente. A área da tecnologia da informação tem uma
grande preocupação com a sustentabilidade, o reaproveitamento e economia
energética e estas ações são conhecidas como Green Computing, Green IT (ou TI
Verde no Brasil).
O termo Green IT começou a ser usado em 1992, depois que a Agência de
Proteção Ambiental dos Estados Unidos desenvolveu o projeto “Energy Star” com o
objetivo de reconhecer a eficiência energética de diversas tecnologias.
Green IT significa trabalhar o que produzimos na computação em questão
de dispositivos, hardwares e até mesmo softwares de modo que eles consigam ser
reaproveitados, remanejados e reciclados facilmente. Green IT abrange desde se
produzir equipamentos com uma melhor eficiência energética ao desenvolvimento
de softwares mais eficientes, mas que exijam menos de seu hardware.
Se os níveis atuais de consumo forem mantidos, os gastos com eletricidade
podem chegar a 50% dos orçamentos de tecnologia de uma grande empresa,
segundo estima a consultoria Gartner Group. Não é por acaso que um dos principais
motores da inovação no mundo da computação é a revolução verde. A Gartner
calcula que o desperdício de energia nessas instalações possa chegar a 60%, e é
justamente esse ponto que tem sido alvo da atenção da indústria tecnológica.
Desde o início desta década, com a expansão da internet e a
crescente digitalização dos negócios, o número de servidores em uso no
mundo passou de 6 milhões para 28 milhões. A maioria deles fica em
grandes data centers, que podem ocupar áreas de até 5.000 metros
quadrados e consomem até 50 vezes mais energia elétrica do que um
escritório tradicional. (Teixeira, 2007).
O consumo energético é um dos principais desafios para a TI a nível
empresarial. O consumo de energia dos datacenters representa 1,5% de todo o
consumo de eletricidade nos EUA. Essas empresas procuram cada vez mais
soluções que lhes permitam reduzir os custos energéticos e o consumo, ao mesmo
tempo em que mantêm os níveis de serviço e aumentam a capacidade de resposta
para que o negócio não pare.
As CPUs são os componentes que mais consomem energia em servidores,
portanto, os modelos de CPU de energia eficientes com uma gestão eficaz de
energia podem ajudar muito na eficiência.
30
A nova arquitetura Kepler tem como objetivo não só criar a GPU mais rápida
do mundo, mas também a GPU com maior desempenho e eficiência energética. Sua
fabricação em 28nm é um dos importantes fatores pelo baixo consumo da nova
arquitetura sem deixar de lado a alta performance. A economia de energia é algo
que realmente impressiona na Kepler. Comparada à antecessora, a energia ativa foi
reduzida em 15%, e o desperdício em 50%, o que resultou em uma melhora global
de 35%. (LEPILOV E ZABELIN, 2012).
O novo design do SM (Streaming Multiprocessor) que recebeu o nome de
"SMX" foi especialmente redesenhado para obter a maior eficiência em performance
por Watt, oferecendo 3x mais performance por Watt que sua antecessora, assim
como demonstra a figura 2.
Figura 2: Novo design SM.
Fonte: MORGAN, 2012
Estes novos avanços da tecnologia sobre as GPUs comprovam sua grande
eficiência aliada ao baixo consumo de energia, geralmente isto também está
associado a menor frequência de clock que elas possuem, porem o grande número
de núcleos compensa esta perda e mais, até supera as CPUs no poder de
processamento em certas operações. Como prova disso, temos o novo
supercomputador da Petrobras que mudou seu paradigma e partiu para uma nova
tecnologia. Este novo supercomputador usa nada menos que 180 GPUs e em geral
31
ele é muito mais rápido que o anterior e consome 90% menos energia, sem contar a
falta de espaço que estavam sofrendo e também o supercomputador TITAN, que
possui 10 vezes mais desempenho e 5 vezes mais eficiência energética que seu
predecessor JAGUAR.
32
3 HARDWARE
O hardware é um dos principais temas abordados por este trabalho, por se
tratar da peça fundamental e responsável pelo ganho de desempenho proposto.
Portanto, este capítulo aborda as caraterísticas e peculiaridades de ambas
arquiteturas estudadas.
3.1 CPU
A CPU (Unidade Central de Processamento), ou então processador como é
mais conhecido pela comunidade em geral, é responsável por fazer a maior parte do
processamento dos dados do computador e por isso é peça fundamental dos
computadores. Por esses e outros fatores ele é considerado o componente mais
complexo e frequentemente o mais caro de um computador. (MORIMOTO, 2007).
Em 1965, Gordon Moore, um dos fundadores da Intel, afirmou que o número
de transistores em um chip dobraria, sem custo adicional, a cada 18 meses. Tal
afirmação ficou conhecida como a Lei de Moore, a qual foi válida durante anos,
principalmente no final da década de 90. (ARRUDA, 2011).
Conforme a tecnologia dos processadores foi evoluindo, o tamanho de seus
transistores foi diminuindo de forma significativa. Porem, após o lançamento do
Pentium 4, eles já estavam tão pequenos e numerosos que se tornou muito difícil
aumentar o clock por limitações físicas, principalmente pelo superaquecimento
gerado. (ARRUDA, 2011).
A principal solução para esse problema veio com o uso de mais de um
núcleo(core) ao mesmo tempo, através da tecnologia multicore. Assim, cada núcleo
não precisa trabalhar numa frequência tão alta. Um processador dual-core de 1,5
GHz, por exemplo, poderia ter um desempenho semelhante a uma CPU de núcleo
único de 3 GHz se o sistema de escalonamento fosse mais eficiente. Mas não parou
por aí, a tecnologia continuou expandindo e hoje temos processadores com diversos
núcleos em um único chip.
Muito embora esta não fosse a primeira tecnologia de processamento
paralelo em nível de hardware, ela causou uma verdadeira revolução no paralelismo.
Devido a este avanço temos hoje supercomputadores com mais de um milhão de
33
núcleos de processamento, mas não necessariamente o mesmo número de
processadores, oque seria inviável.
Lançando-se então para o paralelismo em nível de hardware com núcleos de
processamento diversos e em larga escala, temos as GPUs.
3.2 GPU
A própria NVIDIA define Unidade de Processamento Gráfico (GPU) como:
“Processadores massivamente paralelos”. Você pode pensar na GPU como blocos
de vários pequenos processadores trabalhando em paralelo, como pode ser
observado na figura 3.
Figura 3: Diferença de cores entre uma CPU e uma GPU.
Fonte: CAVERNADATI, 2011
Usadas, normalmente para cálculos gráficos, operações geométricas e
operações de ponto flutuante, ou seja, processamento gráfico e jogos, em PCs,
estações de trabalho, consoles de jogos. Mas somente a partir das séries 8, Tesla e
Quadro da NVIDIA, e com a introdução da CUDA, as GPUs se tornaram
programáveis, sendo capazes de executar aplicativos comuns e, com isso, através
da modificação de algumas aplicações para algoritmos paralelos, conseguiram uma
maior performance e escalabilidade dessas aplicações, resultando então em um alto
poder de processamento designado principalmente a computadores de grande
porte, ou seja, supercomputadores.
34
A filosofia do design das GPUs são significativamente diferentes,
já que as placas de vídeo devem ser capazes de executar um número
massivo de operações de ponto flutuante por frames de vídeo. Para isto,
esses hardwares executam um número muito grande de threads
simultanemante, enquanto um outro grande número de threads aguarda a
latência de memória, minimizando assim o controle lógico de execução de
tarefas. Ainda nessa arquitetura, pequenos cache de memória são utilizados
para auxiliar o controle de memória, evitando que diferentes threads
acessem várias vezes a memória DRAM. Consequentemente, mais áreas
de chips são utilizadas para operações de ponto flutuante. (SANTOS, 2011).
Tudo isso se deu devido ao alto custo e esforço de desenvolvimento
envolvidos na programação das GPUs, pois o hardware não era flexível e não se
adaptava facilmente a evolução de novos algoritmos. Com o objetivo de atender a
esta demanda por flexibilidade, surge a motivação para o uso de Stream Processors
programáveis e dos Pixel Shaders.
O modelo para computação com GPU é baseado em uma CPU e uma GPU
juntas em um modelo de computação heterogêneo “co-processamento”. A parte
sequencial do aplicativo é executado na CPU e na parte de computação intensiva é
acelerada pela GPU. Do ponto de vista do usuário, o aplicativo apenas corre mais
rápido, porque ele está usando a grande quantidade de SPs da GPU para aumentar
o desempenho. Esta forma de uso das GPUs para propósitos gerais é chamada de
GPGPU (General Purpose Computation on Graphics Processing Units).
Desse modo, como a GPU é um processador vetorial massivamente paralelo
onde é executada uma instrução em múltiplos conjuntos de dados, faz ela uso da
classificação de Flynn, SIMD.
Na figura 4 é possível observar uma comparação entre um chip de CPU e
GPU, os núcleos ou cores os quais são tão mencionados, aqui estão ilustrados em
quadrados verdes.
35
Figura 4: Comparação entre chips CPU e GPU.
Fonte: IXBTLABS, 2008
No ano passado a Intel apresentou o seu Knights Corner, um coprocessador que permite alcançar a marca de 1 teraflops - 1 trilhão de operações de
ponto flutuante por segundo. O chip não é um processador comum, é um coprocessador, cuja função é tirar do processador a incumbência de realizar os
cálculos matemáticos mais intensivos. Em nota a Intel também publicou que “este
novo chip parece ser o primeiro a fazer frente às GPUs, os processadores gráficos
da NVIDIA, que estão dominando o mercado dos chamados supercomputadores de
baixo custo”.
O Knights Corner tem nada menos do que 50 núcleos em um único chip e,
segundo a empresa, já incorpora a especificação PCI Express 3.0, que eleva a
transferência de dados para 32 gigabytes por segundo. (INTEL, 2011).
3.2.1 GPGPU
GPGPU significa computação de proposito geral em unidades de
processamento gráfico, também conhecida como computação em GPU. Uma vez
projetadas especialmente para computação gráfica e difíceis de programar, as GPUs
de hoje são processadores paralelos de uso geral com suporte para interfaces de
programação e linguagens padrão da indústria. (GPGPU.ORG).
Paralelismo dinâmico em GPUs Kepler, dinamicamente gera novas threads,
adaptando-se os dados sem voltar para a CPU, simplificando e acelerando
programação em GPU e acelerando um conjunto amplo de algoritmos populares.
Uma plataforma multi-GPU representada por uma ou mais GPUs é capaz de
realizar a computação heterogênea, aproveitando a capacidade de computação
paralela dos múltiplos núcleos das GPUs para fornecer um aumento muito grande
no desempenho com a complexidade de programação mínima. Enquanto aumenta
36
muito a capacidade funcional, a plataforma GPGPU também oferece o desempenho
com tamanho muito menor, peso e potência. Isso resulta em economias
significativas no custo, risco e tempo de colocação no mercado. (GE-IP.COM).
A próxima geração de aceleradores gráficos de proposito geral está prestes
a bater o mercado e mostram um novo foco em consumo de energia. Se é medido
como GFLOPS / Watt ou picoJoules por instrução, e se a tecnologia está sendo
aplicada a bateria óculos de visão noturna, grandes problemas exascale, a
quantidade de energia consumida e a quantidade de calor a ser rejeitado é cada vez
mais importante. A mudança de ênfase dos fornecedores de GPU reflete isso, e
permitirá novas aplicações em ambas as extremidades do espectro do tamanho do
sistema. (GE-IP.COM).
Mike Houston da Universidade de Stanford abordou em seu trabalho o que
aplicativos ideais para operarem com GPGPU devem ter:

Grandes conjuntos de dados.

Alto Paralelismo.

Dependências mínimas entre elementos de dados.

Intensidade aritmética alta.

Muito trabalho para fazer, sem intervenção da CPU.
Arnaldo Tavares, executivo responsável pela linha Tesla da NVIDIA na
América do Sul, em uma palestra NVIDIA sobre processamento paralelo em GPUs
na arquitetura Fermi, fez uma breve explicação sobre a GPU TESLA:
Tesla, são unidades de processamento gráfico (GPUs) da NVIDIA
criadas especialmente para supercomputadores que rodam em alta
performance e que necessitam de precisão extrema em seus resultados.
Centros de pesquisas (medicina, biomecânica, astronomia), setor
petrolífero e entidades financeiras, são instituições que necessitam da
tecnologia para melhorarem o desempenho de seus trabalhos. (TAVARES,
2011).
37
Figura 5: NVIDIA TESLA M2090 .
Fonte: http://www.NVIDIA.in/object/why-choose-tesla-in.html
As GPUs são tão potentes por possuírem maior número de transistores para
processamento que para caching de dados e controle de fluxo. Basicamente, a
diferença fundamental entre as CPUs e as GPUs é o fato de que as CPUs são
otimizadas para cálculos sequenciais, executando aplicativos diversos, enquanto as
GPUs são otimizadas para cálculos massivamente paralelos, processando gráficos
3D.
Os testes desta pesquisa foram realizados sobre uma NVIDIA GTX 465, a
qual é otimizada para aplicações 3D. A linha de GPUs Tesla da NVIDIA é uma linha
otimizada para GPGPU, estas GPUs são utilizadas principalmente em grandes
servidores e supercomputadores. Esta linha possui um desempenho superior aos
outros modelos, conforme a figura 6 publicada pela NVIDIA:
38
Figura 6: Desempenho NVIDIA Tesla.
Fonte: http://www.NVIDIA.in/object/why-choose-tesla-in.html
3.2.2 Projeto Denver
O Projeto Denver é o codinome de uma iniciativa da NVIDIA que apresenta
uma CPU NVIDIA executando um conjunto de instruções ARM, que será totalmente
integrada no mesmo chip como a GPU NVIDIA.
O Projeto Denver vai inaugurar uma nova era para a computação, alargando
a gama de desempenho da arquitetura de instruções ARM, permitindo que a
arquitetura ARM cubra uma maior parte do espaço da computação. Acoplada a uma
GPU NVIDIA, que irá fornecer a plataforma de computação heterogênea do futuro,
combinando uma arquitetura padrão com desempenho impressionante e eficiência
energética.
ARM já é a arquitetura padrão para dispositivos móveis. O Projeto Denver
amplia a gama de sistemas ARM para cima de, PCs, servidores de data centers, e
supercomputadores. Um anúncio da Microsoft revela estar trazendo o Windows para
processadores de baixo consumo de energia, como CPUs baseadas em ARM, este
era o ingrediente final necessário para permitir PCs com arquiteturas ARM.
Um processador ARM juntamente com uma GPU NVIDIA representa a
plataforma de computação do futuro. Uma CPU de alta performance com um
conjunto de instruções padrão irá executar as partes seriais das aplicações e
39
fornecer compatibilidade enquanto uma GPU altamente eficiente e paralela irá
executar as partes paralelas dos programas.
O resultado é que os futuros sistemas vão integrar uma excepcional
combinação de desempenho e eficiência de energia. Seus processadores irão
fornecer o melhor dos dois mundos, permitindo maior vida útil da bateria para
soluções móveis. (DALLY, 2011).
3.2.3 Evolução das Placas Gráficas NVIDIA
Na figura 7 é possível observar a evolução das GPUs durante os anos, estes
chips gráficos começaram como processadores de vídeo com função fixa, mas se
tornaram cada vez mais programáveis e poderosos em termos de computação, o
que levou a NVIDIA a lançar a primeira GPU. No período entre 1999 e 2000,
cientistas de computação e cientistas de campo em diversas áreas começaram a
usar GPUs para acelerar uma variedade de aplicativos científicos. Esse foi o início
do
movimento
chamado
de GPGPU,
ou
Computação
de
Uso
Geral
na
GPU. (NVIDIA).
FIGURA 7: Evolução Das GPUs.
Fonte: CAVERNADATI, 2011
Jen-Hsun Huang CEO e co-fundador da NVIDIA em uma palestra em 2010,
falou sobre o sucesso das GPUs, do CUDA e da importância da computação
paralela. Além disso ele também demonstrou, através da figura 8, a evolução da
arquitetura das placas gráficas da NVIDIA e o futuro da mesma.
40
Figura 8: NVIDIA GPU Roadmap.
Fonte: http://www.geeks3d.com/20111126/NVIDIA-kepler-gpus-roadmap-gk107-gk106-gk104-gk110and-gk112/
Atualmente estamos com a arquitetura Kepler, no próximo ano teremos a
Maxwell, que conforme Jen-Hsun, vai acrescentar cerca de 16 vezes a performance
de precisão dupla da Fermi.
O ano de 2012 foi o ano da Kepler, com diversos lançamentos de GPUs,
principalmente no segmento desktop (GK112, GK107, GK106, GK104) com chips
produzidos a um processo de apenas 28nm e oferecendo mais de 1 TFLOPS de
processamento. Em 2013 a nova arquitetura Maxwell promete uma arquitetura ainda
menor, com apenas 22nm oque resultará em um ganho de performance de 16 vezes
superior à da Kepler, 10-12 vezes mais veloz em aplicações “convencionais” e 40
vezes mais potente que a Fermi além de ser a primeira arquitetura a efetivamente
usar o Projeto Denver. (HUANG, 2010).
A capacidade de computação (Compute Capability) descreve os recursos
suportados por uma GPU NVIDIA com suporte a CUDA. O primeiro hardware com
suporte a CUDA, como a GeForce 8800 GTX, tem uma capacidade de computação
(CC) de 1.0, já as mais recentes como a Tesla K20 tem um CC de 3.5, conforme a
figura 9.
41
Figura 9: Tabela De Especificações.
Fonte: http://www.pcper.com/reviews/Graphics-Cards/NVIDIA-Reveals-GK110-GPU-Kepler-71BTransistors-15-SMX-Units
Impossível falar desta maravilhosa tecnologia sem citar um grande caso de
sucesso. Relativamente há pouco tempo atrás a Petrobras abriu uma licitação para
compra de equipamentos que serviriam para montar seu novo supercomputador e
teve como vencedora a empresa ITAUTEC. (Petrobras monta supercomputador...,
2011).
Luiz Rodolpho Rocha Monnerat, analista de sistemas sênior da Petrobras,
que encabeçou o projeto, afirma que a nova tecnologia permitirá à Petrobras
aumentar em dez vezes a capacidade de processamento de imagens de áreas com
potencial de produção de gás e óleo. O upgrade se deu principalmente devido a falta
de espaço no atual CPD e ao alto consumo de energia do antigo sistema. O novo
supercomputador denominado Grifo4 é constituído por 544 servidores com um total
de 1 petaflop de capacidade de processamento e ocupa a 68ª posição no ranking
dos 500 melhores supercomputadores do mundo conforme o site Top500.org. Mas
oque mais marcou na evolução do CPD da Petrobras foi a mudança do paradigma
de processamento, integrando ao sistema nada menos que 180 GPUs NVIDIA Tesla
2050. Conforme Monnerat, o parque de computação de alto desempenho tem saltos
tecnológicos a cada dez anos. A mudança atual é a substituição de CPUs pelas
42
GPUs. "Nossa meta é acompanhar esses saltos tecnológicos", afirma Monnerat. A
Petrobras começou a testar GPUs em 2006. O primeiro cluster foi feito em 2008,
com 180 placas gráficas.
Bernardo Fortunato Costa, analista de sistemas júnior da Petrobras, afirma
que os maiores problemas se deram devido aos softwares que ainda não eram
adaptados para o processamento com as GPUs e que esse foi um dos fatores que
levaram a companhia a desenvolver os programas internamente.
Além do poder computacional aumentado pelo novo supercomputador, a
mudança também se refletiu no consumo energético, o qual reduziu em 90%.
3.4 APU
Relativamente recente, o termo APU(Unidade de Processamento Acelerado)
foi comentado inicialmente durante a Computex de 2010 e relembrado durante a
durante a CES de 2011, onde a AMD demonstrou os novos chips e anunciou seu
novo produto revolucionário de processamento paralelo batizado de Fusion.
“Acreditamos que o Fusion é simplesmente o maior avanço em processamento
desde a introdução da arquitetura x86, há mais de quarenta anos”, disse Rick
Bergman, vice-presidente sênior e gerente geral do Grupo de Produtos da AMD,
durante a CES de 2011.
Segundo a detentora da marca, uma APU é um chip híbrido composto por
uma CPU x86 e uma GPU, conforme a figura 10. Tal tecnologia permite um aumento
na taxa de transmissão de dados entre ambos os dispositivos e uma expressiva
diminuição no consumo de energia, o que é muito importante principalmente quando
lembramos que o Fusion foi projetado inicialmente para portáteis, tais como
notebooks.
43
Figura 10: Arquitetura de uma APU.
Fonte: http://mecanicadocomputador.webnode.com.pt/album/galeria-de-fotos-paginainicial/processador-cpu-gpu-1-jpg/
Grande parte da experiência de computação está conectada ao
software e, até agora, os desenvolvedores de software têm sido limitados
pela natureza independente com a qual as CPUs e GPUs processam a
informação. No entanto, as APUs AMD Fusion removem este obstáculo e
viabilizam que os desenvolvedores aproveitem totalmente a capacidade de
processamento incomparável de uma GPU — mais de 500 GFLOPs na ASeries “Llano” APU(v) — proporcionando uma performance do tipo de
supercomputador para as tarefas de computação diárias. Mais aplicativos
podem ser executados simultânea e mais rapidamente do que os designs
anteriores da mesma categoria. (INÍCIO DA ERA DA APU..., 2011).
3.5 COMPARAÇÃO
O crescimento da frequência das CPUs é agora limitado por questões físicas
e consumo elevado de energia. Seu desempenho é muitas vezes elevado pelo
aumento do número de núcleos.
Processadores atuais podem contem em torno de quatro núcleos, porem
eles são projetados para aplicações comuns, baseados na arquitetura MIMD
(múltiplas
instruções
sobre
dados
múltiplos). Cada
núcleo
funciona
independentemente dos outros, executando várias instruções diferentes para os
vários processos. Estes núcleos são projetados para executar um único segmento
de instruções sequenciais, com velocidade máxima.
Características especiais de vetor (SSE2 e SSE3) para vetores de ponto
flutuante simples e de precisão dupla apareceram em processadores de propósito
44
geral por causa do aumento das exigências de aplicações gráficas em primeiro
lugar. É por isso que é mais conveniente usar GPUs para determinadas tarefas.
Além das centenas de núcleos das GPUs, elas contêm uma memória global
rápida, que pode ser acessada por todos os multiprocessadores, memória local em
cada multiprocessador e memória especial para constantes. O mais importante é
que esses vários núcleos em uma GPU são SIMD (Uma instrução sobre múltiplos
dados). E esses núcleos de executam as mesmas instruções simultaneamente, pois
foram projetados para a execução rápida de várias threads de instrução
paralelas. Este estilo de programação é comum para algoritmos gráficos e muitas
tarefas científicas. (IXBTLABS, 2009).
Também vale lembra que o crescimento anual da tecnologia da arquitetura
das CPUs é de apenas 40% se comparado as GPUs que é de 130%.
45
4 SOFTWARE
Apenas o hardware por si só não é capaz de nos fornecer o desempenho
esperado, portanto, para a arquitetura ser completa ela é dependente de um
software que demande destes recursos de hardware, mas acima disso, o software
deve tratar deste ambiente de hardware de forma a extrair o máximo de
desempenho dele, fazendo uso de todos seu recursos.
4.1 LINGUAGENS DE PROGRAMAÇÃO
Esta seção foca única e exclusivamente no nível de linguagens de
programação, por se tratarem da base para se criar um software. A linguagem de
programação a ser utilizada em uma aplicação de alto desempenho é um fator
crucial, pois assim como o hardware, a linguagem também deve proporcionar muito
desempenho, como a linguagem C, que fornece uma iteração próxima ao hardware.
4.1.1 C/C++
A linguagem C foi criada por Dennis Ritchie, em 1972, no centro de
pesquisas da Bell Laboratories, é uma das linguagens de programação mais
populares e existem poucas arquiteturas para as quais não existem compiladores.
Sua primeira utilização importante foi a reescrita do Sistema Operacional UNIX, que
até então era escrito em assembly.
C é uma linguagem de programação compilada, estruturada, imperativa,
procedural, de propósito geral e padronizada pela ISO. C foi útil para muitas
aplicações que foram codificadas originalmente em Assembly, fornecendo acesso de
baixo nível a memória e ao hardware em geral, mas também foi desenvolvido para
ser uma linguagem de alto nível, para maior reaproveitamento do código.
A linguagem C++, além das características do próprio C, ela é considerada
uma linguagem de nível médio, pois combina características de linguagens de alto e
baixo nível. É uma das linguagens comerciais mais populares, sendo bastante usada
também na academia por seu grande desempenho e base de utilizadores.
46
O C++ foi desenvolvido como um adicional a linguagem C por Bjarne
Stroustrup em 1983 no Bell Labs. Novas características foram adicionadas com o
tempo, como funções virtuais, sobrecarga de operadores, herança múltipla,
gabaritos, tratamento de exceções e também implementou vários elementos chave
de programação orientada a objetos.
4.1.2 Java
O Java, além de uma linguagem de programação de alto nível, também é
uma plataforma. Linguagem orientada a objeto, com sintaxe parecida ao C,
desenvolvida na década de 90 por uma equipe de programadores chefiada
por James Gosling, na empresa Sun Microsystems, mas atualmente é mantida pela
Oracle.
Na linguagem de programação Java, todo código fonte é primeiramente
escrito em arquivos de texto simples que terminam com a extensão .java. Estes
arquivos por sua vez são compilados em arquivos .class pelo compilador javac.
Uma arquivo .class não contém código que é nativo para o processador, ao invés
disso ele contém bytecodes que por sua vez é a linguagem de máquina da Java
Virtual Machine, como pode ser observado na figura 11. A ferramenta de execução
Java então executa sua aplicação com uma instância da máquina virtual Java.
(ORACLE).
É este arquivo pré-compilado .class com os bytecodes que fornece a
portabilidade ao Java. Este mesmo arquivo pode ser executado tanto em um
ambiente Windows quanto Linux ou Mac, como demonstra a figura 12.
Figura 11: Plataforma Java.
Fonte: http://docs.oracle.com/javase/tutorial/getStarted/intro/definition.html
47
Como um ambiente independente de plataforma, a plataforma Java pode ser
um pouco mais lenta do que o código nativo. No entanto, os avanços no compilador
e tecnologias de máquinas virtuais estão trazendo mais perto o desempenho do
código nativo sem ameaçar a portabilidade.
Figura 12: HelloWord Java.
Fonte: http://docs.oracle.com/javase/tutorial/getStarted/intro/definition.html
4.2 DESENVOLVIMENTO PARA GPU
Desenvolver uma aplicação voltada a utilizar os recursos de uma GPU é
uma tarefa árdua, por se tratar de um paradigma diferente que requer muita atenção
e trabalho ao utilizar o hardware. Nestes casos geralmente são utilizadas APIs as
quais fornecem um desenvolvimento de alto nível ao hardware, como são os casos
do CUDA e da APARAPI.
4.2.1 CUDA
48
A NVIDIA, detentora da marca, define CUDA (Compute Unified Device
Architecture) como uma arquitetura de computação paralela de propósito geral que
tira proveito do mecanismo de computação paralela das unidades de processamento
gráfico (GPUs) NVIDIA para resolver muitos problemas computacionais complexos
em uma fração do tempo necessário em uma CPU. Ela é uma extensão para a
linguagem de programação C, desenvolvida pela NVIDIA, que possibilita o uso de
computação paralela. A ideia por trás disso tudo é que programadores possam usar
os poderes da unidade de processamento gráfico (GPU) para realizar algumas
operações mais rapidamente. Ou seja, a GPU passa a operar como se fosse mais
uma
CPU
dentro máquina,
aumentando
consideravelmente,
com
isso,
a
performance do sistema.
A CUDA também é conhecida como a arquitetura de computação paralela
da NVIDIA que também já há algum tempo tem sido aclamada como "A
Supercomputação para as Massas". Esta API é muito famosa entre os acadêmicos
da computação e chegou também a ser usada no programa Exascale, do
Departamento de Defesa dos Estados Unidos. Mas não é só o desempenho o
responsável por tanta popularidade: o preço dá o golpe final nessa luta. Com todo o
seu poder de computação, as placas gráficas são incrivelmente baratas. Como
observou Sharon Glotzer, da Universidade de Michigan,: "Hoje você pode ter dois
gigaflops por US$500. Isso é ridículo." (PFISTER, 2010).
Segundo a NVIDIA, a CUDA permite utilizar recursos dos aceleradores
NVIDIA utilizando chamadas em C (C for CUDA, compilador nvcc), o que torna o
processo como um todo relativamente fácil para os BONS PROGRAMADORES.
Existem também abstrações para a linguagem Java, C# e também Python.
O processamento paralelo da GPU permite executar mais ações com menos
tráfego de informações em barramentos, usando a área de cache comum e acesso
direto a memória.
Segundo a NVIDIA, com a utilização da API OpenMM na química
computacional que desenvolve cálculos moleculares, é possível desenvolver estes
cálculos de uma forma mais simples e, principalmente, integrada com as GPUs.
Logo o trabalho que uma CPU processaria em dias, poderá ser processado em
horas, ou até mesmo minutos, conforme as figuras 13, 14 e 15.
49
Figura 13: Comparação de desempenho.
Fonte: http://www.NVIDIA.com/object/citizenship-report-science-medicine.html
A GPU se torna mais apta para o trabalho de processamento
paralelo por ter sido desenvolvida para atender à demanda de processos de
computação 3D em alta resolução e em tempo real. Assim, com o passar do
tempo, as GPUs modernas se tornaram muito eficientes ao manipular
grandes blocos de informações (O QUE É A TECNOLOGIA..., 2011).
O processamento na GPU é feito dentro dos núcleos CUDA
(CUDA Cores), que podem ser comparados, a grosso modo, com os
núcleos de um processador comum. Por isso, quanto mais núcleos CUDA
tiver a placa de vídeo, mais operações podem ser realizadas em paralelo (O
QUE É A TECNOLOGIA..., 2011).
Na questão da Dinâmica de Fluídos, a NVIDIA divulga vários projetos em
andamento sobre modelos de Navier-Stokes e métodos de Lattice Boltzman, os
quais mostraram grandes ganhos de velocidade usando GPUs habilitadas para
CUDA.
50
Figura 14: Modelo de Navier-Stokes.
Fonte: http://www.NVIDIA.com/object/computational_fluid_dynamics.html
Figura 15: Método de Lattice Boltzman.
Fonte: http://www.NVIDIA.com/object/computational_fluid_dynamics.html
O modelo de programação CUDA implica em agrupar as threads em grupos
que são chamados de blocks que comunicam-se entre si através da memória
compartilhada, os blocks por sua vez também são agrupados e chamados de grids.
Um programa (kernel) é executado através de uma grid de blocks de threads,
conforme a figura 16. Uma grid é executada de cada vez. Cada bloco pode ser de
uma, duas, ou três dimensões, de forma, que pode consistir de 1024 threads
51
dependendo do equipamento, como demonstra a figura 16. (GRAMMELSBACHER E
MEDRADO, 2009).
Figura 16: Modelo de Programação CUDA.
Fonte: IXBTLABS, 2008
Blocos de threads são executados sob a forma de pequenos grupos
chamados warps. É o volume mínimo de dados, que podem ser processados por
multiprocessadores. Mas o CUDA também permite trabalhar com blocos contendo
outros números de threads. (IXBTLABS, 2008).
O modelo de memória CUDA permite endereçamento bytewise com suporte
para reunir e dispersar. Cada stream processor possui até 1024 registradores. O
acesso a esses registradores é muito rápido, eles podem armazenar 32 bits de
números inteiros ou de ponto flutuante. Assim como mostra a figura 17, cada thread
tem acesso aos seguintes tipos de memória:
52
Figura 17: Modelo de Memória CUDA.
Fonte: IXBTLABS, 2008
Memória global - o maior volume de memória disponível para todos os
multiprocessadores em uma GPU, com mais de 6 GB em soluções empresariais
modernas. Ela oferece alta largura de banda, mais de 170 GB/s de soluções top da
NVIDIA.
Memória local - volume pequeno de memória, que pode ser acessado apenas por
um stream processor. É relativamente lento, assim como a memória global.
Memória compartilhada - de até 48 Kb de memória, nos melhores modelos da
NVIDIA, compartilhado entre todos os stream processors em um multiprocessador. É
rápida, assim como registradores. Esta memória fornece a interação entre as
threads, é controlada pelos desenvolvedores diretamente e apresenta baixas
latências, ela pode ser usada como uma cache L1 controlável, menos chamadas
para memória global.
Armazenamento constante – pouco maior que a memória compartilhada, ela é
somente leitura para todos os multiprocessadores, bastante lenta.
53
Memória
de
textura -
está
disponível
para
a
leitura
de
todos
os
multiprocessadores. Os dados são obtidos por unidades de textura em uma GPU, de
modo que os dados podem ser interpolados linearmente, sem custos extras, lenta
como a memória global.
Figura 18: Memórias CUDA.
Fonte: IXBTLABS, 2008
Na figura 18, é possível observar que CUDA implica em uma abordagem
especial para o desenvolvimento, um pouco diferente da programação da CPU. É
preciso ter a consciência de tipos de memória diferentes, o fato de que a memória
local e global não está em cache e que suas latências de acesso são muito mais
elevadas do que na memória de registrador, como é fisicamente localizado em chips
separados. Vale ressaltar que estes números citados neste tópico são exclusivos da
GPU e estão sujeitos a variações conforme o modelo. Segue um modelo típico de
programação CUDA, mas não obrigatório:

Dividir uma tarefa em subtarefas.

Dividir os dados de entrada em pedaços que se encaixam na memória
compartilhada.

Cada bloco de dados é processado por um thread block.
54

Carregar parte dos dados da memória global na memória compartilhada.

Dados de processamento na memória compartilhada.

Copiar resultados da memória compartilhada de volta à memória global.
Algumas restrições do modelo CUDA (JEONG, 2008):

Não permite recursão.

Não se pode declarar variáveis estáticas dentro do corpo da função.

Não se pode ter um número variável de argumentos.

Não se pode ter ponteiro de função.

Parâmetro de função para uma função kernel é limitado a 256 bytes.
4.2.2 CUDA 5
O CUDA 5 traz diversas novas funcionalidades e aperfeiçoamentos para as
que já existiam, como:

Dynamic Parallelism;

GPU Object Linking;

New Nsight Eclipse Edition;

GPUDirect;
O Paralelismo Dinâmico é a capacidade para qualquer thread de GPU poder
lançar um kernel paralelo nela mesma, de forma dinâmica, simultânea e
independente. Na arquitetura Fermi, apenas a CPU poderia gerar trabalho para a
GPU. A partir da arquitetura Kepler, a GPU pode gerar trabalho para ela mesma,
criando um novo kernel sempre que necessário, como pode ser observado nas
figuras 19 e 20. (HARRIS).
55
FIGURA 19: Paralelismo Dinamico.
Fonte: http://www.NVIDIA.com/object/NVIDIA-kepler.html
Figura 20: Mandelbrot.
Fonte: http://techreport.com/review/22989/a-brief-look-at-NVIDIA-gk110-graphics-chip/2
Talvez a melhor ilustração desta capacidade seja o caso de computação
clássica de avaliação de uma imagem fractal como o conjunto de Mandelbrot. Na
GK110, uma rotina de Mandelbrot poderia avaliar a área de imagem, quebrando-a
em uma malha grossa e verificando para ver quais partes desta grade contem uma
borda. Os blocos que não contêm uma borda não teriam de serem avaliados
56
posteriormente, e o programa pode ampliar as zonas com borda para calcular a sua
forma mais detalhadamente. O programa pode repetir este processo várias vezes,
de cada vez ignorando blocos sem borda e focando mais em blocos com bordas em
si mesmas, de modo a conseguir um resultado de altíssima resolução sem executar
o trabalho e sem ser necessário ficar constantemente retornando para a CPU para
orientação. (TECHREPORT).
GPUDirect é o recurso que permite remotamente acesso direto a memória
entre qualquer GPU do cluster. A versão inicial do GPUDirect suporta comunicação
acelerada com dispositivos de rede e de armazenamento, mas continuou a evoluir,
adicionando suporte para a comunicação peer-to-peer entre GPUs e APIs otimizado
para soluções de vídeo em 2011 e RDMA entre GPUs com a versão 5 do CUDA em
2012. (NVIDIA).
4.2.3 APARAPI
A Parallel API, ou simplesmente APARAPI é uma biblioteca que vem sendo
desenvolvida pela AMD há relativamente pouco tempo. Segundo a detentora, a
biblioteca possui código fonte aberto e seus fins são diversos.
A AMD está revendo as necessidades dos programadores Java, tratando de fazer
com o APARAPI uma implementação contraria e muito diferenciada das existentes,
pois a APARAPI não procura mudar a forma de trabalho nem os métodos de
programação dos desenvolvedores Java, mas adaptar-se a eles.
A APARAPI pode ser usada por desenvolvedores Java para desenvolver
aplicações que serão executadas diretamente por uma GPU e/ou uma APU sem
precisar escrever uma linha de ATI Stream, OpenCL ou CUDA. Ela reforça o lema
do Java, “Write Once, Run Anywhere”, que significa “Escreva uma vez, execute em
qualquer lugar”, porem com o diferencial de que sua aplicação portável quase
sempre poderá fazer uso de todos recursos de processamento em qualquer
computador que seja executado, tendo ele uma GPU ou não. A biblioteca é capaz
de converter de forma transparente em tempo de execução o bytecode Java em um
código compatível com o OpenCL, compatível com os mais diversos modelos de
GPU. Desta forma permite que os desenvolvedores de aplicativos e plugins Java
aproveitem o poder computacional das GPUs e APUs sem demasiadas
complicações. Também de forma transparente, a APARAPI, analisa o código e
57
verifica a disponibilidade de uma GPU com suporte ao OpenCL, se não estiver
disponível no computador, a APARAPI automaticamente voltará a execução usando
um pool de threads Java, fazendo com que a aplicação rode em múltiplas CPUs
obtendo o máximo de desempenho possível. (GEADA, 2012).
Recursos oferecidos pela APARAPI:

Não há necessidade de pensar em transferências de dados explícitos entre o
host do programa e o kernel GPU (Se necessário você pode fazer para os fins
de melhorias).

Não há necessidade de consultar os dispositivos e a plataforma.

Não há necessidade de criação de contexto e gerenciamento de fila de
comando.

Não há necessidade de escrever código para a criação de buffers.

Não há necessidade de definir explicitamente os argumentos do kernel.

Não há necessidade de definir parâmetros como os globalThreads e
localThreads e dimensões.

Se a GPU não é encontrada, a APARAPI transforma o código do kernel para
usar JTP (Java Pool Thread) ou a CPU. Você não é obrigado a verificar
explicitamente se existe GPU ou não e não há necessidade de criar
explicitamente tópicos para gerenciar o paralelismo de dados.

Não há necessidade de aprender OpenCL C para escrever aplicações
altamente paralelas de dados.

Curva de aprendizagem rápida.

APARAPI é um projeto Open Source.
A APARAPI está tendo uma série de vantagens, porem existem algumas limitações:

Não há suporte para a memória local (isto causa uma enorme perda de
desempenho).

Pode não alcançar o desempenho altamente otimizado por OpenCL C para
operações matemáticas complexas.
58
4.2.4 OpenCL
OpenCL é um padrão aberto e livre de royalties para a programação paralela
em ambientes computacionais heterogêneos, compostos de CPUs, GPUs, APUs e
outros processadores, como mostra a figura 21. OpenCL permite a escrita de código
multi-plataforma para execução nestes dispositivos, possibilitando a utilização de
todo o poder computacional disponível no ambiente.
Figura 21: OpenCL.
Fonte: http://forums.macrumors.com/showthread.php?t=613836
O OpenCL é uma espécie de primo-irmão do OpenGL, que apesar de não
ser mais tão usado em jogos (perdendo espaço para o DirectX) é o mais usado em
aplicações profissionais. Ambos são desenvolvidos pelo “Khronos Group”, uma
associação de fabricantes destinada a desenvolver padrões abertos. Ele permite não
apenas o uso de GPUs, CPUs e APUs, mas também de outros chips aceleradores
(como processadores Cell) o que o torna uma opção teoricamente universal.
A partir da versão 1.1 da especificação do OpenCL, o modelo de
programação OpenCL é híbrido de paralelismo de tarefas e paralelismo de dados.
Para tirar vantagem da arquitetura do OpenCL, dois modelos de programação
59
podem ser utilizados. O primeiro é o modelo de programação de dados paralelos e a
outra é o modelo de programação de tarefas paralelas. No modelo de programação
de dados em paralelo, cada kernel executa o mesmo bloco de código, mas em
dados diferentes. O programador pode escolher particionar manualmente os kernels
em grupos, ou delegar essa tarefa para o middleware do OpenCL. Além do modelo
de programação paralela de dados, o programador pode optar por usar o modelo de
programação de tarefas paralelas. Neste caso, cada elemento de processamento vai
executar um núcleo de computação separada, e paralelismo é alcançado através da
execução de múltiplos núcleos separados work-items. (COMPUTER SCIENCE,
2011).
60
5 AMBIENTE DE TESTES
Para se comprovar a eficiência da arquitetura estudada, foi mantado um
ambiente de testes, o qual compreende hardware e software que estão descritos nas
seções seguintes.
5.1 HARDWARE
Para os testes realizados na arquitetura, foi utilizado um computador pessoal
com os seguintes itens de hardware descritos na tabela 2:
Tabela 2: Hardware
Descrição
Item
1
Quantidade
Processador: Intel Core I7 920 2.66Ghz 8Mb
Cache 4.8GT/s 4 Cores 8 Threads
1
2 Placa-mãe: XFX X58i
1
3 Memória RAM: 1Gb DDR3 1333Mhz
3
Placa de Vídeo: NVIDIA GeForce GTX465
EVGA 256bit 1Gb GDDR5, 102.6 Gb/s, PCI4 Exp 2.0 16X, Processor Clock 1215Mhz
1
Memory Clock 1603Mhz, Graphics Clock
607Mhz
5 Fonte: Corsair 650TX 650Watts 80Plus
6
7
1
Disco Rígido: Western Digital Caviar Black
1Tb 64mb 7200rpm SATA 3
1
Disco Rígido: Samsung 160Gb 7200rpm
SATA2 (RAID 0)
2
Detalhes técnicos da GPU utilizada na arquitetura e exibida na figura 22,
relevantes ao processamento paralelo estão descritos na tabela 3:
Tabela 3: Detalhes da GPU
Compute Capability
Dimensão das Grids de Blocos de threads
2.0
3
61
Dimensão do Bloco de threads
3
Dimensão dos Blocos
1024
Quantidade de Blocos
32
CUDA cores
352
Compute Performance
30x
Versão OpenGL
4.2
Figura 22: GTX 465
Fonte: 3DVISION-BLOG, 2010
5.2 SOFTWARE
Para a realização dos testes de benchmark, foram utilizados os seguintes
softwares descritos na tabela 4:
Item
Tabela 4: Software
Descrição
Licença
1 Compilador para a linguagem C: gcc 4.7.2
Freeware
2 API CUDA Toolkit 5
Freeware
3 Biblioteca APARAPI (06/05/2012)
Freeware
4 Driver de vídeo NVIDIA 306.97
Freeware
5 Java JRE e JDK 6
Freeware
6 IDE de desenvolvimento Java: Netbeans 7.2.1 LGPL/GPL
7 Sistema Operacional: Linux Mint 13 Kernel 3.2 GPL
62
5.3 Benchmarks
Para comprovar a eficiência das GPUs, foram realizados uma série de testes
com algoritmos de benchmarks executando cálculos na GPU. Da mesma forma
foram executados os algoritmos de benchmark na CPU a fim de comparar o
desempenho de ambas. As codificações dos algoritmos e os trechos de códigos
mais relevantes se encontram em anexo, no final do trabalho.
O CUDA Toolkit instalado para realizar os testes, também acompanha
diversos exemplos de aplicações que fazem uso do CUDA. Destes exemplos, foram
utilizados o algoritmo do Conjunto de Mandelbrot, a simulação de corpos para testes
espaciais e a multiplicação de matrizes com CUBLAS que possui um trecho de seu
código no ANEXO A4. Também foram desenvolvidos algum algoritmo de benchmark
como busca de números perfeitos, números primos e multiplicação de matrizes,
tanto com CUDA quanto com APARAPI.
5.3.1 O Conjunto de Mandelbrot
O conjunto de Mandelbrot é um fractal definido pela seguinte equação:
Um fractal é uma forma geométrica que pode ser dividida em diversas partes
de si mesma e ainda assim continuar semelhante ao formato original, como pode ser
observado na figura 23:
Figura 23: Conjunto de Mandelbrot
Fonte: MAZZON, 2012
63
5.3.2 Simulação de Corpos
Uma simulação de corpos numericamente aproxima a evolução de um
sistema de corpos nos quais cada organismo interage continuamente com qualquer
outro corpo. Um exemplo familiar é uma simulação astrofísica em que cada corpo
representa uma galáxia ou uma estrela individual, e os corpos atraem-se
mutuamente através da força da gravidade. Alguns exemplos podem ser observados
na figura 24.
Figura 24: Simulação de Corpos.
Fonte: NYLAND
64
6 RESULTADOS
Os resultados coletados nos testes foram com base na execução dos
algoritmos de benchmark, citados nos ANEXOS, em ambas as arquiteturas. Cada
algoritmo faz uso de paralelismo e explora de igual o melhor da arquitetura. Cada
resultado de cada teste foi obtido através de uma média de 10 execuções sobre as
mesmas condições. Estes resultados foram agrupados e exibidos através de
gráficos comparativos nas seções seguintes.
6.1 CUDA
Para os testes realizados sobre a API CUDA, cada algoritmo foi otimizado
em questão de quantidade de threads visando aperfeiçoar a eficiência em ambas as
arquiteturas e para uma comparação mais justa. Na GPU foram criados 32 blocos
com 1024 threads cada, já na CPU foram criadas 20 threads.
6.1.1 Cálculo de Números Perfeitos
Este teste se dá pelo fator número máximo calculado pelo tempo consumido
em segundos para reproduzir a simulação. Onde quanto menor o tempo gasto,
melhor. Os algoritmos destes testes podem ser observados nos anexos ANEXO A,
utilizando CUDA, e ANEXO A1, usado pthread.
Tempo (segundos)
Cálculo de Números Perfeitos
2500
2000
1500
1000
500
0
100000
500000
1000000
2000000
GPU
0,2
3,6
14
61
CPU
1,4
34
132
531
Seq
5,6
129
515
2110
SpeedUp
28
35,83
36,79
34,59
Número máximo calculado / Tempo
65
6.1.2 Cálculo de Números Primos
Este teste se dá pelo fator número máximo calculado pelo tempo consumido
em segundos para reproduzir a simulação. Onde quanto menor o tempo gasto,
melhor. Os algoritmos destes testes podem ser observados nos anexos ANEXO A2,
utilizando CUDA, e ANEXO A3, usado pthread.
Cálculo de Números Primos
700
Tempo (segundos)
600
500
400
300
200
100
0
100000
500000
1000000
2000000
GPU
0,4
8,2
32
133
CPU
0,6
12
45
165
Seq
2,3
42
175
590
SpeedUp
5,75
5,12
5,47
4,44
Número máximo calculado / Tempo
6.1.3 Multiplicação de Matrizes
Este teste se dá pelo fator dimensão da matriz pelo tempo consumido em
segundos para reproduzir a simulação. Onde quanto menor o tempo gasto, melhor.
O algoritmo deste teste pode ser observado no ANEXO A4, utilizando CUDA e a
biblioteca CUBLAS. Este teste foi extraído dos exemplos que acompanham o CUDA
Toolkit.
66
Multiplicação de Matrizes
1600
Tempo (segundos)
1400
1200
1000
800
600
400
200
0
640x640
1280x1280
1600x1600
3200x3200
GPU
0,4
0,7
1
4,7
CPU
9,6
45
92
394
Seq
36
168
351
1490
SpeedUp
90
240
351
317,02
Dimensão da matriz / Tempo
6.1.4 Conjunto de Mandelbrot
Este teste se dá pelo fator detalhamento da imagem pelos FPS produzidos.
Onde quanto mais FPS melhor. Este teste foi extraído dos exemplos que
acompanham o CUDA Toolkit.
Conjunto de Mandelbrot
120
100
FPS
80
60
40
20
0
256
512
1024
2048
4096
8192
16384
GPU
60
60
60
110
70
30
14
CPU
3,2
1,7
0,9
0,4
0,2
0,1
0,01
Seq
0,8
0,425
0,225
0,1
0,05
0,025
0,0025
Detalhamento da imagem / FPS
67
6.1.5 Simulação de Corpos
Este teste se dá pelo fator quantidade de corpos pelo tempo consumido em
milissegundos para reproduzir a simulação. Onde quanto menor o tempo gasto,
melhor. Este teste foi extraído dos exemplos que acompanham o CUDA Toolkit.
Simulação de Corpos
Tempo (milissegundos)
2500000,00
2000000,00
1500000,00
1000000,00
500000,00
0,00
1000
5000
10000
20000
50000
GPU
0,63
13,39
54,20
213,13
1181,44
CPU
209,67
5246,95
20975,89
83927,92
524653,13
Seq
838,70
20987,78
83903,56
335711,69
2098612,50
SpeedUp
1333,38
1568,01
1548,06
1575,12
1776,32
Número de Corpos / Tempo
6.2 APARAPI
6.2.1 Cálculo de Números Perfeitos
Este teste se dá pelo fator número máximo calculado pelo tempo consumido
em segundos para reproduzir a simulação. Onde quanto menor o tempo gasto,
melhor. O algoritmo deste teste pode ser observado no ANEXO B.
68
Cálculo de Números Perfeitos
2500
Tempo (segundos)
2000
1500
1000
500
0
10000
100000
500000
1000000
GPU
0,2
0,5
7,7
29
CPU
0,007
5,1
126
513
Seq
0,025
18
480
1932
SpeedUp
0,13
36
62,34
66,62
Número máximo calculado / Tempo
6.2.2 Cálculo de Números Primos
Este teste se dá pelo fator número máximo calculado pelo tempo consumido
em segundos para reproduzir a simulação. Onde quanto menor o tempo gasto,
melhor. O algoritmo deste teste pode ser observado no ANEXO B1.
Cálculo de Números Primos
Tempo (segundos)
2500
2000
1500
1000
500
0
10000
100000
500000
1000000
GPU
0,2
0,5
8,2
32
CPU
0,007
5,2
130
521
Seq
0,025
20
495
1958
SpeedUp
0,13
40
60,37
61,19
Número máximo calculado / Tempo
69
6.2.3 Multiplicação de Matrizes
Este teste se dá pelo fator dimensão da matriz pelo tempo consumido em
segundos para reproduzir a simulação. Onde quanto menor o tempo gasto, melhor.
O algoritmo deste teste pode ser observado no ANEXO B2.
Tempo (segundos)
Multiplicação de Matrizes
450
400
350
300
250
200
150
100
50
0
640x640
1280x1280
1600x1600
3200x3200
GPU
0,4
2,2
2,8
26
CPU
7,1
23,7
25,8
117
Seq
28,4
82
89
405
71
37,27
31,79
15,58
SpeedUp
Dimensão da matriz / Tempo
6.2.4 Conjunto de Mandelbrot
Este teste se dá pelo fator dimensão da imagem gerada, pelos FPS
produzidos. Onde quanto mais FPS, melhor. Este teste foi extraído dos exemplos
que acompanham a APARAPI.
70
FPS
Conjunto de Mandelbrot
1000
900
800
700
600
500
400
300
200
100
0
256x256
512x512
1024x1024
2048x2048
GPU
930
420
136
31
CPU
24
20
9
2
Seq
7
6
3
0,5
Dimensão da imagem / FPS
6.3 Comparação entre as APIs
6.3.1 Cálculo de Números Perfeitos
Tempo (segundos)
Números Perfeitos
35
30
25
20
15
10
5
0
100000
500000
1000000
CUDA
0,2
3,6
14
APAR
0,5
7,7
29
Número máximo calculado / Tempo
71
6.3.2 Cálculo de Números Primos
Tempo (segundos)
Números Primos
35
30
25
20
15
10
5
0
100000
500000
1000000
CUDA
0,4
8,2
32
APAR
0,5
8,2
32
Número máximo calculado / Tempo
6.3.3 Multiplicação de Matrizes
Multiplicação de Matrizes
Tempo (segundos)
30
25
20
15
10
5
0
640x640
1280x1280
1600x1600
3200x3200
CUDA
0,4
0,7
1
4,7
APAR
0,4
2,2
2,8
26
Dimensão da Matriz / Tempo
72
CONCLUSÃO
O aumento de velocidade dramático do núcleo computacional fornece uma
forte motivação para
o trabalho contínuo em paralelismo
com
GPGPU.
Evidentemente, a paralelização com GPU tem um efeito dramático sobre o tempo
total de execução, reduzindo-o por um fator de mais de 10 vezes que por sua vez
comparado a energia dissipada pela GPU em questão que é de no máximo 200W
com a energia dissipada pela CPU que chega a 130W, teoricamente ela mostra-se
uma melhor relação consumo/desempenho.
O novo CUDA 5 se mostrou muito mais prático e fácil de usar, desde a
instalação ao desenvolvimento simplificado, sem contar os diversos exemplos que
acompanham o SDK.
Com relação ao CUDA, a APARAPI se mostrou bem promissora, apesar de
ser relativamente nova quanto ao CUDA, ela fornece aos desenvolvedores Java a
possibilidade de paralelizar tarefas na GPU, oque antes era inviável na linguagem.
Comparando-se a performance da APARAPI com CUDA, é possível afirmar que os
resultados obtidos foram satisfatórios, pois apesar da performance ter sido um
pouco menor, deve-se levar em conta que esta está executando sobre uma JVM que
consequentemente gera uma perda de desempenho na aplicação, pois não é uma
aplicação nativa rodando diretamente sobre o sistema operacional.
A transição de nível de sistema para plataformas de computação exóticas é
sempre uma troca de engenharia. Os benefícios de desempenho de um ambiente
como esse devem ser suficientemente altos para compensar o custo de
desenvolvimento e manutenção com o novo sistema. A plataforma GPGPU e seu
ambiente de programação é suficientemente familiar para um programador e expõe
a massiva capacidade paralela de uma forma simples. O aumento de desempenho
imediato é evidente a partir dos benchmarks preliminares apresentados neste
relatório. Significativas otimizações adicionais podem ser realizados em trabalhos
futuros através de mais análise e refinamento desta abordagem GPGPU.
Empresas de hardware estão trabalhando duro para fornecer sistemas
sólidos acessíveis baseados em GPU, projetados especificamente para computação
de propósito geral, e há uma grande quantidade de software disponível para
aproveitar as oportunidades oferecidas por esses dispositivos. GPGPUs fornecerem
soluções para desafios do mundo real, a partir de renderização de vídeo e alto
73
desempenho em jogos de computação matemática e análise numérica (GPGPU
COMPUTING..., 2010).
Por fim, ainda foi realizada uma comparação entre as APIs CUDA e
APARAPI sobre os resultados obtidos nos testes anteriores. Ambas tem uma
relação muito próxima e apesar da APARAPI ser uma biblioteca para aplicativos
Java, ela converte o código da GPU para código OpenCL, o que também torna muito
flexível e com uma melhor performance, o que levou a obter resultados muito
próximos ao CUDA. Portanto, com certeza a APARAPI já é uma boa alternativa aos
desenvolvedores Java, podendo facilmente tornar uma simples aplicação altamente
escalável.
Em trabalhos futuros uma arquitetura alternativa poderia firmemente unir
processos de CPU com processos de GPU para maximizar a utilização do sistema.
A implementação do kernel da GPU pode ser mais trabalhado conforme o contexto
do problema, esta prática otimizaria ainda mais a performance de GPU.
Recomendável também que em trabalhos futuros seja utilizada uma GPU
com maior capacidade computacional e principalmente que seja de uma série
destinada ao GPGPU assim como a série TESLA, para que possam serem extraídos
mais recursos da arquitetura e da própria GPU.
Outra alternativa para trabalhos futuros é fazer uma analise do real consumo
de energia de ambas arquiteturas para calcular melhor o custo benefício. Esta
analise demonstraria com precisão os dados do consumo para comprovar, ou não, a
melhor eficiência energética das GPUs.
74
REFERÊNCIAS BIBLIOGRAFICAS
ABOUT GPGPU.org. Disponível Em: <http://gpgpu.org/about>. Acesso em: 09 jul.
2012.
ABOUT
the
Java
Technology.
Disponível
Em:
<http://docs.oracle.com/javase/tutorial/getStarted/intro/definition.html>. Acesso em:
05 set. 2012.
AMD Accelerated Processing Units. Traduzido por: Google Tradutor. Disponível
em:
<http://www.amd.com/us/products/technologies/fusion/Pages/fusion.aspx>.
Acesso em: 26 mar. 2012.
AMD
apresenta
o
Fusion
na
CES
de
2011.
Disponível em:
<http://ontheroad.adrenaline.uol.com.br/tecnologia/noticias/7128/amd-apresenta-ofusion-na-ces-2011.html>. Acesso em 26 mar. 2012.
ARRUDA, Felipe. A história dos processadores. 2011. Disponível Em:
<http://www.tecmundo.com.br/historia/2157-a-historia-dos-processadores.htm>.
Acesso em: 15 set. 2011.
CARTEY, Luke. Domain Specic Languages for Massively Parallel Processors
PRS
Transfer
Report.
2010.
Disponível
Em:
<http://www.hmmingbird.co.uk/downloads/transferreport.pdf>. Acesso em: 18 ago.
2012.
COMPUTER Science. CSC/ECE 506 Spring 2011/ch2a mc. 2011. Disponível Em:
<http://pgserver.csc.ncsu.edu/mediawiki/index.php/CSC/ECE_506_Spring_2011/ch2a_mc>.
Acesso em: 22 jul. 2012.
CLUSTERS
e
Supercomputação.
Disponível
<http://www.vivaolinux.com.br/artigo/Clusters-e-Supercomputacao>. Acesso
26 mar. 2012.
em:
em:
CUDA
PROGRAMAÇÃO
Paralela
Facilitada.
<http://www.NVIDIA.com.br/object/cuda_home_new_br.html>.
29 mar. 2012.
em:
em:
Disponível
Acesso
CUDA
PROGRAME
a
sua
NVIDIA.
Disponível
em:
<http://pt.opensuse.org/ARTIGOS:CUDA_Programe_a_sua_NVIDIA>. Acesso em:
15 set. 2011.
CUDA
programming
model.
2008.
Disponível
<http://ixbtlabs.com/articles3/video/cuda-1-p5.html>. Acesso em: 20 set. 2012.
Em:
CUDA TOOLKIT 4.0 and Parallel Nsight 2.0. Traduzido por: Google Tradutor.
Disponínel em: <http://developer.NVIDIA.com/content/cuda-toolkit-40-and-parallelnsight-20-now-production-release>. Acesso em: 15 set. 2011.
75
DALLY, Bill. “PROJECT DENVER” PROCESSOR TO USHER IN NEW ERA OF
COMPUTING. 2011. Disponível Em: <http://blogs.NVIDIA.com/2011/01/projectdenver-processor-to-usher-in-new-era-of-computing>. Acesso em: 2 out. 2012.
DONGARRA, Jack. O Que As Pessoas Estão Dizendo. Disponível em:
<http://www.NVIDIA.com.br/object/cuda_home_new_br.html>.
Acesso
em:
29 mar. 2012.
EL-REWINI, H.; ABD-EL-BARR, M., Advanced Computer Architecture and
Parallel Processing, 2005.
ENTENDA os supercomputadores, potentes máquinas usadas na pesquisa
científica. 2011. Disponível Em: <http://www.geek.com.br/posts/15978-entenda-ossupercomputadores-potentes-maquinas-usadas-na-pesquisa-cientifica>. Acesso em:
05 jun. 2012.
FERLIN, Edson P. O que é Processamento Paralelo?. 2011. Disponível Em:
<http://professorferlin.blogspot.com.br/2011/08/o-que-e-processamentoparalelo.html>. Acesso em: 25 ago. 2012.
FLYNN, Michael J. Disponível em: <http://arith.stanford.edu/~flynn/>. Acesso em:
25 out. 2011.
FROST, Gary. Aparapi: An Open Source tool for extending the Java promise of
‘Write Once Run Anywhere’ to include the GPU. Portland, 2012. Disponível Em:
<http://www.oscon.com/oscon2012/public/schedule/detail/23434>. Acesso em: 15
jun. 2012.
FUTURE generations of General-Purpose Graphics Processing Units (GPGPUs)
look to improve performance per watt. 2011. Disponível Em: <http://defense.geip.com/news-events/detail/2942>. Acesso em: 25 ago. 2012.
GPGPU. Disponível Em: <http://defense.ge-ip.com/products/family/gpgpu>. Acesso
em: 02 ago. 2012.
GPGPU Computing Horizons: Developing and Deploying for Microsoft
Windows.
2010.
Disponível
Em:
<http://download.microsoft.com/download/A/6/C/A6C0223B-9460-4346-8DC0B6BCFD9269B4/MSFT_GPGPU_whitepaper_FINAL.PDF>. Acesso em: 25 out.
2012.
GPU DO PRINCÍPIO ATÉ A COMPUTAÇÃO DE PROPÓSITO GERAL. 2011.
Disponível Em: <http://cavernadati.wordpress.com/2011/07/05/gpu-do-principio-atea-computacao-de-proposito-geral>. Acesso em: 21 out. 2012.
GRAMMELSBACHER, Alessandro V; MEDRADO, Jão C. C. COMPARAÇÃO DE
DESEMPENHO ENTRE GPGPU E SISTEMAS PARALELOS. São Paulo, 2009.
Disponível
Em:
<http://gpubrasil.files.wordpress.com/2009/12/comparacao-dedesempenho-entre-gpgpu-e-sistemas-paralelos.pdf>. Acesso em: 11 jun. 2012.
76
HARRIS,
Mark.
CUDA
5
and
Beyond.
Disponível
Em:
<http://developer.download.NVIDIA.com/GTC/PDF/GTC2012/PresentationPDF/S064
1-GTC2012-CUDA-5-Beyond.pdf>. Acesso em: 18 set. 2012.
HOLZ, Lucas. Comparação de desempenho entre CPU e GPU usando CUDA.
2010. Trabalho de Conclusão de Curso apresentado como pré-requisito para
conclusão do Curso de Ciência da Computação. Universidade Regional do Noroeste
do Estado do Rio Grande do Sul. Ijuí, 2010.
HOUSTON, Mike. General Purpose Computation on Graphics Processors
(GPGPU).
Disponível
Em:
<http://graphics.stanford.edu/~mhouston/public_talks/R520-mhouston.pdf>. Acesso
em: 11 set. 2012.
HUANG, Jen-Hsun. NVIDIA CEO Talks Parallel and Shows Roadmap at GTC
2010.
2010.
Disponível
Em:
<http://www.brightsideofnews.com/news/2010/9/22/NVIDIA-ceo-talks-parallel-andshows-roadmap-at-gtc-2010.aspx>. Acesso em: 02 out. 2012.
INTEL apresenta co-processador de 1 teraflops. 2011. Disponível Em:
<http://www.inovacaotecnologica.com.br/noticias/noticia.php?artigo=intel-apresentaco-processador-1-teraflops&id=010150111118>. Acesso em: 30 ago. 2012.
INÍCIO
DA
ERA
DA
APU
AMD
Fusion.
Disponível
em:
<http://www.guiadopc.com.br/noticias/16741/inicio-da-era-da-apu-amd-fusion.html>.
Acesso em: 26 mar. 2012.
JEONG,
Min
Kyu.
CUDA
ray-tracer.
2008.
Disponível
Em:
<http://www.cs.utexas.edu/~fussell/courses/cs384g/projects/final/artifacts_f08/mjeon
g>. Acesso em: 11 jun. 2012.
KEPLER - THE WORLD'S FASTEST, MOST EFFICIENT HPC ARCHITECTURE.
Disponível Em: <http://www.NVIDIA.com/object/NVIDIA-kepler.html>. Acesso em: 01
ago. 2012.
LEPILOV Sergey; Zabelin Alexey. GPU Architecture and Positioning. 2012.
Disponível Em: <http://www.xbitlabs.com/articles/graphics/display/NVIDIA-geforcegtx-680_2.html#sect0>. Acesso em: 13 jun. 2012.
MAZZON,
Tiago.
Os
Fractais.
2012.
Disponível
Em:
<http://www.deldebbio.com.br/2012/01/16/os-fractais/>. Acesso em: 28 out. 2012.
MONNERAT, Luiz R. R. Petrobras muda tecnologia e monta supercomputador.
Portland, 2012. Disponível Em: <http://www.geofisicabrasil.com/noticias/37empresas/2548-petrobras-muda-tecnologia-e-monta-supercomputador.html>.
Acesso em: 18 ago. 2012.
MORAES, Sérgio Ricardo Dos Santos. COMPUTAÇÃO PARALELA EM CLUSTER
DE GPU APLICADO A PROBLEMA DA ENGENHARIA NUCLEAR. Rio de Janeiro,
2012.
Disponível
Em:
77
<http://www.ien.gov.br/posien/teses/dissertacao_mestrado_ien_2012_01.pdf>.
Acesso em: 14 set. 2012.
MORIMOTO, Carlos E. O ABC das placas 3D, parte 1. 2010. Disponível Em:
<http://www.hardware.com.br/tutoriais/abc-placas-3d/pagina6.html>. Acesso em: 13
set. 2012.
MORIMOTO,
Carlos
E.
Processador.
2007.
Disponível
<http://www.hardware.com.br/termos/processador>. Acesso em: 28 set. 2012.
Em:
NON-GRAPHIC computing with graphics processors. 2008. Disponível Em:
<http://ixbtlabs.com/articles3/video/cuda-1-p1.html>. Acesso em: 20 set. 2012.
NVIDIA GPUDIRECT. Disponível Em: <http://developer.NVIDIA.com/cuda/NVIDIAgpudirect>. Acesso em: 17 ago. 2012.
NVIDIA Kepler GPUs Roadmap: GK107, GK106, GK104, GK110 and GK112.
Disponível Em: <http://www.geeks3d.com/20111126/NVIDIA-kepler-gpus-roadmapgk107-gk106-gk104-gk110-and-gk112>. Acesso em: 21 jun. 2012.
NYLAND, Lars; HARRIS Mark; PRINS Jan. Fast N-Body Simulation with CUDA.
Exemplos CUDA.
O QUE É A TECNOLOGIA de processamento gráfico CUDA?. Disponível em:
<http://www.tecmundo.com.br/10507-o-que-e-a-tecnologia-de-processamentografico-cuda.htm>. Acesso em: 14 set. 2011.
O QUE É COMPUTAÇÃO via GPU?. Traduzido por: Google Tradutor. Disponível
em: <http://www.NVIDIA.com/object/GPU_Computing.html>. Acesso em: 15 set.
2011.
O
que
é
computação
com
GPU?.
Disponível
Em:
<http://www.NVIDIA.com.br/object/what-is-gpu-computing-br.html>. Acesso em: 02
out. 2012.
O
que
são
STREAM
PROCESSORS?.
2007.
Disponível
Em:
<http://forum.gamevicio.com/i/topicos/52/52403-stream-processor-entenda-maissobre-este-assunto/index.html>. Acesso em: 10 set. 2012.
Petrobras monta supercomputador mais rápido do Brasil. 2011. Disponível Em:
<http://www.techtudo.com.br/noticias/noticia/2011/11/petrobras-montasupercomputador-mais-rapido-do-brasil.html>. Acesso em: 25 dez. 2012
PFISTER, Greg. Supercomputadores de baixo custo estão com os dias
contados.
2010.
Disponível
Em:
<http://www.inovacaotecnologica.com.br/noticias/noticia.php?artigo=supercomputad
ores-baixo-custo-gpu&id=010150101109>. Acesso em: 10 jul. 2012.
78
QUIMICA
COMPUTACIONAL.
Disponível
em:
<http://www.NVIDIA.com.br/object/computational_chemistry_br.html>. Acesso em:
25 mar. 2012.
SANTOS, Fábio. GPU dos games à computação científica. 2011. Disponível Em:
<http://www.notasemcfd.com/2011/01/gpu-dos-games-computacao-cientifica.html>.
Acesso em: 18 jul. 2012.
STEREOSCOPIC 3D Related News Coming from Computex 2010. 2010.
Disponível Em: <http://3dvision-blog.com/tag/geforce-gtx-465/>. Acesso em: 28 out.
2012.
SUPERCOMPUTADOR Chinês torna-se o mais rápido do mundo. Disponível em:
<http://NVIDIA.adrenaline.uol.com.br/tecnologia/noticias/6589/supercomputadorchines-torna-se-o-mais-rapido-do-mundo.html>. Acesso em: 27 mar. 2012.
SUPERCOMPUTADOR de mesa atinge 12 teraflops. 2009. Disponível Em:
<http://adrenaline.uol.com.br/tecnologia/noticias/3716/supercomputador-de-mesaatinge-12-teraflops.html>. Acesso em: 11 ago. 2012.
SUPERCOMPUTADOR Titan opera com mais de 18 mil GPUs NVIDIA. 2012.
Disponível
Em:
<http://NVIDIA.adrenaline.uol.com.br/tecnologia/noticias/14442/supercomputadortitan-opera-com-mais-de-18-mil-gpus-NVIDIA.html>. Acesso em: 03 nov. 2012.
TANENBAUM, Andrew S. Organização Estruturada de Computadores. 5.ed. São
Paulo: Pearson Prentice Hall, 2007.
TAVARES, Arnaldo. Especialista da NVIDIA fala sobre processamento paralelo
na
USP.
São
Paulo,
2011.
Disponível
Em:
<http://www.NVIDIA.com.br/object/prbr_030111a.html>. Acesso em: 25 ago. 2012.
TEIXEIRA, Sérgio. A era da computação verde. 2007. Disponível Em:
<http://planetasustentavel.abril.com.br/noticia/desenvolvimento/conteudo_238522.sht
ml>. Acesso em: 20 mai. 2012.
THE SMX core. Disponível Em: <http://techreport.com/review/22989/a-brief-look-atNVIDIA-gk110-graphics-chip/2>. Acesso em: 20 out. 2012.
TOP500. Disponível em: <http://www.top500.org>. Acesso em: 15 set. 2011.
Tudo sobre Arquitetura ATI vs Nvidia (Stream Processor). 2009. Disponível Em:
<http://www.hardware.com.br/comunidade/processors-stream/1013219/>.
Acesso
em: 25 dez. 2012.
VOLPATO, Diego G. Exploração de Diferentes Níveis de Paralelismo Visando a
Redução da Área de Processadores Embarcados. Porto Alegre, 2011. Disponível
Em:
<http://www.lume.ufrgs.br/bitstream/handle/10183/37167/000819607.pdf?sequence=
1>. Acesso em: 26 mar. 2012.
79
YAMASHIRO, Fábio M; CORREA, Felipe. Paralelismo em Nível de Instrução.
Disponível Em: <http://www.ic.unicamp.br/~ducatte/mo401/1s2012/T2/G01-120437123789-134097-t2.pdf>. Acesso em: 14 jul. 2012.
80
OBRAS CONSULTADAS
COMPUTAÇÃO de alto desempenho utilizando CUDA. Disponível
<http://www.brunocardoso.cc/cuda/cuda.pdf>. Acesso em: 15 set 2011.
em:
CUDA – A Revolução Esperada. Disponível em: <http://www.pcmanias.com/cudaa-revoluo-esperada>. Acesso em: 16 set 2011.
CUDA, Supercomputação para as Massas. Traduzido por: Google Tradutor.
Disponível em: <http://drdobbs.com/architecture-and-design/207200659>. Acesso
em: 12 set 2011.
DALLALANA, Frederico José Camiloti. Uso das GPU para sistemas de
Computação de Alto Desempenho. São José do Rio Preto. Disponível em:
<http://www.dcce.ibilce.unesp.br/~aleardo/cursos/hpc/Fred.pdf>. Acesso em: 15 set
2011.
GPU
–
Graphics
Processing
Unit.
Disponível
em:
<http://www.ic.unicamp.br/~rodolfo/Cursos/mc722/2s2008/Trabalho/g26_texto.pdf>.
Acesso em: 15 set 2011.
GPU
Computing
in
Nuclear
Engineering.
Disponível
<http://verl.npre.illinois.edu/AdvancedComputing.html>. Acesso em: 8 jul 2012.
Em:
GPU: Aspectos Gerais. Disponível em: <http://pt.scribd.com/doc/52491224/ArtigoGPU>. Acesso em: 14 set 2011.
KIRK, David B; HWU, Wen-mei W. Programming Massively Parallel Processors.
United States of America, 2010. Elsevier.
LAÍS, Aline. Green Computing Computacao Sustentavel. 2012. Disponível Em:
<http://cjr.org.br/2012/04/green-computing-computacao-sustentavel>. Acesso em: 16
jun 2012.
MAILLARD, N; NAVAUX, Philippe O. A. Novos rumos em programação
para
a
exploração
de
paralelismo
multi-nível.
Disponível
Em:
<http://www.ic.unicamp.br/~cmbm/desafios_SBC/maillardnavaux.pdf>. Acesso em:
27 jul 2012.
MORGAN, Timothy Prickett. NVIDIA's Kepler pushes parallelism up to eleven.
2012.
Disponível
Em:
<http://www.theregister.co.uk/2012/05/15/NVIDIA_kepler_tesla_gpu_revealed>.
Acesso em: 22 jul 2012.
ORGANIZAÇÃO
e
Arquitetura
de
Computadores.
Disponível
<http://www.inf.ufsc.br/~mario/oac.pdf>. Acesso em: 23 out 2011.
em:
81
ANEXOS
ANEXO A - Cálculo de Números Perfeitos, CUDA/C
#include
#include
#include
#include
#include
<stdio.h>
<cuda.h>
<time.h>
<windows.h>
"temp.h"
__global__ void perfect(int *a, int N, int max, int threads)
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int soma, b, trab, i;
trab = max/threads;
for (i=0; i<trab; i++) {
if (idx%2==0) {
soma=0;
for (b=1; b<(idx/2)+1; b++) {
if (idx%b==0) {
soma = soma + b;
}
}
if (soma==idx) {
a[idx] = idx;
}
}
idx +=threads;
}
}
int main(int argc, char *argv) {
int limit;
printf ("\nNumeros PERFEITOS Usando CUDA\n Testar ate qual
valor?\n");
scanf ("%d", &limit);
int *a_h=0;
// pointeiro da RAM
int *a_d=0;
// pointeiro da GPU
struct timeval tempo1, tempo2;
long elapsed_mtime;
gettimeofday(&tempo1, NULL);
int n_blocks = 32;
int block_size = 1024;
const int N = n_blocks*block_size;// tamanho do vetor
int i;
int size = N * sizeof(int); // qtde de bytes do vetor
a_h = (int *)malloc(size);
for (i=0; i<N; i++)
a_h[i] = 0;
cudaMalloc((void**)&a_d, size);
cudaMemset(a_d, 0, size);
//copia da RAM para a GPU
82
cudaMemcpy(a_d, a_h, size, cudaMemcpyHostToDevice);
//printf("\n\nQt de blocos:
%d",n_blocks);
//printf("\ntamanho do bloco: %d\n\n", block_size);
//executa o kernel na GPU
perfect <<< n_blocks, block_size >>> (a_d, N, limit,
n_blocks*block_size);
// copia da GPU para a RAM
cudaMemcpy(a_h, a_d, size, cudaMemcpyDeviceToHost);
gettimeofday(&tempo2, NULL);
for (i=0; i<N; i++)
if (a_h[i] != 0)
printf("\nN. Perfeito: %d", a_h[i]);
//libera memorias
free(a_h);
cudaFree(a_d);
elapsed_mtime = ((tempo2.tv_sec - tempo1.tv_sec) * 1000 +
(tempo2.tv_usec - tempo1.tv_usec)/1000.0) + 0.5;
printf("\nElapsed time = %ld milliseconds\n", elapsed_mtime);
}
ANEXO A1 - Cálculo De Números Perfeitos, C
#include <stdio.h>
#include "tempo.h"
#include <pthread.h>
typedef struct
{
int id,
inicio,
fim;
}thread_info;
void *perfect(void *arg);
void main(int argc, char *argv[]){
int limit, num_threads, trab;
printf ("\nNúmeros PERFEITOS!\nUsando pThread\n");
printf ("\nTestar até qual valor? ");
scanf ("%d", &limit);
printf ("Executar com quantas Threads? ");
scanf ("%d", &num_threads);
trab = limit/num_threads;
pthread_t thread_id[num_threads];
thread_info t_info[num_threads];
int i = 0;
//Cria as threads
tempo1();
for(i=0; i<num_threads; i++){
t_info[i].id = i;
83
if (i!=0)
t_info[i].inicio = t_info[i-1].fim;
else
t_info[i].inicio = 2;
if (i==num_threads-1)
t_info[i].fim = limit;
else
t_info[i].fim = t_info[i].inicio+trab;
pthread_create(&(thread_id[i]), NULL, perfect, (void
*)&(t_info[i]));
}
for(i=0; i<num_threads; i++){
pthread_join(thread_id[i], NULL);
}
tempo2();
tempoFinal("mili segundos", "tempo", MSGLOG);
}
void *perfect(void *arg){
int soma, i, b;
thread_info *thi = (thread_info *) arg;
for (i=thi->inicio; i<=thi->fim; i++) {
soma=0;
if (i%2==0) {
for (b=1; b<(i/2)+1; b++) {
if (i%b==0) {
soma = soma + b;
}
}
if (soma==i) {
printf("Thread %d - Nº Perfeito: %d\n", thi>id, i);
}
}
}
}
ANEXO A2 - Cálculo de Números Primos, CUDA/C
#include
#include
#include
#include
#include
<stdio.h>
<cuda.h>
<time.h>
<windows.h>
"temp.h"
__global__ void prime(int *a, int N, int max, int threads)
{
int idx = (blockIdx.x * blockDim.x) + threadIdx.x;
int trab, i, c;
trab = max/threads;
for (i=0; i<trab; i++) {
for ( c = 2 ; c <= idx - 1 ; c++ )
{
if ( idx%c == 0 )
break;
}
if ( c == idx )
84
{
a[idx] = idx;
}
idx +=threads;
}
}
int main(int argc, char *argv) {
int limit;
printf ("\nNumeros PRIMOS Usando CUDA\n Testar ate qual valor?\n");
scanf ("%d", &limit);
int *a_h=0;
// pointeiro da RAM
int *a_d=0;
// pointeiro da GPU
struct timeval tempo1, tempo2;
long elapsed_mtime;
gettimeofday(&tempo1, NULL);
int n_blocks = 32;
int block_size = 1024;
const int N = limit;
// tamanho do vetor
int i;
int size = N * sizeof(int); // qtde de bytes do vetor
a_h = (int *)malloc(size);
for (i=0; i<N; i++)
a_h[i] = 0;
cudaMalloc((void**)&a_d, size);
cudaMemset(a_d, 0, size);
//copia da RAM para a GPU
cudaMemcpy(a_d, a_h, size, cudaMemcpyHostToDevice);
//printf("\n\nQt de blocos:
%d",n_blocks);
//printf("\ntamanho do bloco: %d\n\n", block_size);
//executa o kernel na GPU
prime <<< n_blocks, block_size >>> (a_d, N, limit,
n_blocks*block_size);
// copia da GPU para a RAM
cudaMemcpy(a_h, a_d, size, cudaMemcpyDeviceToHost);
gettimeofday(&tempo2, NULL);
for (i=0; i<N; i++)
if (a_h[i] > 0)
printf("\nN. Primo: %d", a_h[i]);
//libera memorias
free(a_h);
cudaFree(a_d);
elapsed_mtime = ((tempo2.tv_sec - tempo1.tv_sec) * 1000 +
(tempo2.tv_usec - tempo1.tv_usec)/1000.0) + 0.5;
printf("\nElapsed time = %ld milliseconds\n", elapsed_mtime);
85
}
ANEXO A3 - Cálculo de Números Primos, C
#include <stdio.h>
#include "tempo.h"
#include <pthread.h>
typedef struct
{
int id,
inicio,
fim;
}thread_info;
void *perfect(void *arg);
void main(int argc, char *argv[]){
int limit, num_threads, trab;
printf ("\nNúmeros PRIMOS!\nUsando pThread\n");
printf ("\nTestar até qual valor? ");
scanf ("%d", &limit);
printf ("Executar com quantas Threads? ");
scanf ("%d", &num_threads);
trab = limit/num_threads;
pthread_t thread_id[num_threads];
thread_info t_info[num_threads];
int i = 0;
//Cria as threads
tempo1();
for(i=0; i<num_threads; i++){
t_info[i].id = i;
if (i!=0)
t_info[i].inicio = t_info[i-1].fim;
else
t_info[i].inicio = 2;
if (i==num_threads-1)
t_info[i].fim = limit;
else
t_info[i].fim = t_info[i].inicio+trab;
pthread_create(&(thread_id[i]), NULL, perfect, (void
*)&(t_info[i]));
}
for(i=0; i<num_threads; i++){
pthread_join(thread_id[i], NULL);
}
tempo2();
tempoFinal("mili segundos", "tempo", MSGLOG);
}
void *perfect(void *arg){
int soma, i, b, c;
thread_info *thi = (thread_info *) arg;
for (i=thi->inicio; i<=thi->fim; i++) {
for ( c = 2 ; c <= i - 1 ; c++ )
{
86
if ( i%c == 0 )
break;
}
if ( c == i )
{
printf("Thread %d - Nº Primo: %d\n", thi->id, i);
}
}
}
ANEXO A4 - Parte do Código de Multiplicação De Matrizes, CUDA/C
Fonte: Exemplos CUDA
// CUBLAS version 2.0
{
cublasHandle_t handle;
cublasStatus_t ret;
ret = cublasCreate(&handle);
if (ret != CUBLAS_STATUS_SUCCESS) {
printf("cublasCreate returned error code %d, line(%d)\n", ret,
__LINE__);
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
const float alpha = 1.0f;
const float beta = 0.0f;
//Perform warmup operation with cublas
ret = cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
matrix_size.uiWB, matrix_size.uiHA, matrix_size.uiWA, &alpha, d_B,
matrix_size.uiWB, d_A, matrix_size.uiWA, &beta, d_C, matrix_size.uiWA);
if (ret != CUBLAS_STATUS_SUCCESS) {
printf("cublasSgemm returned error code %d, line(%d)\n", ret,
__LINE__);
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
// Allocate CUDA events that we'll use for timing
cudaEvent_t start;
error = cudaEventCreate(&start);
if (error != cudaSuccess) {
fprintf(stderr, "Failed to create start event (error code
%s)!\n", cudaGetErrorString(error));
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
cudaEvent_t stop;
error = cudaEventCreate(&stop);
if (error != cudaSuccess) {
fprintf(stderr, "Failed to create stop event (error code
%s)!\n", cudaGetErrorString(error));
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
// Record the start event
error = cudaEventRecord(start, NULL);
if (error != cudaSuccess) {
fprintf(stderr, "Failed to record start event (error code
87
%s)!\n", cudaGetErrorString(error));
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
for (int j = 0; j < nIter; j++) {
//note cublas is column primary!
//need to transpose the order
ret = cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
matrix_size.uiWB, matrix_size.uiHA, matrix_size.uiWA, &alpha, d_B,
matrix_size.uiWB, d_A, matrix_size.uiWA, &beta, d_C, matrix_size.uiWA);
if (ret != CUBLAS_STATUS_SUCCESS)
{
printf("cublasSgemm returned error code %d, line(%d)\n",
ret, __LINE__);
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
}
printf("done.\n");
// Record the stop event
error = cudaEventRecord(stop, NULL);
if (error != cudaSuccess) {
fprintf(stderr, "Failed to record stop event (error code
%s)!\n", cudaGetErrorString(error));
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
// Wait for the stop event to complete
error = cudaEventSynchronize(stop);
if (error != cudaSuccess)
{
fprintf(stderr, "Failed to synchronize on the stop event
(error code %s)!\n", cudaGetErrorString(error));
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
float msecTotal = 0.0f;
error = cudaEventElapsedTime(&msecTotal, start, stop);
if (error != cudaSuccess) {
fprintf(stderr, "Failed to get time elapsed between events
(error code %s)!\n",
cudaGetErrorString(error));
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
printf("\nGPU ELAPSED: %.3f\n", double(clock() - gpuBegin) /
CLOCKS_PER_SEC);
// Compute and print the performance
float msecPerMatrixMul = msecTotal / nIter;
double flopsPerMatrixMul = 2.0 * (double)matrix_size.uiWA *
(double)matrix_size.uiHA * (double)matrix_size.uiWB;
double gigaFlops = (flopsPerMatrixMul * 1.0e-9f) /
(msecPerMatrixMul / 1000.0f);
printf(
"Performance= %.2f GFlop/s, Time= %.3f msec, Size= %.0f
Ops\n",
gigaFlops,
msecPerMatrixMul,
flopsPerMatrixMul);
88
// copy result from device to host
error = cudaMemcpy(h_CUBLAS, d_C, mem_size_C,
cudaMemcpyDeviceToHost);
if (error != cudaSuccess) {
printf("cudaMemcpy h_CUBLAS d_C returned error code %d,
line(%d)\n", error, __LINE__);
shrQAFinishExit(argc, (const char **)argv, QA_FAILED);
}
}
ANEXO B - Cálculo de Números Perfeitos, JAVA
package test;
import com.amd.aparapi.Kernel;
public class Perfeitos extends Thread {
private int range;
private int perfeitos[];
public static final int CPU = 0;
public static final int GPU = 1;
public Perfeitos(int mode, int range) {
this.range = range;
perfeitos = new int[range];
if (mode == CPU) {
onCPU();
} else {
onGPU();
}
}
public static void main(String[] args) {
new Perfeitos(GPU, 1000000);
}
//
//
//
//
//
//
//
//
private void onCPU() {
int threads = 20;
for (int i = 1; i <= threads; i++) {
int trab = Math.round(range / threads);
int fim = trab * i;
int ini = (fim - trab) + 1;
new Perfeitos.CpuThread(ini, fim).start();
}
for (int i = 0; i < perfeitos.length; i++) {
if (perfeitos[i] != 0) {
System.out.println(perfeitos[i]);
}
}
}
private void onGPU() {
Perfeitos.GpuThread gt = new Perfeitos.GpuThread(range);
gt.setExecutionMode(Kernel.EXECUTION_MODE.GPU);
int perfeitos[] = gt.getPerfeitos();
System.out.println(gt.getExecutionTime() + "ms");
for (int i = 0; i < perfeitos.length; i++) {
if (perfeitos[i] != 0) {
System.out.println(perfeitos[i]);
89
//
//
}
}
}
//CPU THREAD WORK
public class CpuThread extends Thread {
private int ini;
private int fim;
private int perfeitos[];
//
public CpuThread(int ini, int fim) {
this.ini = ini;
this.fim = fim;
setPriority(MAX_PRIORITY);
}
@Override
public void run() {
for (int ate = ini; ate <= fim; ate++) {
int soma = 0;
for (int i = 1; i < ate; i++) {
if (ate % i == 0) {
soma += i;
}
}
if (ate == soma) {
perfeitos[ate] = ate;
}
}
}
}
//GPU THREAD WORK
public class GpuThread extends Kernel {
final int range;
final int perfeitos[];
public GpuThread(final int range) {
this.range = range;
this.perfeitos = new int[range];
}
@Override
public void run() {
int soma = 0;
for (int i = 1; i < getGlobalId(); i++) {
if ((getGlobalId() % i) == 0) {
soma += i;
}
}
if (soma == getGlobalId()) {
perfeitos[getGlobalId()] = getGlobalId();
}
}
public int[] getPerfeitos() {
execute(range);
return perfeitos;
}
90
}
}
ANEXO B1 - Cálculo de Números Primos, JAVA
package test;
import com.amd.aparapi.Kernel;
public class Primos extends Thread {
private int range;
private int primos[];
public static final int CPU = 0;
public static final int GPU = 1;
public Primos(int mode, int range) {
this.range = range;
primos = new int[range];
if (mode == CPU) {
onCPU();
} else {
onGPU();
}
}
public static void main(String[] args) {
new Primos(GPU, 10000);
}
//
//
//
//
//
//
//
//
//
//
private void onCPU() {
int threads = 20;
for (int i = 1; i <= threads; i++) {
int trab = Math.round(range / threads);
int fim = trab * i;
int ini = (fim - trab) + 1;
new CpuThread(ini, fim).start();
}
for (int i = 0; i < primos.length; i++) {
if (primos[i] != 0) {
System.out.println(primos[i]);
}
}
}
private void onGPU() {
GpuThread gt = new GpuThread(range);
gt.setExecutionMode(Kernel.EXECUTION_MODE.GPU);
int primos[] = gt.getPrimos();
System.out.println(gt.getExecutionTime() + "ms");
for (int i = 0; i < primos.length; i++) {
if (primos[i] != 0) {
System.out.println(primos[i]);
}
}
}
//CPU THREAD WORK
public class CpuThread extends Thread {
private int ini;
91
private int fim;
private int primos[];
//
public CpuThread(int ini, int fim) {
this.ini = ini;
this.fim = fim;
setPriority(MAX_PRIORITY);
}
@Override
public void run() {
for (int ate = ini; ate <= fim; ate++) {
int primo = 0;
for (int i = 2; i < ate; i++) {
if ((ate % i) == 0) {
primo++;
}
}
if (primo == 0) {
primos[ate] = ate;
}
}
}
}
//GPU THREAD WORK
public class GpuThread extends Kernel {
final int range;
final int primos[];
public GpuThread(final int range) {
this.range = range;
this.primos = new int[range];
}
@Override
public void run() {
int primo = 0;
for (int i = 2; i < getGlobalId() + 1; i++) {
if (((getGlobalId() + 1) % i) == 0) {
primo++;
}
}
if (primo == 0) {
primos[getGlobalId() + 1] = getGlobalId() + 1;
}
}
public int[] getPrimos() {
execute(range);
return primos;
}
}
}
ANEXO B2 - Multiplicação de Matrizes, JAVA
package test;
import com.amd.aparapi.Kernel;
92
import java.util.Random;
public class Matrix {
public static void main(String[] args) throws Exception {
final int r = 3200;
final int c1 = r;
final int c2 = r;
AparapiMatMul ap = new AparapiMatMul(r, c1, c2);
//
try {
ap.setExecutionMode(Kernel.EXECUTION_MODE.GPU);
ap.setExecutionMode(Kernel.EXECUTION_MODE.CPU);
ap.execute(r, c2);
System.out.println(ap.getExecutionTime() + "ms");
} catch (NullPointerException ne) {
ne.printStackTrace();
}
ap.dispose();
}
}
class AparapiMatMul extends Kernel {
double matA[];
double matB[];
double matC[];
double C[];
int rows;
int cols1;
int cols2;
@Override
public void run() {
int i = getGlobalId();
int j = getPassId();
double value = 0;
for (int k = 0; k < cols1; k++) {
value += matA[k + i * cols1] * matB[k * cols2 + j];
}
matC[i * cols1 + j] = value;
}
public AparapiMatMul(int r, int c1, int c2) {
rows = r;
cols1 = c1;
cols2 = c2;
matA = new double[r * c1];
matB = new double[c1 * c2];
matC = new double[r * c2];
C = new double[r * c2];
//matC should be initialized with zeros
for (int i = 0; i < r; i++) {
for (int j = 0; j < c1; j++) {
matC[i * c1 + j] = 0;
}
}
for (int i = 0; i < r; i++) {
for (int j = 0; j < c1; j++) {
93
matA[i * c1 + j] = new Random().nextDouble();
}
}
for (int i = 0; i < r; i++) {
for (int j = 0; j < c1; j++) {
matB[i * c2 + j] = new Random().nextDouble();
}
}
}
}
Download

UNIVERSIDADE REGIONAL DO NOROESTE DO ESTADO DO RIO