23
Computação genérica de alto desempenho com GPUs Vítor Oliveira Distributed Computer Graphics ‘07/08 MAP-i Doctoral Program in Computer Science [email protected] Resumo. ... 1. Introdução Os GPUs evoluíram significativamente em capacidade de processamento nos últimos seis anos, tornando-se não só mais rápidos que os CPUs em determinado tipo de tarefas, mas também por vezes ordens de magnitude mais rápidos que estes. Naturalmente, esta enorme capacidade de processamento aparece associada à resolução de problemas próprios do processamento gráfico, altamente paralelo e orientado aos dados, em boa parte motivada pela indústria dos jogos de computador. O aproveitamento do potencial dos GPUs para o processamento genérico veio permitir resolver alguns problemas computacionalmente exigentes em postos de trabalho individuais que de outra forma precisariam de um conjunto mais alargado de recursos, vindos de sistemas de clustering HPC, Grid ou outros. No entanto, parece importante enquadrar os GPUs com a computação de alto desempenho, pelo que, no contexto do presente trabalho sobre computação genérica com unidades de processamento gráfico (GPU), se tenta uma abordagem dirigida às questões da computação de alto desempenho onde os GPUs podem desempenhar um papel importante. Apresentam-se, ainda assim, as formas como foram e são utilizados os GPUs para obter esse alto desempenho. Tal opção decorre, por um lado, do facto da proliferação de bibliografia sobre este último tema tornar difícil a apresentação de alguma contribuição interessante e, por outro lado, porque os GPU começam a ser considerados para clusters computacionais e para integração nos processadores genéricos, pelo que certamente haverá uma alteração significativa no papel que GPUs irão desempenhar neste contexto.

Computação genérica de alto desempenho com GPUspaginas.fe.up.pt/~aas/pub/Aulas/DiCG/VitorOliveira.pdf · de execução simultâneos, para um máximo teórico de 11GFLOPS, o Intel

Embed Size (px)

Citation preview

Computação genérica de alto desempenho com GPUs

Vítor Oliveira

Distributed Computer Graphics ‘07/08

MAP-i Doctoral Program in Computer Science [email protected]

Resumo. ...

1. Introdução

Os GPUs evoluíram significativamente em capacidade de processamento nos últimos seis anos, tornando-se não só mais rápidos que os CPUs em determinado tipo de tarefas, mas também por vezes ordens de magnitude mais rápidos que estes. Naturalmente, esta enorme capacidade de processamento aparece associada à resolução de problemas próprios do processamento gráfico, altamente paralelo e orientado aos dados, em boa parte motivada pela indústria dos jogos de computador.

O aproveitamento do potencial dos GPUs para o processamento genérico veio permitir resolver alguns problemas computacionalmente exigentes em postos de trabalho individuais que de outra forma precisariam de um conjunto mais alargado de recursos, vindos de sistemas de clustering HPC, Grid ou outros.

No entanto, parece importante enquadrar os GPUs com a computação de alto desempenho, pelo que, no contexto do presente trabalho sobre computação genérica com unidades de processamento gráfico (GPU), se tenta uma abordagem dirigida às questões da computação de alto desempenho onde os GPUs podem desempenhar um papel importante. Apresentam-se, ainda assim, as formas como foram e são utilizados os GPUs para obter esse alto desempenho. Tal opção decorre, por um lado, do facto da proliferação de bibliografia sobre este último tema tornar difícil a apresentação de alguma contribuição interessante e, por outro lado, porque os GPU começam a ser considerados para clusters computacionais e para integração nos processadores genéricos, pelo que certamente haverá uma alteração significativa no papel que GPUs irão desempenhar neste contexto.

2

2. Computação de alto desempenho

O motor da investigação em sistemas de computação é frequentemente a obtenção de desempenhos cada vez mais elevados recorrendo a cada vez menos recursos. Tal objectivo pode ser facilmente observado no contexto dos computadores genéricos, mas vai desde o contexto dos dispositivos móveis, passando pelos sistemas empresariais até ao mundo da computação de alto desempenho, entre outros.

As tecnologias e mecanismos criados para este fim diferem muito entre si, em particular se a comparação se fizer entre as unidades de processamento autónomas (processadores e unidades auxiliares locais) e as redes de sistemas de sistemas autónomos (com clusters HPC/HTC, Grid e clouds).

Neste capítulo apresentar-se-á o enquadramento, onde programaticamente se apresentam características técnicas das diversas tecnologias subjacentes. Pretende-se assim tornar mais claro que quando se compara CPUs com GPUs, ou redes de baixa latência com redes Ethernet, ou barramentos PCI com PCI-express, não deixa de se estar a comparar grandezas relacionadas e que os problemas, restrições e soluções em determinado nível podem constituir também problemas, restrições e soluções em diversos outros níveis.

2.1 Maximização do desempenho

Embora as áreas de investigação em arquitecturas intra-computador e inter-computadores sejam vastas, há um objectivo que se pode considerar transversal a todos, que é o maximizar a capacidade de computação recorrendo a um determinado conjunto de unidades de processamento e de memória interligadas por barramentos de comunicações. O desempenho depende de cada um destes três factores, sendo que estes se apresentam em diversos níveis hierárquicos: no caso de um processador, temos unidades ALU, memórias cache e o barramento interno; no caso de um sistema autónomo temos os diversos processadores, as memórias centrais e os barramentos (FSB, PCI, PCI-Express, USB); no caso de um cluster temos os nós de computação, as memórias distribuídas pelos nós e as redes de interligação.

Partindo de um modelo base aproximado, o que distingue realmente o desempenho nos diferentes contextos são as características de cada uma das unidades de processamento, de memória e de comunicações entre estas (e o resto do sistema). No caso dos processadores importa saber quanto tempo demora a executar um determinado conjunto de instruções, no caso da memória interessa se o seu desempenho e capacidade são suficientes para suportar o programa com os seus dados e no caso das comunicações interessa

3

saber qual é a largura de banda e a “latência” e qual o atraso que estas impõe no cálculo e no acesso à memória.

