Olá Mundo Paralelo com MPI

MPI é a sigla para Message Passing Interface, um padrão de comunicação de dados para computação paralela. O MPI oferece diversas abstracções que facilitam e padronizam o desenvolvimento de aplicações paralelas. Por exemplo, você pode programar para vários processadores, nós de um cluster, supercomputadores ou Internet utilizando a mesma infraestrutura transparentemente.

Supercomputador Nasa
Cluster Columbia da NASA, com 1024 nós.

Como MPI é um padrão, existem vários padrões de implementação, abertas, fechadas, comerciais ou gratuitas. MPI é definido a princípio para C e Fortran, mas há implementações em outras linguagens como Java ou Python, por exemplo. A implementação que eu vou utilizar nesse exemplo é a OpenMPI.

A notícia boa é que você não precisa ter um supercomputador em casa para aprender e praticar computação paralela, uma máquina doméstica serve. Se você tiver uma máquina com múltiplos processadores, melhor ainda.

Instalação

Para instalar um ambiente de desenvolvimento para MPI no Ubuntu Linux basta um comando:

sudo apt-get install build-essential openmpi-dev

Isso vai instalar um conjunto básico de compiladores e o ambiente OpenMPI.

O código

Vamos criar um arquivo chamado ola.c com o conteúdo:

#include
#include
int size, rank;
int main(int argc, char *argv[]){
   MPI_Init(&argc,&argv);
   MPI_Comm_size(MPI_COMM_WORLD,&size);
   MPI_Comm_rank(MPI_COMM_WORLD,&rank);
   printf("Oi. Eu sou o processo %d de %d\n", rank, size);
   MPI_Finalize();
}

Compilação

Para compilar esse código vamos usar o comando mpicc que foi instalado junto com o pacote openmpi-dev. Ele é uma interface para o gcc, e vai cuidar de toda a linkagem com as bibliotecas do MPI. Você pode usar os parâmetros do gcc com o mpicc.

mpicc ola.c -o ola

Se tudo der certo esse comando vai criar o binário ola.

Execução

Outra ferramenta importante é o mpirun, que levantar o mpi nos diversos nós e mandar cada nó executar o binário. O mpirun não precisa de um programa mpi para rodar, por exemplo, se dermos esse comando:

mpirun -np 4 echo oi

Você vai ter essa saída:

oi
oi
oi
oi

Você mandou 4 nós (-np 4) executar o comando echo oi (imprime oi). Para mandar 5 nós executarem nosso binário ola:

mpirun -np 5 ola

E vamos ter uma saída mais ou menos assim:

Oi. Eu sou o processo 1 de 5
Oi. Eu sou o processo 4 de 5
Oi. Eu sou o processo 0 de 5
Oi. Eu sou o processo 2 de 5
Oi. Eu sou o processo 3 de 5

Por que as saídas sairam desordenadas? Porque elas rodaram em paralelo e não temos como saber qual foi sua ordem de execução. Assim cada nó entrou no printf em um momento diferente e imprimiu seu rank e seu size naquele momento. Você pode experimentar usar o parâmetro -np com outros números maiores ou menores que 5.

Troca de Mensagens

Até aqui não há muita graça porque não há troca de mensagens. Há muito o que se dizer sobre como trocar mensagens do MPI mas a maneira mais fácil de se começar é com a função mpi_send.

Vamos fazer um programa bem simples onde o nó 0 vai mandar uma mensagem para o nó 1. A mensagem vai ser um número, 42. Criemos um arquivo chamado msg.c com o código:

#include
#include
 
int size, rank, msg, source, dest, tag;
 
int main(int argc, char *argv[]){
   MPI_Status stat;
 
   MPI_Init(&argc,&argv);
   MPI_Comm_size(MPI_COMM_WORLD,&size);
   MPI_Comm_rank(MPI_COMM_WORLD,&rank);
 
	if(rank==0){
   	msg = 42; dest = 1; tag = 0;
   	MPI_Send(&msg, 1, MPI_INT, dest, tag, MPI_COMM_WORLD);
   	printf("Processo %d enviou %d para %d.\n", rank, msg, dest);
	}
 
	if(rank==1){
		source = 0; tag = 0;
		MPI_Recv(&msg, 1, MPI_INT, source, tag, MPI_COMM_WORLD, &stat);
		printf("Processo %d recebeu %d de %d.\n", rank, msg, source);
	}
 
   MPI_Finalize();
}

No processo de rank 0 vamos enviar o conteúdo da variável inteira msg para o processo de rank 1. Note que no processo de rank 1, o valor de msg não está definido. O comando MPI_Send vai receber 6 parâmetros.

