Comparativo de Arquiteturas Paralelas

Neste artigo vou fazer uma comparação entre as capacidades de paralelismo presentes no processador AMD Phenom II X3 720 (Arquitetura K10) e na GPU nVidia GeForce 9600GT (Architetura G94).

O K10

A série Phenom II é uma linha de processadores (CPUs) multi-core de propósito geral da AMD compatíveis com o conjunto de instruções x86. O X3 720 é composto de na verdade de 4 cores de processamento, no entanto, como o nome indica, apenas 3 estão habilitados, devido a falhas detectadas na fabricação, ou simplesmente para atender à demandas de produção. Em alguns espécimes, o core desabilitado pode ser re-ativado via firmware, permitindo seu uso (com o risco de anomalias causadas por defeitos de fabricação.)

Cada core possui 64KB de cache L1 de dados e de instrução, e mais 512KB de cache L2 que pode ser utilizado para ambos, totalizando 640KB de memória dedicada por core. Além disso, o processador como um todo possui 6MB de memória cache L3 compartilhada entre todos o cores ativos. Isso totaliza 8064KB (ou 8704KB caso os 4 cores estejam ativados) de memória local integrada no processador.[cpuz] Cada core comunica-se apenas com seus caches locais, e estes com o cache L3. Todos as leituras/escritas na memória global (DDR3) tem de passar pelo cache L3, que se comunicada com o controlador de memória integrado na CPU.[k10] A coerência de dados entre os caches de cada core é gerenciada automaticamente pelo hardware através de mensagens entre cada core para fazer a invalidação e travamento de linhas de cache.

Em termos de computação, cada core da CPU é praticamente independente dos outros, não compartilhando recursos de processamento, apenas o cache L3. Portanto, cada core pode executar operações de forma autônoma. Cada core tem, dentro de si mesmo, alguma forma de paralelismo, pois pode executar várias instruções simultaneamente (paralelismo em nível de instrução), sendo um processador super-escalar.[k10] Também pode executar a mesma instrução sobre vários dados ao mesmo tempo (de vários tipos) usando as instruções SSE, permitindo por exemplo, operar em 4 itens ponto-flutuante precisão simples ao mesmo tempo.

O processador possuí uma grande quantidade de lógica em hardware responsável por realizar execução especulativa, reordenação de operações, predição de branches, entre outras operações complexas, que visam manter a alta performance e acomodar código não ótimo ou que por natureza possui muitos branches. Essa hardware ocupa uma quantidade relativamente grande de espaço no chip, que aumenta a performance de certos tipos de código comuns, ao custo de poder de processamento puro.[k10]

A GPU

A série GeForce 9000 é a segunda linha de GPUs de arquitetura unificada da nVidia, isto é, a arquitetura utiliza os mesmo recursos para processar vértices e fragmentos quando utilizada para gráficos, tarefa que era realizada por hardware diferente e especializado em designs anteriores. Isto resulta em melhor eficiência de utilização dos recursos disponíveis, além de igualar as capacidades disponíveis para shading de vértices ou fragmente. Mas, mais relevante para nosso caso, esses recursos de processamento agora tem propósito muito mais geral, e podem ser usados para processar outros tipos de dados de forma muito mais sensata e eficiente. Muitos dos modelos da série 9000 são simplesmente renomeamentos de modelos equivalentes da série anterior, a 8000. A 9600GT é baseada no chip G94, uma versão retrabalhada de um chip da série anterior, e portanto não se enquadra nisso.[gf9]

O poder de processamento da GPU é organizado da seguinte forma: A unidade básica de processamento é denominada "thread", e cada um tem seus próprios registradores de dados. Threads são aglomerados em "warps", que são o menor bloco que pode ser executado ao mesmo tempo, contendo 32 threads cada.[cuda-spec] Os threads individuais não possuem ponteiro de instrução próprio, ele está presento no warp. Portanto, todos threads em um mesmo warp tomam o mesmo caminho de execução no programa, embora possivelmente com dados diferentes. (Neste quesito são similares a arquiteturas SIMD em CPUs normais.) Threads são também agrupados em "thread groups", que podem ter tamanho variável, definido pelo usuário.[ryg][cuda-c]

Cada thread tem seus próprios registradores, assim como 16KB de memória local ao thread. Cada thread group também compartilha outro espaço de 16KB de memória. Além disso, também é permitida a leitura e escrita diretamente para a memória global da GPU. (Essas operações são naturalmente muito mais lentas do que acesso às memórias locais.) A sincronização dessas memórias é feita ou de forma implícita (não é possível ocorrer mais de um acesso a mesma parte da memória ao mesmo tempo) ou fazendo uso de diversos comandos de 'barreira' que esperam certos eventos acontecerem.[ryg] Somando-se a essas memórias de escrita/leitura, existe uma área de memória de constantes de 64KB, e unidades de leitura de texturas.[cuda-c][cuda-spec] O gerenciamento e uso apropriado das vários áreas de memória disponíveis é uma parte importante da programação eficiente de GPUs.

Comparadas com CPUs modernas, as GPUs investem muito mais hardware em poder de processamento puro, requerendo cuidado em sua programação e sendo mais apropriadas para quando grandes quantidades de dados possam ser processados de maneira coerente, mas trazendo grandes vantagens em termos de velocidade e eficiência nesse caso.

Conclusão

Os dois processadores analisados diferem muito em sua arquitetura. A CPU é arquitetada de forma que possa executar uma variedade de tarefas, mesmo aquelas que envolvem muitas decisões e caminhos de execuções divergentes, além de criar a ilusão de uma única área de memória, que pode ser utilizada de forma homogênea para qualquer propósito, de forma relativamente eficiente. (Até certo ponto, quando se precisa prestar atenção nesses detalhes para se realizarem otimizações.) Isso é refletido em sua construção: pouquíssimos módulos autônomos e poderosos.

A GPU, por outro lado, segue a filosofia contrária: É composta de centenas de unidades de execução simples, que trabalham paralelamente para realizar cálculos, com o programador precisando cuidadosamente gerenciar as várias áreas de memória disponíveis de maneira apropriada, algumas vezes requerendo uma significante mudança no algoritmo utilizado para outro que se adequa melhor ao modelo de programação da máquina. No entanto, exatamente por utilizar esse modelo de 'trabalhadores' simples, pecam na execução de certos tipos de tarefas que por sua natureza divergem nos caminhos de código que tomam, ou utilizam acessos semi-aleatórios à memória.

Referências