O panorama actual nos sistemas de computação de alto desempenho é esclarecedor em termos de modelos subjacentes, conforme se pode ver na lista do Top500 [1] de Novembro de 2007. Os processadores utilizados mais frequentemente são processadores genéricos, embora se possa distinguir entre aqueles que favorecem um maior número de processadores menos capazes (o primeiro da lista, o IBM BlueGene/L, tem 212.992 processadores PowerPC de 2.8GFLOPS/núcleo) e aqueles que favorecem processadores mais capazes mas em menor número (o terceiro da lista possui 14.336 processadores Xeon Quad-core de 12GFLOPS/núcleo). Relacionado com a escolha do número de nós está a memória RAM disponível em cada nó de processamento, que varia também segundo os modelos que privilegiam trabalhos de pequenas dimensões (256MB/núcleo) ou os que privilegiam os trabalhos grandes dimensões em cada nó de processamento (8GB/núcleo) ou ainda os que privilegiam um número alargado de núcleos e memória por nó de processamento (1024GB em 128 núcleos, como acontece no nó de processamento de maiores dimensões do Finis Terrae[2]). Em termos de interligação os sistemas mais rápidos possuem redes de interligação de baixa latência e alto débito, proprietárias ou genéricas como a Infiniband ou Myrinet, enquanto os sistemas menos capazes apostam na interligação dos nós de processamento com Ethernet.

O reflexo das características de memória e das redes na eficiência dos sistemas é bastante marcado, já que uma mesma capacidade bruta se transforma em valores LINPACK [3] muito diferentes. No caso do BlueGene/L a eficiência é de 80%, enquanto um sistema típico sobre Ethernet apresenta uma eficiência entre os 45 a 55%. No caso do Finis Terrae um número de nós reduzido mas com um número elevado de processadores interligados por Infiniband permite uma eficiência de 84%.

2.2 Desenvolvimentos nos microprocessadores

A evolução dos microprocessadores passa cada vez mais pelo aumento do número de núcleos e de memória cache em cada um dos processadores, secundarizando o aumento do desempenho dos núcleos individuais. Tal opção parte da premissa que os programadores das aplicações, ou os compiladores em nome destes, serão responsáveis por adaptar os programas para explorar todo o potencial dos recursos computacionais apresentados nas diferentes formas, mesmo para aplicações que se destinam a ser utilizadas num único computador. O fantasma da Lei de Amdahl[4] parece finalmente descansar, o que ajuda a libertar a arquitectura dos CPUs do espartilho da programação sequencial.

4

O desenvolvimento de CPUs passa, assim, por sistemas com múltiplos núcleos simétricos, como o Sun UltraSPARC T2[6] com 8 núcleos e 64 fios de execução simultâneos, para um máximo teórico de 11GFLOPS, o Intel Xeon com quatro núcleos e 4 instruções de virgula flutuante por ciclo do relógio e o AMD Phenon[7] com quatro núcleos e comunicações com HyperTransport e tecnologia NUMA, ou assimétricos como o Cell Broadband Engine [8], com 1 processador genérico e 7 ou 8 SPE que lhe permitem atingir um desempenho de pico de 205GFLOPS.

O desempenho global do sistema passou também a ser avaliado em relação ao consumo eléctrico, e à consequente capacidade de arrefecimento, necessário para o atingir. Este factor assume particular relevância quando se tinha verificado que as gerações mais recentes de processadores aumentavam o consumo eléctrico para valores impensáveis há poucos anos, em particular devido às frequências elevadas e ao associado leakage nos transístores. Os ganhos recentes têm sido significativos e a título de exemplo refira-se que um processador Xeon de núcleo único, a 3.6Ghz, da geração NetBurst consome 110W para produzir 7.2GFLOPS, enquanto um processador Xeon Quadcore baseado na microarquitectura Core2 e processo de fabrico de 45nm consome menos de 80W para produzir 48GFLOPS, um aumento de eficiência de uma ordem de grandeza em apenas 2 anos [9].

Espera-se que no final deste ano seja lançado o sucessor do processador Itanium II, com mais de 2.000 milhões de transístores, 30MB de cache, bem como no primeiro processador Intel Xeon com 6 cores por processador, com 16MB de cache e um desempenho superior a 72GFLOPS por processador.

Embora exista apenas como um sistema de investigação, a Intel apresentou já o primeiro processador que ultrapassa 1TFLOPS com um consumo de 62W, bem como a sua visão para os futuros processadores TeraScale [5].

Figura 1. Processador Intel Terascale com 80 cores e 1TFLOPS [5] .

5

2.3 Processamento com co-processadores

O sistema que se prevê venha a ser o primeiro a atingir em HPL um desempenho sustentado superior a 1PFLOPS será o IBM Roadrunner [10], que apresenta a particularidade de assentar numa configuração que junta num único nó de processamento – TriBlade – quatro núcleos Opteron com 16GB de RAM e quatro processadores PowerXCell 8i também com 16GB de RAM. Os processadores Cell utilizados possuem um núcleo PowerPC e oito SPE, apresentando102GFLOPS sustentados em HPL[11]. A comunicação entre os processadores Opteron e Cell decorre sobre quatro links PCI-Express x8, enquanto a comunicação inter-nó decorre sobre uma rede Infiniband.

A utilização de co-processadores para aumentar as capacidades dos processadores genéricos está longe de ser nova, sendo os co-processadores aritméticos (FPU) 8087/287/387 [12] dos mais conhecidos. Estas funcionalidades básicas de vírgula flutuante foram incluídas nos CPUs actuais, mas existem situações em que continua a sentir-se a necessidade de co-processadores, nomeadamente nos campos do processamento de sinal (DSPs), do cálculo avançado em vírgula flutuante (de que exemplo a ClearSpeed[13]), da criptografia (aceleradores TLS/SSL), do processamento gráfico (GPUs), etc.

