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 foi direcionao à  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?

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.

openmp.org

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?

leitor angustiado

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 (-)

(Retirado do site oficial)

Convencido(a)?

Instalando o OpenMP

É simples, você não instala.

Como assim?

leitor confuso

Você DEVE ter um compilador que tenha o OpenMP implementado.

E o meu compilador implementa?

leitor esperançoso

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):

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.

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