Programação Multicore com OpenMP

Hoje em dia é normal ouvir do vendedor “Esse computador é ótimo! Tem 4 GB de RAM e é um Core Duo!“. Apesar de, na maioria das vezes, a pessoa nem fazer idéia do que diabos é um Core Duo. Mas tudo bem, esse é o papel do vendedor, certo? É claro que qualquer entendido sabe que isso não qualifica o produto como “bom“, longe disso, temos que levar em conta muitos outros detalhes que pessoas que não são da área não entendem.
Com a chegada dos processadores de múltiplos núcleos, o marketing direcionou-se à esse fator e esqueceu de que memória cache e barramento contam MUITO no desempenho. A maioria das pessoas não entendem, por exemplo, que um processador de 2 núcleos de 2.0 GHz NÃO significa 4.0 GHz ou que um sistema operacional de 32 bits não aproveita as vantagens do hardware de 64 bits.
O que me deixa triste é ouvir esse tipo de enganação picaretagem de amigos/conhecidos da faculdade ( Tecnologia em Informática com Ênfase em Gestão de Negócios – FATEC Rio Preto). By the way, não quero desmerecer a faculdade, pois devo muito à vários professores que tiveram a paciência de aguentar alunos como eu e o bomba.
Antes de entrar no assunto principal, entenda que não adianta ter um processador com 487564764 núcleos se o software não sabe como utilizá-los, okay?
Como recentemente adquiri um Phenom X3 (é, 3 núcleos) resolvi descobrir como aproveitar todo esse poder. Com uma busca aqui e uma ali (no oráculo), trago o que aprendi.
OpenMP?
O OpenMP (Open Multi-Processing) é uma API aberta para programação paralela. De acordo com o site oficial:
A API OpenMP auxilia na programação multiplataforma, paralela e com memória compartilhada em C/C++ e Fortran em todas as arquiteturas, incluindo plataformas Unix e Windows NT. Constituída por um grupo de grandes empresas de hardware e software, o OpenMP é um modelo portável e escalável que oferece ao programador uma interface flexível para o desenvolvimento de aplicações paralelas visando desktop até supercomputadores.
A tradução no olhômetro ficou um lixo, mas dá pra ter uma idéia do que se trata.
Beleza, nunca ouvi falar. Vale a pena alocar recursos (tempo) para aprender?
Saiba que essa API (pelo menos a especificação) existe desde 1997, sendo os membros permanentes do Architecture Review Board (ARB):
- AMD (David Leibs)
- Cray (James Beyer)
- Fujitsu (Matthijs van Waveren)
- HP (Uriel Schafer)
- IBM (Kelvin Li)
- Intel (Sanjiv Shah)
- NEC (Kazuhiro Kusano)
- The Portland Group, Inc. (Michael Wolfe)
- SGI (Lori Gilbert)
- Sun Microsystems (Nawal Copty)
- Microsoft (-)
Convencido(a)?
Instalando o OpenMP
É simples, você não instala.
Como assim?
Você DEVE ter um compilador que tenha o OpenMP implementado.
E o meu compilador implementa?
Aqui vc pode ver a lista de compiladores que disponibilizam o recurso. Como, provavelmente, vc usa:
- Visual C++ 2008: apenas a versão Professional (ou sei lá o nome da versão que não seja a Express) possui os headers necessários. ACHO que se vc copiar os headers da versão paga para a Express, irá funcionar;
- Code::Blocks (MinGW): apenas nas versões (do gcc) 4.3.2 ou superiores.
Baixei o MinGW mais recente no site oficial e instalei para usar com o Code::Blocks. Porém, a versão do gcc era a 3.4.alguma-coisa e é claro, não funcionou pois não possuia os headers necessários.
Então, resolvi instalar logo o TDM’s GCC/MinGW que é uma build não oficial dos binários mais recentes do GCC. Faça o download do instalador mais recente e, na hora da instalação, escolha o diretório MinGW que está dentro do diretório de instalação do seu Code::Blocks (no meu caso, E:\CodeBlocks\MinGW):

Clique em “Next >” e selecione um mirror:

Clique em “Next >” e selecione apenas os componentes que deseja instalar. No meu caso, deixei como está (todos):