O que distingue um processador de um co-processador será, entre outros, que o primeiro pode funcionar sem o segundo e o inverso não é válido. Com os primeiros co-processadores aritméticos era habitual existirem encaixes dedicados para a sua eventual instalação junto dos CPUs. Actualmente, é mais habitual disponibilizar os co-processadores na forma de placas de expansão, uma vez que é uma forma flexível e económica de integrar os componentes. No entanto, a utilização de um barramento de expansão implica limitações de largura de banda e a arbitragem e comunicação com atrasos significativos e, por vezes, inadequados para os objectivos do co-processador. De forma a contornar o problema houve duas abordagens distintas: na primeira tentou-se adequar os barramentos às características dos processadores, que deu origem a barramentos dedicados a GPUs como o AGP [14] que possuía uma largura de banda acrescida em relação ao barramento PCI; na segunda, e mais recente, criam-se mecanismos que permitem integrar os co-processadores directamente nos barramentos dos processadores.

A iniciativa Torrenza [15] da AMD destina-se a permitir que terceiros possam desenvolver co-processadores que podem ser encaixados directamente nos slots destinados a processadores Opteron/Phenom, ou nos sockets HTX, dando acesso directo às ligações HyperTransport [16] e ao barramento PCI-Express (ver figura a seguir).

6

Figura 2. Iniciativa AMD Torrenza, com as quatro configurações previstas [17]

3. As unidades de processamento gráfico (GPUs)

No capítulo anterior pretendeu-se apresentar uma panorâmica do estado actual dos sistemas de computação para tentar contextualizar devidamente a processamento em GPU. No capítulo que se inicia agora introduz-se o GPU como unidade de processamento auxiliar e apontam-se as suas vantagens e as suas inevitáveis desvantagens.

3.1 Breve história dos GPUs

Os GPUs surgiram no contexto de segmentos de mercado específicos onde era imperioso obter capacidades gráficas mais evoluídas na altura, o que veio a acontecer através de equipamentos de empresas como a Silicon Graphics. Os elevados custos destes equipamentos não permitiram a sua generalização, mas abriram caminho para que surgissem entretanto equipamentos mais acessíveis à generalidade dos utilizadores.

A generalização veio a acontecer em 1998 com a primeira geração de GPUs que incluía as Nvidia TNT2, a ATI Rage e a 3DFX Voodoo3, que incluíam a rasterização de triângulos e a mistura limitada de múltiplas texturas num único passo de rasterização.

7

A segunda geração que incluiu as Nvidia GeForce 256 e GeForce 2, bem como as ATI Radeon 7500 e S3 Savage3D, surgiu em 2000 trazendo a capacidade de transformar os vértices 3D e a iluminação para o GPU. O GPU passa a ser responsável por manter as matrizes de transformação dos vértices e pelos cálculos de iluminação em tempo real, à medida que a cena é apresentada. Embora as operações disponíveis para combinar texturas e colorir pixéis tenham sido expandidas, continuam a ser em número reduzido.

A possibilidade de programar os vértices, embora muito limitada, surgiu com a terceira geração de GPUs em 2001 e que incluiu dispositivos como a GeForce3 e GeForce4 da Nvidia e a ATI Radeon 8500. Embora a utilização GPUs para processamento genérico tenha surgido com esta geração, as limitações ao nível do acesso às texturas, do formato das texturas e a ausência de estruturas de controlo para saltos limitavam a sua utilização.

A quarta geração de GPU inclui as Nvidia GeForceFX, GeForce 6800, GeForce 7800, bem como as ATI Radeon 9700, 9800, X800 e X1800. Nesta geração, que inclui a maior parte dos dispositivos actuais, são introduzidas diversas funcionalidades diferenciadoras. Entre outras, passaram a ter unidades de vírgula flutuante, passaram a existir estruturas de controlo - mesmo que penalizantes em termos de desempenho - e os programas de shading para vértices e fragmentos passara a ter tamanho quase ilimitado.

A quinta geração inclui os dispositivos com arquitecturas unificadas, que incluem as Nvidia 8800/8900/9900, Tesla e NV280/260, bem como as ATI Radeon HD e AMD FireStream 9170. Esta última inclui, pela primeira vez, suporte para operações de vírgula flutuante com precisão dupla. Adiante descrever-se-á algumas das funcionalidades desta geração.

Figura 3. Pipeline tradicional dos GPU (esq.) e a arquitectura da Nvidia 6800 (dir.) [17]

8

3.2 O pipeline das arquitecturas GPU actuais

A enorme capacidade de processamento dos GPU está associada aos shaders que são aplicados quer aos vértices, quer aos fragmentos, quer (mais recentemente) às geometrias. Com a separação entre shaders no modelo de pipeline tradicional o balanceamento da carga entre as diversas pode ser problemático. Se as aplicações exigirem muito trabalho do GPU só ao nível dos vértices, as unidades associadas aos fragmentos ficam paradas, ou vice-versa. A evolução de número de unidades associadas a cada uma das funções acabou por favorecer o processamento de fragmentos, por serem mais habitual existirem mais pixéis do que pontos a processar, mas o desequilíbrio e a existência de gargalos pode ser problemática, como se pode ver nos exemplos que se seguem.

Figura 4. Utilização das unidades do GPU num pipeline tradicional [32]

As gerações mais recentes dos GPUs contornam este problema ao incluir um único tipo de unidade de processamento, que pode ser utilizado por qualquer tipo de shader. Desta forma, todas as unidades de processamento ficam disponíveis para todos os tipos de tarefas, o que leva a uma utilização plena da capacidade de processamento.

Figura 5. Comparação entre um pipeline tradicional e um pipeline unificado (dir.) [31]

9

Nvidia GeForce 8800/9800

Figura 6. Arquitectura dos Nvidia G80/G92 (GeForce 8 e 9) [32]

O diagrama acima representa a arquitectura interna que foi adoptada pela Nvidia para a geração GeForce 8 e 9 [32]. Esta arquitectura assenta num conjunto de 8 multiprocessadores, cada um dos quais com 16 unidades de processamento, para um total de 128 processadores. A gestão de fios de execução com o GigaThreads permite-lhe gerir em hardware milhares de threads em simultâneo. O relógio do GPU está dividido em várias partes, funcionando os shaders a uma velocidade superior às unidades de controlo. O pipeline foi encurtado significativamente em relação aos níveis da GeForce 7, que chegava a ter 200 níveis só para o processador de fragmentos. As entradas de dados são conduzidas ao topo do núcleo de shader e as saídas são escritas em registos e lançadas para o topo do núcleo de shading que vai tratar do passo seguinte.