int MPI_Send( void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)

  • void *buf, um ponteiro para a mensagem que você vai mandar. No nosso caso a variável inteira msg.
  • int count, a quantidade de elementos que tem nessa mensagem. No nossa caso só 1. Se quisemos mandar um vetor de dois inteiros, seria 2.
  • MPI_Datatype datatype, uma constante que define o tipo de dados que você está enviando. No nosso caso MPI_INT. Isso evita que ajam incompatibilidade no tamanho de inteiros entre arquiteturas diferentes.
  • int dest, o rank do nó destino, o destinatário. No nosso caso o nó 1.
  • int tag, a tag seria num email o assunto da mensagem. Estamos mandando tag 0 então no outro lado tem que estar esperando uma tag 0, caso contrário não há comunicação.
  • MPI_Comm comm, o comunicador. Nesse e na maioria dos casos a constante MPI_COMM_WORLD.

Do outro lado, no processo 1 vamos usar o MPI_recv, que recebe 7 parâmetros.

int MPI_Recv( void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)

  • void *buf, um ponteiro para onde vai ser guardada a mensagem que vamos receber. No nosso caso a variável msg, que no processo 1 está vazia.
  • int count, a quantidade de elementos que vem nessa mensagem.
  • MPI_Datatype datatype, a mesma constante do MPI_send.
  • int source, o rank do nó remetente. No nosso caso o nó 0.
  • int tag, a tag da mensagem conforme explicado no MPI_send.
  • MPI_Comm comm, o comunicador.
  • MPI_Status *status, uma estrutura para que depois que a função for executada você possa inspecionar detalhes da transmissão. No nosso caso ela é inútil.

Para compilar esse exemplo usamos novamente o mpicc.

mpicc msg.c -o msg

E para executa-lo o mpirun.

mpirun -np 2 msg

O programa vai escrever essa mensagem:

Processo 0 enviou 42 para 1.
Processo 1 recebeu 42 de 0

No processo 1 a msg estava inicialmente vazia e no processo 0 havia 42, mas depois do MPI_recv o processo 1 pode escrever o conteúdo 42 de msg. Logo, houve comunicação.

Dicas

Por um problema no empacotamento do mpich no Ubuntu toda vez que você executa o MPI você recebe umas mensagens horrorosas de erro, que na verdade são só um aviso que ele não encontrou uma placa de rede Infiniband.

Para você silenciar na unha essa chatice use o mpirun assim:

mpiexec –mca btl ^openib -np 1 executável

Onde -np 1 deve ser substituido pelo seu número de processos e executável pelo seu executável.

Outra dica é que você pode utilizar uma distribuição Linux que já venha com o MPI instalado. Por exemplo o Scientific Linux ou o Parallel Knoppix.

Qual o Tamanho da Wikipédia?

Se a Wikipédia em inglês fosse impressa ela teria esse tamanho:

Tamanho estimado da Wikipédia em inglês impressa
Ilustração original de Nikola Smolenski.

Usando livros de 25 centímetros por 5 centímetros, com mais ou menos 400 páginas, cada página contendo duas colunas, cada coluna contendo 80 linhas e cada linha contendo 50 caracteres, dá aproximadamente 6MB por volume. Como a versão da Wikipédia em inglês tem 4.4 GB de texto (isso em Outubro de 2006) temos então 750 volumes. E isso é uma medida conservadora visto que não estamos incluindo imagens e tabelas, que consomem muito mais espaço nas folhas.

E pelo crescimento da Wikipédia desde esse período, o tamanho da prateleira deve ter dobrado! E olhe que isso é só a Wikipédia em inglês.

Olá Mundo em Java

Esse post faz parte de uma série de posts com pequenos códigos que mostram certas funcionalidades de linguagens como Java. A vantagem do Java é ser uma linguagem bem estabelecida, madura e portável. Há diversos excelentes ambiente para desenvolvimento e um imenso framework padrão da linguagem, além de vários outros frameworks e bibliotecas adicionais.

Para fazer seu primeiro programa em Java, crie um arquivo chamado OlaMundo.java com esse conteúdo:

public class OlaMundo {
    public static void main(String[] args) {
        System.out.println("Olá Mundo!");
    }
}

O que você está fazendo é criar uma classe chamada OlaMundo. A linguagem exige que o nome do arquivo e o nome da classe sejam o mesmo, sendo o arquivo acrescido da extensão java, no nosso caso fica OlaMundo.java. Depois nós declaramos um método padrão chamado main, o que significa public, static, void, String args não é muito importante para quem está começando. A chamada System.out.println com um parâmetro “Olá Mundo” vai escrever na tela a mensagem “Olá Mundo”.

É necessário compilar esse código para a máquina virtual Java. Isso pode ser feito através do comando no prompt:

javac OlaMundo.java

