Uma série de notas sobre a programação usando GPUs e o OpenCL:
1) Método extremamente sensível, passível de erros: tive algumas surpresas desagradáveis ao escrever programas básicos em OpenCL por causa da sensibilidade da linguagem de programação em relação à estrutura geral do programa. Para deixar um pouco mais claro, a programação em OpenCL consiste em escrever um arquivo central ("kernel") onde o processamento paralelo é realizado. Este arquivo central possui uma terminação diferente dos demais arquivos em C/C++ e é compilado de forma diferente do resto do programa. Assim, a estratégia geral de escrever um arquivo em C/C++ com parâmetros e estruturas do problema não funcionam para o "kernel", já que a passagem destas definições para o arquivo não é direta. Mais do que isto, é necessário repassar as definições exatamente na mesma ordem, com exatamente os mesmos valores das definições gerais, mesmo que estas não estejam em uso diretamente no "kernel". Caso contrário, o programa pode transferir informações erradas para o GPU, comprometendo todo o programa. Conversando com um amigo formado em ciências da computação, ele me explicou que a compilação de programas em OpenCL (e CUDA, também) não passa por uma série de verificações e avaliações que os programas em C/C++ normalmente passam para conferência da consistência interna do programa. Daí a possibilidade de erros.
2) Aprenda a usar a memória local! É bastante fácil montar um "kernel" básico para operações simples e escrever programas mais complexos a partir desta estrutura básica. Entretanto, os ganhos significativos de velocidade acontecem exatamente quando o programador aprende a explorar a memória local do GPU. Os ganhos devem-se, basicamente, a dois fatores: por um lado, usando memória local, reduz-se a transferência de informações contidas na memória do CPU para a memória do GPU (e isto gasta muito tempo); por outro lado, a memória local da GPU, apesar de normalmente menor em termos de capacidade, é de acesso muito mais rápido que a memória do CPU. Este último ponto é uma questão de arquitetura do GPU, já que estas placas são feitas para "devorar dados", ao invés de guardar informações. Assim, o pouco de memória existente nas placas funciona apenas como um buffer provisório para agilizar o processamento. Eu ainda não tenho controle total sobre as operações com a memória local, mas já consigo fazer alguns procedimentos básicos para acelerar os meus cálculos.
3) OpenCL ou CUDA? A Nvidia gastou um bocado para tentar impor a sua linguagem de acesso aos GPUs, com sucesso aparente até agora. Entretanto, dá para notar uma lenta convergência, ou ao menos um equilíbrio, em relação ao OpenCL hoje em dia. Por exemplo, a AccelerEyes criou o Jacket (uma interface para integrar a computação via GPU ao Matlab) inicialmente apenas baseado no CUDA. Hoje abri a página e pude verificar que o programa foi expandido para também trabalhar com OpenCL. Além disto, um pacote de rotinas para GPU, chamado de ArrayFire, foi desenvolvido tanto em CUDA quanto OpenCL. A tendência? Se a Nvidia não generalizar o CUDA para também trabalhar com outras placas de vídeo que não as da própria marca, acho que o OpenCL vira o padrão da indústria.
4) OpenCL e CUDA são a panacéia, o emplastro Brás Cubas disponível para resolver todos os males da computação lenta? De forma alguma! Como eu falei anteriormente, existe um custo para transferir informações entre a memória do CPU e a memória do GPU. Este custo pode ser tão significativo que o processamento via GPU torna-se mais lento que o uso serial da máquina. Como um exemplo, no programa que gerou os resultados mostrados no último post, eu precisei em dado instante obter o somatório dos valores de um vetor. Realizar esta soma no CPU ou no GPU era absolutamente indiferente, já que o tamanho do vetor não compensava o uso do GPU na operação (o maior vetor tinha 80000 valores). Várias outras operações que poderiam ser paralelizadas também foram mantidas no CPU por causa da pouca diferença nos resultados entre GPU e CPU. Eu precisaria, e ainda pesquiso a respeito, algumas soluções muito criativas para manter o problema em questão mais tempo no GPU e ganhar tempo através da redução das transferências de dados entre CPU e GPU.
5) Para quando os resultados? Eu vou quebrar o trabalho em dois artigos, com o primeiro apenas explicando como fiz as computações e o outro detalhando o problema do filtro de partículas. Espero ter uma primeira versão de ambos pronta para circular em março.
Saudações!
Nenhum comentário:
Postar um comentário