ATI Radeon HD 3800

O diagrama apresentado representa a arquitectura das ATI Radeon HD 3800 que, tal como no caso anterior, implementa um pipeline unificado. O processamento é baseado numa microarquitectura VLIW, com 64 clusters de shaders, cada um com 5 unidades, para um total de 320 unidades de processamento. Cada uma das primeiras quatro unidades do cluster consegue debitar uma instrução MADD ou MUL ou ADD ou DP por cada ciclo do relógio, enquanto a quinta unidade pode executar também funções transcendentais como o seno e o coseno.

10

Figura 7. Arquitectura das ATI Radeon HD 3800 [32]

A dependência da microarquitectura VLIW acarreta consigo os desafios inerentes à mesma, nomeadamente no que diz respeito à manutenção de um fluxo optimizado de instruções. Por isso, o desempenho é altamente dependente dos programas implementados e da capacidade do compilador reordenar devidamente as ditas instruções.

Tal como na geração anterior e com a Nvidia, o suporte a múltiplos fios de execução é instrumental para a obtenção de elevados desempenhos. Cada conjunto de 80 unidades de processamento tem o seu próprio árbitro e sequenciador, o primeiro destinado a seleccionar os fios de execução que irão executar a seguir e o segundo destinado a reordenar as instruções de forma a obter o maior desempenho possível.

3.3 A capacidade de processamento dos GPU

Os GPUs evoluíram formidavelmente em capacidade de processamento nos últimos anos, atingindo valores de pico teóricos em processamento de vírgula flutuante superiores a qualquer processador genérico actualmente disponível.

Efectivamente, pela análise das especificações de um processador Xeon da última geração com 48GFLOPS de capacidade de processamento presume-se que este terá dificuldade em rivalizar em capacidades aritméticas com o processador da Nvidia 9800 GTX que apresenta 648 GFLOPS como máximo teórico, mesmo que com um número aproximado de transístores (820 milhões no primeiro vs 750 milhões no segundo).

Os valores máximos teóricos apresentados no parágrafo anterior ofrma calculados considerando que cada núcleo do Xeon é capaz de executar quatro

11

instruções de vírgula flutuante em cada ciclo de relógio, pelo que com quatro núcleos a funcionar a 3GHz obtemos um total de 48GFLOPS. Para o GPU os cálculos são mais complexos, na medida em que há potencial de cálculo nas diversas unidades, eventualmente diferentes, do processador. No entanto, tomando os shaders da arquitectura unificada do Nvidia 9800GTX, os 16 multiprocessadores a funcionar a 1688GHz com 8 unidades SIMD podem processar uma multiplicação e uma multiplicação+adição em cada ciclo e chegar a um total de 648GFLOPS [35].

A definição de uma métrica como o FLOPS apresenta algumas vantagens importantes, nomeadamente o facto de definir um limite máximo ao processamento. Naturalmente os valores de desempenho efectivo dependem da aplicação e podem ser muito inferiores aos valores de pico mas determina, por exemplo, que o máximo de speedup que um GPU 9800GTX sobre um Xeon Quadcore 3.0 será de apenas 13,5x, bem longe dos speedups superiores a 2 ordens de grandeza que chegam a ser referidos. Perante esta relação entre o potencial dos GPUs e o CPUs passa a ser responsabilidade do programador optimizar as aplicações em ambos os contextos para o utilizar devidamente.

Figura 8. Evolução do desempenho dos CPUs da Intel e dos GPUs da AMD e da Nvidia [18]

A pergunta mais evidente perante esta constatação será certamente “mas porquê?”. Será que as capacidades aritméticas dos dispositivos são directamente proporcionais ao talento dos seus projectistas na Intel e na Nvidia? Certamente que não, pelo que de seguida se irão apresentar as diferenças fundamentais entre os CPUs e os GPUs e a forma como estas se manifestam na capacidade de processamento disponibilizado. Estes factos servirão certamente para justificar porque é que um núcleo de computação de um Intel Xeon actual utiliza mais de 200 milhões de transístores, enquanto um núcleo do processador G92 da Nvidia requer pouco mais de 5 milhões de transístores. Tal corresponde a afirmar que em 2,5% do espaço em circuito integrado o G92 consegue 40% do desempenho em vírgula flutuante de um Xeon Penryn (considera-se que os transístores para os restantes componentes, incluindo a memória, são divididos pelo número de núcleos de cálculo).

12

3.4 CPU vs GPU

Os CPU foram desenhados com o objectivo principal de optimizar um único fio de execução sequencial, sendo essa a sua especialidade. Tecnologias como o multi-threading e a inclusão de múltiplos núcleos de execução num único circuito integrado não alteram significativamente este panorama, uma vez que todos os núcleos são semelhantes, possuem grande complexidade interna e foram desenhados para funcionar autonomamente.

Os GPUs, por outro lado, foram desenhados para resolver rapidamente problemas relacionados com a computação gráfica que são, na sua maioria, trivialmente paralelizáveis, ou embaraçosamente paralelos. Mesmo no caso de os algoritmos serem sequenciais, o domínio dos dados é intrinsecamente paralelo, existindo as 3 ou 4 componentes de cores e 3 (ou 4) dimensões.

Enquanto a gestão da enorme variabilidade no comportamento das aplicações e dos padrões de acesso à memória levou a que os CPUs incluíssem processadores altamente elaborados com gestão da consistência de múltiplos níveis de memória cache de grandes dimensões, a evolução dos GPUs partiu muito rapidamente para um modelo de funcionamento massivamente paralelo com núcleos individuais simplificados mas de alto desempenho.