Após selecionar os componentes, clique em “Install” e espere a extração dos arquivos terminar.
Hello World!
Se tudo estiver certo, então será possível compilar o “Hello World” abaixo:
#include <omp.h>
#include <stdio.h>
int main()
{
#pragma omp parallel
printf("Hello! (from thread %d, num. threads %d)\n",
omp_get_thread_num(),
omp_get_num_threads());
}
Antes de tentar rodar o exemplo, precisamos definir a compile-time flag -fopenmp para que o compilador saiba que o projeto utiliza o OpenMP. Se vc compilar pelo prompt de comando diretamente com o GCC, basta adicionar o parâmetro -fopenmp, ficando assim:
gcc.exe hello_world.c -o hello_world.exe -fopenmp
Já no Code::Blocks, vc também precisa linkar a libgomp.dll.a e ter o arquivo libgomp-1.dll na pasta BIN do seu compilador ou na pasta do seu projeto. Então, faça assim:
- Selecione a opção “Build options…” no menu “Project” do Code::Blocks;
- Na lista da esquerda, selecione o seu projeto (o 1º item, não o Debug, nem o Release);
- Procure a aba “Other options” dentro da aba “Compiler settings”. Adicione a flag -fopenmp;

- Agora selecione a aba “Linker settings” e adicione o arquivo libgomp.dll.a. Na minha instalação, esse arquivo estava no diretório “E:\CodeBlocks\MinGW\lib\gcc\mingw32\4.4.1\“;
- É importante que vc linke o arquivo libgomp.dll.a e não o libgomp.a;
- O projeto já pode ser compilado, mas para que funcione, será necessário copiar o arquivo libgomp-1.dll para o diretório do seu executável. Assim como a lib, o diretório em que esse arquivo pode ser encontrado não é tão trivial. Na minha instalação, encontrei em “E:\CodeBlocks\MinGW\lib\gcc\mingw32\bin“;
- Creio que não é necessário explicar a parte de copiar o arquivo (CTRL + C, CTRL + V, a mesma coisa que vc usa para fazer os trabalhos da faculdade).
Btw, GOMP é o projeto que implementou o OpenMP nos compiladores C, C++ e Fortran do GNU Compiler Collection.
Pronto, o Hello World deve (ou deveria) mostrar algumas mensagens “Hello!” com o ID da thread em que está sendo executado, seguido do número de threads. Aqui tive o seguinte resultado:

Mas por que 3 threads? Porque esse é o número padrão para a variável ambiente OMP_NUM_THREADS (na minha implementação, pelo menos). Para definir o número de threads, chame a função omp_set_num_threads(num_threads). Então, o “Hello World” modificado ficaria assim:
#include <omp.h>
#include <stdio.h>
int main()
{
omp_set_num_threads(10);
#pragma omp parallel
printf("Hello! (from thread %d, num. threads %d)\n",
omp_get_thread_num(),
omp_get_num_threads());
}
Agora vc verá 10 mensagens “Hello!”.
É só isso?
Não. Apesar de simples, o exemplo anterior usa eficientemente todos os núcleos disponíveis, de forma transparente. Pode parecer que não houve um grande ganho no desempenho, mas com alguns testes vamos ver que a programação multicore pode otimizar de forma significativa o aplicativo.
Para que o post não fique cansativo, deixo o resto para semana que vem.
Fonte: OpenMP.org
ótimo post!!
me ajudou muuuito!
sou iniciante e não estava conseguindo compilar pelo gcc!!
Resolveu meu problema!!
Mesmo sendo tão simples a solução, não havia encontrado referências!!!
Valeu mesmo!!
Fernando
14 nov 09 at 12:20
Muito obrigado Leandro!
Sou pesquisador em Bionformática da UFPR, e agora estou começando a me aventurar na programação paralela a fim de maximizar o desempenho de alguns programas; seus posts a respeito de OpenMP esclarecem muita coisa.
A maioria do material que encontro na net tem uma linguagem um tanto quanto avançada pra mim, enquanto suas explicações são extremamente claras fazendo uso dos exemplos.
Novamente obrigado pelas ótimas explicações!
Eduardo Tieppo
26 fev 10 at 9:05