Se tudo deu certo, nenhuma mensagem deverá aparecer. Se algo der errado, dê uma olhada se o seu código está mesmo igual ao que eu mostrei, e verifique se você realmente salvou o arquivo como OlaMundo.java (esse é um erro muito comum).

Para ver o programa funcionando, você vai invocar a máquina virtual Java e passar como parâmetro a classe OlaMundo. Isso é feito em linha de comando assim:

javac OlaMundo

Que vai retornar:

Olá Mundo!

Cuido para não colocar a extensão .java depois do parâmetro do comando java, esse é um erro muito comum.

Um bom exercício para esse programa é variar a mensagem exibida na tela. Experimente colocar outra mensagem, como o seu nome. :D

SHA1 em Python

Calcular o SHA1 de uma string em Python é feito quase da mesma forma do calculo do md5 que eu já mostrei aqui antes.

A partir da versão do Python 2.5 a maneira correta de se fazer é assim:

import hashlib
print hashlib.sha1('Silveira').hexdigest()

Isso vai imprimir isto:

25e58a0147633e39c2a2fcceff9a13a181e672b9

Para fazer o calculo do MD5, também se usa a hashlib:

import hashlib
print hashlib.md5('Silveira').hexdigest()

Que vai nos dar o md5 da string ‘Silveira’:

43eb9e4547c642db6cd19713b5c1dff2

O módulo hashlib possui os contrutores md5(), sha1(), sha224(), sha256(), sha384(), e sha512(). Dependendo da implementação da OpenSSL que você tem instalada você poderá ganhar alguns outros construtores.

Maiores informações: documentação da hashlib

Usando o Google como Proxy

Eu estava procurando por “Ubuntu nis” no Google e recebo esse resultado:

Busca no Google por Ubuntu e NIS
Busca no Google por Ubuntu e NIS

Essa primeira ocorrência que apareceu no Google é um exemplo bem interessante. É um site que se propõe a tirar dúvidas de usuários através de consultores especializados. Porém se você visitar o site você não conseguira visualizar conteúdo algum, é necessário um registro para poder acessar o site por um período de teste, depois é necessário pagar. O que houve aí foi uma técnica de SEO, na verdade uma fraude. Esse site identifica quando o visitante é um robô do Google indexando páginas, nesse caso ele abre suas portas e mostra que belo site ele é, cheio de informações. Porém quando um usuário comum como eu e você, que foi levado ao site através do Google, vai fazer uma visita, ele mostra suas reais intenções.

Como utilizar o cache do google

Para esse site em específico a uma maneira fácil e prática de engana-lo. Basta usar o botão “Em Cache” que está logo abaixo do link no Google.

Página de cache do google

Isso vai te mostrar uma página exibida ao robô do Google, ou seja, a página boa. De uma certa forma, você está navegando usando um proxy já que você está recebendo uma página que foi requisitada por uma terceira pessoa. Mas isso é tudo muito estático. As coisas ficam realmente interessantes quando você pode fazer isso com um site qualquer, por exemplo um que está bloqueado para você na sua instituição ou no seu pais.

Você pode utilizar o serviço de tradução Google para repetir o processo (certamente qualquer outro tradutor funcionará). Quando eu peço para o Google traduzir de português para inglês site silveiraneto.net ele vai criar esta url:

http://www.google.com/translate?u=http%3A%2F%2Fwww.silveiraneto.net&langpair=pt%7Cen&hl=en&ie=UTF8

Quando você acessa essa url você já está utilizando um proxy, mas o conteúdo está sendo alterado. Para acessar o site e deixa-lo em português basta mexer um pouco na url, pedindo para ele traduzir de português para português:

http://www.google.com/translate?u=http%3A%2F%2Fwww.silveiraneto.net&langpair=pt%7Cen&hl=pt&ie=UTF8

Pronto, você está acessando o Blog do Silveira através de um proxy. :)

Generalizando, para acessar um site coloque assim:

http://www.google.com/translate?u=SITE&langpair=pt%7Cen&hl=pt&ie=UTF8

Função MD5 em Python

MD5, Message-Digest algorithm 5, é um algoritmo de hash de 128 bits, unidirecional. Ele é muito útil como método de verificação de integridade de arquivos, armazenamento de senhas, criptografia ou para hashing em geral. Eu utilizo ele com muita frequência.

Em Python você calcula o hash de uma string em duas linhas:

import md5
print md5.new('silveira').hexdigest()

O md5.new() retorna um objeto que cria md5. Ele é bem flexível e pode ser utilizado várias vezes. O hexdigest é a representação hexadecimal do md5, aquela que pode ser impressa.

Esse código imprime isso:

3df2175295d900d6f0c2f3a521d957cd

Que é o md5 de silveira.

carbon-based lifeform. virgo supercluster