O facto de os GPUs se concentrarem nas tarefas paralelas destinadas, essencialmente, ao processamento gráfico, permite descartar as responsabilidades de um qualquer processador genérico em termos de gestão do hardware e do software de base. Em simultâneo, a exigência da aplicação primária muito exigente – os jogos – vem acompanhada de interfaces bem definidas como o OpenGL ou o DirectX, o que permitiu que os fabricantes pudessem implementar alterações radicais ao nível da estrutura interna que seriam inaceitáveis ao nível dos CPUs. Recorde-se que o conjunto de instruções original do 8086, com mais de trinta anos, continua a ser suportado nos CPUs Intel actuais.

A liberdade de desenho dos GPUs permitiu, ainda, utilizar tecnologias de acesso à memória com larguras de banda fenomenais e baixas latências que não podem ser utilizadas para a computação genérica.

Para além de um número elevado de unidades de processamento, os GPUs possuem a capacidade de gerir em hardware um número muito alargado de fios de execução. Enquanto os CPU suportam algumas dezenas/centenas de fios de execução escalonados pelo sistema operativo, os GPUs conseguem suportar, de forma eficiente, milhares de fios de execução em simultâneo[19][20]. Para obter essa eficiência, esses fios de execução são orientados aos dados, o sistema de memória é que estabelece as prioridades no escalonamento, partilham em alguns casos o apontador para a instrução actual (PC) e não possuem uma stack por thread.

13

A forma como são feitos os cálculos em vírgula flutuante acaba também por distinguir os CPUs dos GPUs. Enquanto a adopção da norma IEEE754 está garantida nos CPUs, nos GPUs os cálculos não respeitam esta norma, nem suportam precisão dupla. A generalidade dos GPU possui apenas 32 bits da precisão simples, embora alguns suportam apenas 24 bits ou 16 bits.

4 Modelos de programação dos GPUs

Tomando como ponto de partida os modelos de GPU com pipeline de funções fixas, diversos processos internos dos GPUs passaram a ser completamente programáveis e o próprio pipeline deixou de ser fixo. Inicialmente eram apenas os programas existentes nos vertex e nos pixel shaders, mas, actualmente, com o DirectX 10, além de ter surgido o shader de geometrias, estes passaram a ter o conjunto de instruções único e a assentar num modelo de shading unificado. Embora tal não obrigasse necessariamente a um modelo unificado a nível do hardware, tal veio a acontecer tanto com a Nvidia como com a ATI, como se apresentou anteriormente.

4.1 Computação orientada às sequências (streams)

Os GPUs podem ser vistos como processadores de sequências de dados que exigem o mesmo tipo de tratamento[21]. O processador executa uma sequência de operações sobre cada unidade nessa sequência de entrada e coloca os resultados numa sequência de saída. Desta forma, os shaders de vértices operam sobre um sequência de vértices enquanto os shaders de fragmentos operam sobre uma sequência de fragmentos.

O modelo de sequências é especialmente eficiente devido a que cada elemento independente pode ser executado em paralelo (o grau de paralelismo passa a ser uma opção da arquitectura subjacente) e os dispositivos podem esconder a latência dos acessos à memória executando apenas as tarefas que já possuem os dados disponíveis e fazendo a busca proactiva de dados.

Entre as funcionalidades fundamentais presentes nos GPUs[22] estão as seguintes: 1) memória de leitura através de acesso a texturas; 2) memória de acesso aleatório através de acesso indexado às texturas; 3) interpolação de dados; 4) valores temporários que podem ser guardados em registos locais nos processadores; 5) valores constantes guardados em registos constantes durante toda a execução do shader; 6) memória de escrita apenas conseguida através da escrita para texturas ou para o framebuffer; 7) operações aritméticas em virgula flutuante, incluindo a soma, subtracção, multiplicação e divisão; dada a frequência da utilização da multiplicação e da soma algumas arquitecturas possuem uma instrução, MADD, que executa ambas as operações num único

14

passo e que é normalmente considerada para definir um limite teórico para a capacidade de processamento.

Existem também diversas funcionalidades que foram excluídas[22], nomeadamente: 1) a stack; 2) a heap; 3) operações bit-a-bit sobre inteiros; 4) operações de scatter, uma vez que os fragment shaders apenas podem escrever para a endereço actual do pixel; 5) operações sobre toda a sequência de inputs, como os máximos, mínimos, médias, etc; 6) conjunto limitado de valores de saída por cada ponto. O facto de não existir suporte para stacks elimina também a possibilidade de se utilizarem funções recursivas.

As operações habituais dos CPUs como obter um valor de uma tabela, executar um ciclo sobre um conjunto de dados, ou guardar dados passam no GPU por operações como indexar uma textura, executar um shader sobre todos os pixéis de uma textura e guardar para uma textura/framebuffer, respectivamente. O arranque do programa ou rotina passa pelo desenho de um pixel, o que provoca o arranque da execução dos shaders.

4.2 Ferramentas de desenvolvimento de shaders

Partindo de uma plataforma em hardware torna-se necessário trazer para o nível do programador as ferramentas necessárias ao desenvolvimento das aplicações que tomam partido dos GPU.

As primeiras ferramentas surgiram, naturalmente, para dar suporte às aplicações gráficas como os jogos que, fruto do desejo de desenvolver capacidades cada vez mais apelativas e avançados, rapidamente aproveitaram o potencial dos GPU através da programação dos pixel shaders e dos fragment shaders. Antes de terem sido introduzidas as linguagens que a seguir se irão apresentar, os shaders eram codificados directamente em linguagem assembly de cada placa de cada fabricante, o que era um processo fastidioso, mais sujeito a erros de programação e de portabilidade muito limitada.

Figura 9. O pipeline gráfico do Cg [21]

15

C for Graphics (Cg)

A linguagem Cg foi criada pela Nvidia a partir da linguagem C com alguma inspiração de C++ e java. É independente da plataforma base e é compatível quer com OpenGL quer com Direct3D. Para suportar diferentes tipos de arquitecturas gráficas possui o conceito de perfil, que agrega características que garantidamente são suportadas em equipamentos desse perfil.

High Level Shading Language (HLSL)

O HLSL foi desenvolvido pela Microsoft e pela Nvidia para ser introduzido com o DirectX 9. É essencialmente a mesma linguagem que o Cg, mas faz parte da API DirectX, pelo que só compila com código Direct3D.

OpenGL Shading Language (GLSL)

O GLSL é também baseada em ANSI C, tendo sido mantidas as funcionalidades desta que eram compatíveis com o alto desempenho pretendido nos GPUs. Foram ainda aproveitadas algumas funcionalidades presentes no C++ como as funções com o mesmo nome mas tipos de dados de entrada diferentes e a declaração de dados no início dos blocos e não apenas no início da funções. Foram acrescentadas extensões para dar suporte a tipos de dados vectoriais e matriciais para um suporte acrescido às funções tipicamente necessárias nas operações gráficas. O GLSL faz parte da especificação OpenGL 1.4 e posteriores.

4.3 GPGPU

Tradicionalmente a utilização dos GPUs para computação genérica (GPGPU)[28] constitui um desafio à capacidade do programador transformar os conceitos utilizados na programação tradicional em conceitos que podem ser directamente processados pelo hardware do GPU. Os programadores podem utilizar as linguagens descritas no ponto anterior para construir os shaders que serão aplicados tal como se o utilizador estivesse a tratar de um ecrã gráfico. A juntar a esta transformação de modelos estão as restrições às funcionalidades implícitas a cada tipo de GPU. Construir aplicações eficientes considerando todos pontos não é certamente fácil, pelo que houve existe actualmente necessidade de formas mais expeditas de construir aplicações que aproveitem as capacidades dos GPUs. Entre os esforços neste sentido estão as plataformas Brook, Sh, o CTM e o CUDA, embora cada uma destas tenha objectivos muito distintos. Recentemente tem havido bastante interesse em

16

plataformas comerciais que estendem a optimização de código para além dos GPUs (em direcção às arquitecturas multi-core como o Cell), tais como o RapidMind (baseado no Sh) e o PeakStream (baseado no Brook). No entanto, neste trabalho serão referidos apenas os primeiros.

Brook

O sistema Brook[33] foi criado em Stanford com o objectivo de permitir aos utilizadores “não-gráficos” a utilização do GPU para cálculo numérico. Cria os kernels para executar em DirectX e OpenGL, bem como faz a gestão de todos os comandos gráficos em tempo de execução.

É baseado na linguagem C com extensões de streaming, o que lhe permite obter um desempenho de 80% a 90% de um programa desenvolvido especificamente para o GPU. O Brook suporta diversos tipos de backend, conforme o tipo de hardware onde as aplicações são executados.

Existe um grande número de aplicações que utilizam o Brook, número que deverá aumentar com suporte dado pela ATI e pelo backend CTM.

Sh

A plataforma Sh[34] consiste numa biblioteca que funciona como uma linguagem embedida da linguagem C++, para programar os GPUs e CPUs para uso genérico e para processamento gráfico.

Em 2004 foi criada a empresa RapidMind com o objectivo de comercializar o desenvolvimento do Sh, pelo que as novas versões deixaram de ser do domínio do código livre.

Close to Metal (CTM)

O CTM é uma biblioteca de baixo nível destinada a expor o conjunto de processadores paralelos subjacentes aos GPUs da ATI. Não está destinada a ser utilizada directamente pelo programador, mas para suportar plataformas de programação de mais alto nível como o Brook. Assim, e embora surja na mesma altura que o CUDA, não concorre directamente com este.

17

Figura 10. Comparação do speedup obtido num algoritmo de scan com o CPU vs OpenGL vs CUDA [38].

CUDA

A plataforma CUDA[36], de Compute Unified Device Architecture, apresenta o maior distanciamento dos modelos tradicionais de programação dos GPUs. Esta permite que se continue a trabalhar com os conceitos tradicionais enquanto se desenvolvem as aplicações que irão funcionar nos GPUs, sem incorrer na sobrecarga inerente às API gráficas ao permitir compilar directamente para o hardware. Foi desenvolvida pela Nvidia como uma plataforma baseada em C especificamente para dar suporte à computação genérica com equipamentos da geração 8800 e posteriores.

Embora seja uma tecnologia relativamente recente, tem gerado um interesse alargado por parte da comunidade, como se pode ver pelo conjunto de bibliotecas e literatura produzidos à sua volta[37]. Este interesse poderá ter origem no facto de trazer um conjunto de bibliotecas e exemplos particularmente interessantes, incluindo as bibliotecas numéricas normalizadas como as FFT e BLAS.

5 Aplicação de GPUs para cálculo genérico

Existe na literatura um conjunto muito vasto de aplicações nas mais variadas áreas do conhecimento que utilizam os GPUs para obter a capacidade de processamento necessária ao seu funcionamento. Uma vez que um levantamento exaustivo destas aplicações fica para além do que poderá ser apresentado neste trabalho, além de que existem estudos focados nessa perspectiva[18], apresentar-se-á a seguir algumas das áreas onde a utilização desta ferramenta trás vantagens significativas.

18

5.1 Algumas áreas de aplicação

As áreas potenciais para a aplicação do potencial de cálculo dos GPU, tal como para os CPUs, são imensas. A dificuldade está em, por uma lado, os problemas se adeqúem à natureza altamente paralela e vertical dos GPUs e, por outro lado, que sejam desenvolvidas aplicações que aproveitem devidamente esse potencial. A seguir apresentar-se-ão apenas uma breve descrição de algumas destas áreas e para mais detalhes ver [18].

A área da simulação de fenómenos físicos tem aproveitado o suporte numérico dos GPUs para acelerar a simulação de fenómenos dinâmicos como com as equações diferenciais parciais, como a convecção, a ebulição e a reacção difusão químicas e para a simulação da dinâmica de fluidos.

Na área do processamento de imagem partiu-se para a utilização de GPUs em operações como a segmentação de imagens para a identificação de características em imagens médicas, em especial. O problema subjacente está na identificação de superfícies 3D embebidas nos diversos cortes que constituem uma TAC ou uma ressonância magnética. Embora o processo não possa ainda ser totalmente automatizado, obtêm-se speedups de 10x nos cálculos de apoio aos processos iterativos de construção dos volumes.

Na área do processamento de sinal existem muitas iniciativas no sentido de aproveitar as capacidades de processamento numérico dos GPUs, em particular para o cálculo das transformadas de Fourier (FFT) e transformada do coseno discreto utilizada em compressão de imagens.

Na área da visão por computador existem iniciativas para utilizar o GPU para acelerar operações de projecção e a composição de imagens, bem como para extração de informação de profundidade a partir de múltiplas imagens.

Na área de iluminação global os GPUs existem diversas implementações de algoritmos como o Ray Tracing, Photon Mapping, radiosidade e sub-surface scattering, utilizando o GPU para obter desempenhos mais elevados ou maior qualidade nas imagens finais.

Na área da computação geométrica os GPUs têm sido utilizados em diversas aplicações, incluindo o cálculo de trajectórias, realidade virtual, modelação geométrica, cálculo dos caminhos mais curtos, detecção de colisões, computação de transparências e compressão de dados geométricos.

Outra área que tem despertado bastante interesse está na utilização de GPUs para optimizar ordenação de dados, inquéritos a bases de dados e para data mining. Os resultados com da execução de alguns scripts SQL chegam a ser uma ordem de magnitude superior ao tempo de execução nos CPUs.

19

5.2 Cálculo científico com GPUs

A optimização de operações básicas de cálculo científico, como as operações sobre de matrizes, apresenta um potencial de retorno fantástico para a utilização dos GPUs. As bibliotecas matemáticas como o BLAS e LAPACK são a base de muitos programas desenvolvidos para resolver problemas científicos importantes. A hipótese de que a utilização dos GPUs possa permitir ganhos ordens de grandeza superiores aos dos CPUs e que estes benefícios possam transmitidos a todas as aplicações que usam essas bibliotecas é extremamente interessante.

O facto do suporte ao formato IEE754 ser limitado e apenas de precisão simples levou a que surgissem algoritmos de correcção de precisão para os casos em que os 32 bits de precisão não são suficientes[23].

Tem-se verificado existir um interesse acentuado pelas bibliotecas científicas para GPUs [37] e espera-se que com a disseminação de tecnologias como o CUDA o número de implementações optimizadas venha a aumentar significativamente. Para além das já referidas bibliotecas numéricas, é de salientar a disponibilização de suporte GPU para as bibliotecas VMD, NAMD, FFT e para bibliotecas de redes neuronais, dinâmica molecular, etc.

Há problemas de cálculo científico que tentam consumir todos os recursos de computação disponíveis, simplesmente porque são problemas complexos que estão limitados pela capacidade de processamento. Caso surge alguma capacidade adicional, mais do que terminar as aplicações em menos tempo, poder-se-á incluir mais factores que não puderem ser considerados anteriormente por falta de capacidade.

No entanto, as aplicações científicas são muitas vezes executadas em clusters HPC que não possuem os GPUs a que os postos de trabalho individuais têm acesso. No entanto, surgem cada vez mais clusters computacionais que incluem nós com essa capacidade. Através do cluster Search [39], em particular, os investigadores da Universidade do Minho têm acesso a 8 nós de computação “dual core” com GPU Nvidia 7900GT com 256MB de RAM e a 6 nós de computação “quad core” com GPU Nvidia 8800GT com 1GB de RAM, que pretendem dar suporte à pesquisa de novas formas de explorar o potencial dos GPUs.

As limitações mais sentidas nesta direcção estão na já referida dificuldade de programar aplicações genéricas sobre o paradigma da computação gráfica e nas limitações a nível do comportamento dos GPUs no cálculo em vírgula flutuante. Enquanto a primeira foi significativamente aliviada com a disponibilização da plataforma CUDA, o mesmo não acontece com a segunda que só irá ser resolvida com a próxima geração de GPUs.

20

6 Perspectivas de desenvolvimentos futuros

A utilização de GPUs enquanto co-processadores aritméticos para computação genérica ainda está longe de estar esgotada enquanto tema de investigação em diversas vertentes.

Em primeiro lugar, só agora começam a surgir modelos de programação fáceis de utilizar e adequados à exploração do GPU para trabalhos não orientados a gráficos[27].

Em segundo lugar, a provável inclusão dos GPUs como núcleos de processamento em CPU altera radicalmente os pressupostos de funcionamento dos GPUs, em particular no que concerne aos acessos à memória gráfica. A passagem dos processadores como o Fusion [25] para eventuais variantes de desempenho constituirá certamente um importante marco na evolução dos GPUs, bem como o será a forma de compatibilizar no mesmo pacote processadores exigentes com características internas tão distintas.

Em terceiro lugar, para além da referida inclusão dos GPUs nos CPUs, poderá até chegar o momento em que a evolução dos CPUs em termos de processamento paralelo evolua de tal forma que deixe de fazer sentir-se a necessidade dos GPU, podendo as tarefas dos GPUs ser absorvidas pelos processadores genéricos tal como aconteceu anteriormente com os co-processadores de vírgula flutuante.

Figura 11. Arquitectura prevista para um processador Intel com GPU integrado, SMT [24]

21

7 Conclusão

A computação paralela é fatal. Não é letal, mas é certamente o destino da evolução dos sistemas de computação de alto desempenho. Estranhamente, ainda há poucos anos empresas como a Intel apontavam para um futuro com processadores únicos a funcionar a frequências cada vez mais elevadas[26], atingindo 10GHz em 2011. Nessa altura a única medida de desempenho eram os Hz, enquanto agora a proliferação de múltiplos núcleos parece o caminho a seguir. A responsabilidade por este desvio foi atribuído à diminuição do consumo, mas talvez as provas dadas pelos GPUs tenham tido também alguma influência.

Figura 12. Arquitectura prevista para o processador Intel Nahelem-EX Octo-core, SMT [24]

Os GPUs constituem o primeiro dispositivo de computação massivamente paralela de baixo custo, que surgiu pela libertação dos constrangimentos inerentes às aplicações sequenciais. Torna-se possível constatar na prática, através deles, que as aplicações e os seus programadores se podem adaptar a sistemas altamente paralelos desde que haja motivação para isso. No mesmo caminho surgiram consolas de como a PlayStation2 que com o seu processador Cell também banalizaram os sistemas paralelos

Actualmente a utilização dos GPUs para cálculos genéricos parece ser um acto de racionalidade. As dificuldades ao nível da sua programabilidade têm vindo a diminuir com plataformas como o CUDA que veio trazer um ambiente de programação muito mais próximo do que os programadores estão acostumados. A acrescentar a isto temos o facto de serem dispositivos com uma relação custo/desempenho extraordinariamente baixa.

22

Bibliografia

[1] Top500 Supercomputer Sites, http://www.top500.org [2] CESGA – Centro de Supercomputación de Galicia, http://www.cesga.es [3] A. Petitet, R. C. Whaley, J. Dongarra, A. Cleary , HPL - A Portable

Implementation of the High-Performance Linpack Benchmark for Distributed-Memory Computers, http://www.netlib.org/benchmark/hpl/

[4] Gene Amdahl, "Validity of the Single Processor Approach to Achieving Large-Scale Computing Capabilities", AFIPS Conference Proceedings, (30), pp. 483-485, 1967.

[5] Jim Held, Jerry Bautista, Sean Koehl, “From a Few Cores to Many: A Tera-scale Computing Research Overview”, Intel Corporation White Paper, 2007.

[6] Sun Microsystems, UltraSPARC T2 Processor, http://www.sun.com/processors/UltraSPARC-T2/

[7] AMD Corporation, Phenon X4 Quad-core, http://www.amd.com/us-en/Processors/ProductInformation/0,,30_118_15331_15332,00.html

[8] IBM Corporation, The Cell project, http://www.research.ibm.com/cell/ [9] Ofri Wechsler, “Inside Intel Core Microarchitecture Setting New

Standards for Energy-Efficient Performance”, Tecnology@Intel, Mar. 2006

[10] Los Alamos National Laboratory, Roadrunner, “Roadrunner breaks the 1pflops barrier”, 9 May 2008, http://www.lanl.gov/roadrunner/

[11] Jakub Kurzak, Jack Dongarra, “Implementation of mixed precision in solving systems of linear equations on the Cell processor: Research Articles”, Concurr. Comput. : Pract. Exper., 19(10), 2007.

[12] Wikipedia, 5 May 2008, http://en.wikipedia.org/wiki/Coprocessor [13] ClearSpeed Advance X620 and e620 Accelerator Boards,

http://www.clearspeed.com/products/cs_advance/ [14] Wikipedia, Accelerated Graphics Port, http://en.wikipedia.org/wiki/AGP [15] AMD, Torrenza Initiative: Creating a Community of Collaboration,

http://enterprise.amd.com/us-en/AMD-Business/Technology-Home/Torrenza.aspx

[16] Hypertransport Consortium, http://www.hypertransport.org/ [17] PC Watch, http://pc.watch.impress.co.jp/docs/2006/0713/kaigai287.htm [18] John D. Owens, David Luebke, Naga Govindaraju, Mark Harris,Jens

Krüger, Aaron E. Lefohn, and Tim Purcell. “A Survey of General-Purpose Computationon Graphics Hardware.” Computer Graphics Forum, 26(1):80–113, Mar. 2007

[19] Nvidia Corporation, Nvidia GeForce 8800m Techspecs, http://www.nvidia.com/object/geforce_8800m_tech_specs_br.html

[20] AMD Corporation, “Radeon X1800 Shader Architecture”, White Paper

23

[21] Timothy J. Purcell, Ian Buck, William R. Mark, and Pat Hanrahan. “Ray tracing on programmable graphics hardware.” ACM Transactions on Graphics, 21(3):703-712, Jul. 2002.

[22] Dirk Vanden Boer, “General-Purpose Computing on GPUs”, Masters thesis, School of Information Technology, Transnationale Universiteit Limburg, Jun. 2005.

[23] Accelerating Double Precision FEM Simulations with GPUs, Dominik Göddeke, In Proceedings of ASIM 2005 - 18th Symposium on Simulation Technique, Sept. 2005.

[24] Hiroshige Goto, PC Watch, http://pc.watch.impress.co.jp/ [25] Wikipedia, AMD Fusion, http://en.wikipedia.org/wiki/AMD_Fusion [26] Geek.com, “Intel predicts 10GHz chips by 2011”, Jul 2000,

http://www.geek.com/intel-predicts-10ghz-chips-by-2011/ [27] William Mark, “Future Graphics Architectures”, GPUs for Parallel

Programming, ACM Queue, Vol. 6, No. 2 – March/April 2008 [28] General Purpose Computing using Graphics Hardware,

http://www.gpgpu.org [29] Ian Buck, Tim Foley, Daniel Horn, Jeremy Sugerman, KayvonFatahalian,

Mike Houston, Pat Hanrahan, “Brook for GPUs: Stream Computing on Graphics Hardware”,

[30] Emmet Kilgariff, Randima Fernando, “The GeForce 6 Series GPU Architecture”, GPU Gems 2, 2005

[31] Tomas Akenine-Möller, Jacob Ström, “Graphics Processing Units for Handhelds”, Proceedings of the IEEE, 96(5), May 2008

[32] Nvidia Corporation, “NVIDIA GeForce 8800 GPU Architecture Overview”, Nov 2006

[33] BrookGPU, http://graphics.stanford.edu/projects/brookgpu/ [34] Sh Embeded Metaprogramming Language, http://libsh.org/ [35] Hardware-Infos, “Geforce GTX 280 und 260: Finale Taktraten?”,

http://www.hardware-infos.com/news.php?news=2090 [36] Nvidia Corporation, CUDA Zone, http://www.nvidia.com/cuda [37] Nvidia Corporation, CUDA Showcase,

http://www.nvidia.com/object/cuda_showcase.html [38] John D. Owens, Mike Houston, David Luebke, Simon Green, John Stone,

James Phillips, “GPU Computing”, Proceedings of the IEEE, 96(5), 2008 [39] Universidade do Minho, Cluster Search, http://www.di.uminho.pt/search [40] Wikipedia, ATI Radeon HD 3800,

http://en.wikipedia.org/wiki/Radeon_R600#Radeon_HD_3800