Aula 3
Computação Distribuída de Alto Desempenho
Marcelo Giovanni Marcelo Giovanni Nilton Alves
Nilton Alves
Marcelo Portes de Albuquerque Marcelo Portes de Albuquerque
Centro Brasileiro de Pesquisas Físicas
VI Escola do CBPF – Rio de Janeiro
17 a 28 de julho de 2006
Sumário Aula 03
1. 1. Programa Programa ç ç ão Estruturada x OO ão Estruturada x OO 2. 2. Programa Programa ç ç ão em MPI ão em MPI
3. 3. Charm Charm ++ ++
4. 4. Laborat Laborat ó ó rios rios
3
Programação Estruturada
Divisão das tarefas a serem realizadas em etapas e execução de uma por vez, até que todo o trabalho tenha sido realizado
Programação Estruturada → Modularização
Procedimentos ou Funções → blocos de programa que executam determinada tarefa:
– Procedimentos: podem receber valores, mas não retornam outros valores como resultado
– Função: retorna os valores resultantes das operações que realizou
/* funcao.c */
void main(){
int x, y, r;
printf("Digite dois numeros: ");
scanf("%d %d",&x, &y);
r = soma(x, y);
printf("A soma dos numeros e”: %d", r);
}
/* soma() */
/* retorna a soma de dois numeros */
int soma(int j, int k){
return(j+k);
}
...
...
Programação Estruturada – Exemplo em C
VI Escola do CBPF – Rio de Janeiro
Programação Orientada a Objetos
Enfoque tradicional:
– Conjunto de programas que atuam sobre um determinado conjunto de dados que se deseja manipular de alguma forma para obter os resultados desejados
Enfoque da modelagem de sistemas por objetos:
– Vê o mundo como um conjunto de objetos que interagem entre si – Apresentam características e comportamento próprios
– São representados pelos seus atributos e suas operações
– Os atributos estão relacionados aos dados, e as operações, aos processos que o objeto executa
Classe Objeto
Cão Criar Rex
Instância
Propriedades
Cor:
Cor do Olho:
Peso:
Altura:
Comprimento:
Métodos
Sentar Deitar Latir Comer
Propriedades
Cor: Branco e Preto Cor do Olho: Marrom Peso: 14Kg
Altura: 47 cm
Comprimento: 110cm
Métodos
Sentar Deitar Latir Comer
– Classe é um “molde” que define as propriedades e métodos comuns para todos os objetos
– Objeto é a concretização de uma classe
5
Classe - mãe (animais)
Subclasses
c1
c2
c3 Subclasses herdam
propriedades e métodos da classe-mãe
Herança
Polimorfismo
Uma classe pode ter métodos com o mesmo nome, mas que
executam ações diferentes
c1 - cão c2 - gato c3 - rato
Para descrever uma classe não é
preciso citar as propriedades herdadas
Mecanismos de Orientação a Objetos
Encapsulamento
Separação dos aspectos que são acessíveis por outros objetos,
daqueles que são da implementação do objeto
Cada subclasse pode ter ou não novas propriedades e/ou métodos
Método Público - Pode ser acessado por outro método externo à classe Método Privado - Não é acessado por nenhum método externo à classe
Variável Pública- Pode ser acessada por qualquer método
Variável Privada- Só pode ser modificada pelo método da classe que ela pertence
VI Escola do CBPF – Rio de Janeiro
Exemplo em C++
// classe contador class contador{
private:
unsigned int valor;
public:
contador( ){ // ---Æ Construtor valor = 0;
}
void inicializa(unsigned int val){
valor = val;
}
void incremento( ){
if (valor < 65535) valor++;
}
void decremento( ) {
if (valor > 0) valor--;
}
unsigned int get_valor( ){
return valor;
} };
// classe contador class contador{
private:
unsigned int valor;
public:
contador( ){ // ---Æ Construtor valor = 0;
}
void inicializa(unsigned int val){
valor = val;
}
void incremento( ){
if (valor < 65535) valor++;
}
void decremento( ) {
if (valor > 0) valor--;
}
unsigned int get_valor( ){
return valor;
} };
// Programa principal void main(){
contador cont;
unsigned int x;
cout << “\nValor inicial:"; cin >> x;
cont.inicializa(x);
cont.incremento();
cont.incremento();
cout << “\nValor final:“ <<
cont.get_valor();
}
// Programa principal void main(){
contador cont;
unsigned int x;
cout << “\nValor inicial:"; cin >> x;
cont.inicializa(x);
cont.incremento();
cont.incremento();
cout << “\nValor final:“ <<
cont.get_valor();
}
OK!
7
Sumário Aula 03
1. 1. Programa Programa ç ç ão Estruturada x OO ão Estruturada x OO 2. 2. Programa Programa ç ç ão em MPI ão em MPI
3. 3. Charm Charm ++ ++
4. 4. Laborat Laborat ó ó rios rios
VI Escola do CBPF – Rio de Janeiro
Message Passing Interface- MPI
Biblioteca desenvolvida para ambientes de memória distribuída, máquinas paralelas massivas, Clusters e redes heterogêneas
Define um conjunto de rotinas para facilitar a comunicação entre processos paralelos
Portável para qualquer arquitetura
Aproximadamente 125 funções para programação Ferramentas de análise de performance
A biblioteca MPI possui rotinas para programas em Fortran 77 e ANSI C, portanto pode ser usada também para Fortran 90 e C++
Os programas são compilados e “linkados” à biblioteca MPI Paralelismo é explícito
– Programador é responsável por identificar o paralelismo e implementar o algoritmo utilizando chamadas aos comandos da biblioteca MPI
MPI é dividido em: Básico e Avançado
9
Conceitos Básicos MPI
Processos
– Cada programa em execução constitui um processo
– Em um ambiente multiprocessado, podemos ter processos em inúmeros processadores
– Normalmente a quantidade de processos deve corresponder à quantidade de processadores disponíveis
Rank
– Rank é o identificador único do processo, utilizado para identificar o processo no envio (send) ou recebimento (receive) de uma mensagem
– Essa identificação é contínua representada por um número inteiro, começando de zero até N-1, onde N é o número de processos
Group
– Conjunto ordenado de N processos
– Está associado ao comunicador "MPI_COMM_WORLD"
– Todos os processos são membros de um group com um communicator
VI Escola do CBPF – Rio de Janeiro
Tipos de Dados nas Mensagens
Informação que se deseja enviar ou receber é representado por três argumentos:
– Endereço onde se localiza o dado – Número de elementos do dado na
mensagem – Tipo do dado
- MPI_PACKED
- MPI_BYTE
- -
signed short int MPI_SHORT
unsigned char MPI_UNSIGNED_CHAR
signed long int MPI_LONG
long double MPI_LONG_DOUBLE
unsigned long int MPI_UNSIGNED_LONG
unsigned int MPI_UNSIGNED
unsigned short int MPI_UNSIGNED_SHORT
double MPI_DOUBLE
float MPI_FLOAT
signed int MPI_INT
signed char MPI_CHAR
Definição no C Definição no MPI
Tipos de Dados Básicos no C
11
Tipos de Comunicação MPI
Ponto a Ponto:
– Executam a transferência de dados entre dois processos
• Síncrono: espera sinal de recebimento
• Imediato: sem necessidade de confirmação
• Buferizada
• Padrão
• Existem duas formas de chamadas: blocking e non-blocking
Coletiva:
– Invoca todos os processos em um grupo (group)
– Normalmente a comunicação coletiva envolve mais de dois processos – Existem os seguintes tipos de comunicação coletiva:
• Broadcast (Difusão): um único processo envia os mesmos dados para todos os processos do grupo
• Reduction (Redução): cada processo do grupo tem um operador, e todos eles são combinados usando um operador binário que será aplicado sucessivamente
• Gather (Coleta): dados enviados são coletados por um único processo
• Scatter (Espalhamento): dados de um único processo é distribuído a todos os outros VI Escola do CBPF – Rio de Janeiro
Programando em Paralelo Utilizando MPI
Analise o programa
– Existem procedimentos independentes que podem ser executados concorrentemente?
– Determine os tipos de dependências existentes entre os processos para só então definir como realizar a sincronização entre os procedimentos
Dependência
– Dados: uma mesma variável é modificada dentro do escopo de processos distintos, podendo gerar resultados inconsistentes ao final
– Loops : Iterações dependem de iterações anteriores – y(i) = y(i-1) * 2;
– Recursos: mesmo recurso é acessado simultaneamente por processos distintos – e.g. arquivos
X=Y*4;
X=3;
Y=X*4;
X=3;
Y=X*4;
X=3;
Dependência de saída Anti-dependência
Dependência real
(dependência de fluxo)
Tipos de dependências de dados
13
Como Utilizar o MPI?
Bibliotecas
Adicione em seus programas o seguintes include para programas em C – #include “mpi.h”
Rotinas MPI
Escreva seu programa fazendo chamadas às rotinas MPI
Para a compilação e linkedição utilizar os comandos:
– mpicc [fonte.c] -o [executável] [parâmetros]
– Obs: O comando mpicc aceita os argumentos de compilação do compilador C
Execução
– mpirun -[argumentos] [executável]
– Exemplo: mpirun -np 5 a.out (executa a.out em 5 processadores)
Nomenclatura das Rotinas MPI
– rc = MPI_Xxxxx(parâmetros, ...);
– rc é uma variável inteira que recebe um código de erro – Exemplo: MPI_Init (int *argc, char *argv[ ])
Exemplos usando o MPICH, desenvolvido pelo ARNL - Argonne National Laboratory e pelo MSU – Mississipi State University.
MPI_Init
#include <stdio.h>
#include "mpi.h“
main(int argc, char *argv[]){
int ret;
ret = MPI_Init(&argc, &argv);
if (ret < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else ...
#include <stdio.h>
#include "mpi.h“
main(int argc, char *argv[]){
int ret;
ret = MPI_Init(&argc, &argv);
if (ret < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else ...
Inicia um processo MPI
Inicializa o processo MPI no processador
Deve ser a primeira rotina a ser chamada por cada processo Estabelece o ambiente necessário para executar o MPI Sincroniza a inicialização de todos os processos
Sintaxe (em C):
int MPI_Init (int *argc, char *argv[]) Parâmetros:
– argc – Quantidade de parâmetros da linha de comando – argv – ponteiro para os parâmetros da linha de comando
15
MPI_Comm_rank
Identifica um processo MPI dentro de um determinado grupo
Retorna sempre um valor inteiro entre 0 e n-1, onde n é o número de processos
Sintaxe (em C) : int MPI_Comm_rank (MPI_Comm comm, int *rank) Parâmetros:
– comm - Comunicador do MPI
– rank - Variável inteira com o número de identificação do processo
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[]){
int mpierr, rank;
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
...
} }
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[]){
int mpierr, rank;
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
...
} }
MPI_Comm_Size
Retorna o número de processos dentro de um grupo
Sintaxe (em C):
int MPI_Comm_size (MPI_Comm comm, int *size)Parâmetros:
– comm: Comunicador do MPI
– size: Variável interna que retorna o número de processos iniciados pelo MPI
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[]){
int mpierr, rank, size;
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("O numero de processos e': %d\n",size);
...
} }
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[]){
int mpierr, rank, size;
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("O numero de processos e': %d\n",size);
...
} }
17
MPI_Send
Rotina para envio de mensagens no MPI
– utiliza o modo de comunicação "blocking send" (envio bloqueante), o que traz maior segurança na transmissão da mensagem
Sintaxe (em C):
int MPI_Send (void *sndbuf, int count, MPI_Datatype dtype, int dest, int tag, MPI_Comm comm)Parâmetros:
Sndbuf - Identificação do buffer (endereço de onde os dados serão enviados) count - Número de elementos a serem enviados
dtype - Tipo de dado
dest - Identificação do processo destino tag - Rótulo (label) da mensagem comm - MPI Communicator
MPI_Send
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[]){
int mpierr, rank, size, tag=100, i;
char message[20];
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
}
else {
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("O numero de processos e': %d\n",size);
if (rank == 0){
strcpy(message,"Ola', Mundo!\n");
for (i=1; i<size; i++)
MPI_Send(message, 13, MPI_CHAR, i, tag, MPI_COMM_WORLD);
} else
...
} }
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[]){
int mpierr, rank, size, tag=100, i;
char message[20];
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
}
else {
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("O numero de processos e': %d\n",size);
if (rank == 0){
strcpy(message,"Ola', Mundo!\n");
for (i=1; i<size; i++)
MPI_Send(message, 13, MPI_CHAR, i, tag, MPI_COMM_WORLD);
} else
...
} }
int MPI_Send (void *sndbuf, int count, MPI_Datatype dtype, // 1234567890123
19
MPI_Recv
Rotina para recepção de mensagens no MPI
– utiliza o modo de comunicação "blocking receive" (recepção bloqueante), de forma que o processo espera até que a mensagem tenha sido recebida
Sintaxe (em C):
int MPI_Recv (void *sendbuf, int count, MPI_Datatype dtype,
int source, int tag, MPI_Comm comm, MPI_Status status)
Parâmetros:
– sndbuf - Identificação do buffer de onde os dados serão armazenados – count - Número de elementos a serem recebidos
– dtype - Tipo de dado
– source - Identificação do processo emissor – tag - Rótulo (label) da mensagem
– comm - MPI Communicator
– status - Vetor de informações envolvendo os parâmetros source e tag
MPI_Recv
#include <stdio.h>
#include "mpi.h“
main(int argc, char *argv[]){
int mpierr, rank, size, tag=100, i;
MPI_Status status;
char message[20];
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("O numero de processos e': %d\n",size);
if (rank == 0){
strcpy(message,"Ola', Mundo!\n");
for (i=1; i < size; i++)
MPI_Send(message, 13, MPI_CHAR, i, tag, MPI_COMM_WORLD);
} else{
MPI_Recv(message, 20, MPI_CHAR, 0, tag, MPI_COMM_WORLD, &status);
printf("Mensagem do no' %d : %s\n", rank, message);
...
}
#include <stdio.h>
#include "mpi.h“
main(int argc, char *argv[]){
int mpierr, rank, size, tag=100, i;
MPI_Status status;
char message[20];
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("O numero de processos e': %d\n",size);
if (rank == 0){
strcpy(message,"Ola', Mundo!\n");
for (i=1; i < size; i++)
MPI_Send(message, 13, MPI_CHAR, i, tag, MPI_COMM_WORLD);
} else{
MPI_Recv(message, 20, MPI_CHAR, 0, tag, MPI_COMM_WORLD, &status);
printf("Mensagem do no' %d : %s\n", rank, message);
...
} int MPI_Recv (void *sendbuf, int count, MPI_Datatype dtype,
21
MPI_Finalize
Finaliza um processo MPI
– Última rotina a ser chamada por cada processo.
– Sincroniza todos os processos na finalização de uma aplicação MPI
Sintaxe (em C)
int MPI_Finalize (void)
Parâmetros:
(Nenhum)
MPI_Finalize
#include <stdio.h>
#include "mpi.h“
main(int argc, char *argv[]){
int mpierr, rank, size, tag=100, i;
MPI_Status status;
char message[20];
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("O numero de processos e': %d\n",size);
if (rank == 0){
strcpy(message,"Ola', Mundo!\n");
for (i=1; i < size; i++)
MPI_Send(message, 13, MPI_CHAR, i, tag, MPI_COMM_WORLD);
} else
MPI_Recv(message, 20, MPI_CHAR, 0, tag, MPI_COMM_WORLD, &status);
printf("Mensagem do no' %d : %s\n", rank, message);
MPI_Finalize();
}
#include <stdio.h>
#include "mpi.h“
main(int argc, char *argv[]){
int mpierr, rank, size, tag=100, i;
MPI_Status status;
char message[20];
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf ("Minha identificação no MPI e':%d\n",rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("O numero de processos e': %d\n",size);
if (rank == 0){
strcpy(message,"Ola', Mundo!\n");
for (i=1; i < size; i++)
MPI_Send(message, 13, MPI_CHAR, i, tag, MPI_COMM_WORLD);
} else
MPI_Recv(message, 20, MPI_CHAR, 0, tag, MPI_COMM_WORLD, &status);
printf("Mensagem do no' %d : %s\n", rank, message);
MPI_Finalize();
}
OK!
23
MPI_Finalize
Job started at Mon Jul 17 16:56:31 BRST 2006 Minha identificação no MPI e':1
O numero de processos e': 4
Mensagem do no' 1 : Ola', Mundo!
Minha identificação no MPI e':3 O numero de processos e': 4
Mensagem do no' 3 : Ola', Mundo!
Minha identificação no MPI e':2 O numero de processos e': 4
Mensagem do no' 2 : Ola', Mundo!
Minha identificação no MPI e':0 O numero de processos e': 4
Mensagem do no' 0 : Ola', Mundo!
Job ended at Mon Jul 17 16:56:31 BRST 2006 Job started at Mon Jul 17 16:56:31 BRST 2006 Minha identificação no MPI e':1
O numero de processos e': 4
Mensagem do no' 1 : Ola', Mundo!
Minha identificação no MPI e':3 O numero de processos e': 4
Mensagem do no' 3 : Ola', Mundo!
Minha identificação no MPI e':2 O numero de processos e': 4
Mensagem do no' 2 : Ola', Mundo!
Minha identificação no MPI e':0 O numero de processos e': 4
Mensagem do no' 0 : Ola', Mundo!
Job ended at Mon Jul 17 16:56:31 BRST 2006
Output
OK!MPI_Bcast
Enviando dados para todos os processos
– Diferentemente da comunicação ponto-a-ponto, na comunicação coletiva é possível enviar/receber dados simultaneamente de/para vários processos.
Sintaxe (em C)
int MPI_Bcast (void *buf, int count,MPI_Datatype datatype, int root, MPI_Comm comm)
Parâmetros:
– buffer - Endereço do dado a ser enviado
– count - Número de elementos a serem enviados – datatype - Tipo do dado
– root - Identifica o processo que irá efetuar o broadcast (origem) – tag - Variável inteira com o rótulo da mensagem
– comm - Identifica o Communicator
A0
processadores
Dados
Bcast
A0 A0
A0 A0
processadores
Dados
25
MPI_Bcast
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[]){
int mpierr, rank, size, *index;
char message[20];
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (rank==0)
strcpy(message,"Mensagem do no 0\n");
MPI_Bcast(message, 20, MPI_CHAR, 0, MPI_COMM_WORLD);
printf("A mensagem recebida pelo rank (%d) foi: %s", rank, message);
MPI_Finalize();
} }
#include <stdio.h>
#include "mpi.h"
main(int argc, char *argv[]){
int mpierr, rank, size, *index;
char message[20];
mpierr = MPI_Init(&argc, &argv);
if (mpierr < 0){
printf ("Nao foi possivel inicializar o processo MPI!\n");
return;
} else{
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (rank==0)
strcpy(message,"Mensagem do no 0\n");
MPI_Bcast(message, 20, MPI_CHAR, 0, MPI_COMM_WORLD);
printf("A mensagem recebida pelo rank (%d) foi: %s", rank, message);
MPI_Finalize();
} }
int MPI_Bcast (void *buf, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
OK!
Rank 0 define a mensagem a ser enviada Rank 0 define a mensagem a ser enviada
Rank 0 envia para os outros ranks Rank 0 envia para os outros ranks
MPI_Bcast
Job started at Mon Jul 17 18:29:40 BRST 2006
A mensagem recebida pelo rank (1) foi: Mensagem do no 0 A mensagem recebida pelo rank (3) foi: Mensagem do no 0 A mensagem recebida pelo rank (0) foi: Mensagem do no 0 A mensagem recebida pelo rank (2) foi: Mensagem do no 0 Job ended at Mon Jul 17 18:29:40 BRST 2006
Job started at Mon Jul 17 18:29:40 BRST 2006
A mensagem recebida pelo rank (1) foi: Mensagem do no 0 A mensagem recebida pelo rank (3) foi: Mensagem do no 0 A mensagem recebida pelo rank (0) foi: Mensagem do no 0 A mensagem recebida pelo rank (2) foi: Mensagem do no 0 Job ended at Mon Jul 17 18:29:40 BRST 2006
Output
OK!r3 r2 r1 r0
message []
message []
message []
message []
“”
“”
“”
“Mensagem do no 0\n”
processadores
Broadcast
Dados
r3 r2 r1 r0
message []
message []
message []
message []
“Mensagem do no 0\n”
“Mensagem do no 0\n”
“Mensagem do no 0\n”
“Mensagem do no 0\n”
processadores
Dados
27
MPI_Scatter
Envia mensagens coletivamente
– Envio de mensagens para um subgrupo de processos.
– A mensagem pode ser segmentada e enviada para processos diferentes
Sintaxe (em C):
int MPI_Scatter (void *sbuf,int scount,MPI_Datatype stype, void *rbuf, int rcount,MPI_Datatype rtype,int root, MPI_Comm comm)
Parâmetros:
sbuf - Endereço dos dados a serem distribuídos
scount - Número de elementos enviados para cada processo stype - Tipo do dado a ser enviado
rbuf - Endereço onde os dados serão armazenados rcount - Quantidade de dados recebidos
rtype - Tipo do dado recebido
root - Identifica o processo que irá distribuir os dados comm - Identifica o Communicator
A3 A1 A2
A0
processadores
Dados
Scatter
A2 A1
A3 A0
processadores
Dados
MPI_Scatter
#include "mpi.h"
#include <stdio.h>
#define SIZE 4
int main(int argc, char *argv[]) {
int numtasks, rank, sendcount, recvcount, source;
float sendbuf[SIZE][SIZE] = {
{1.0, 2.0, 3.0, 4.0}, {5.0, 6.0, 7.0, 8.0}, {9.0, 10.0, 11.0, 12.0}, {13.0, 14.0, 15.0, 16.0}};
float recvbuf[SIZE];
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
if (numtasks == SIZE){
source = 0;
sendcount = SIZE;
recvcount = SIZE;
MPI_Scatter(sendbuf, sendcount, MPI_FLOAT, recvbuf, recvcount, MPI_FLOAT, source, MPI_COMM_WORLD);
printf(" Results (%d): %3.0f %3.0f %3.0f %3.0f\n",rank, recvbuf[0], recvbuf[1], recvbuf[2], recvbuf[3]);
} else
printf("Must specify %d processors. Terminating.\n",SIZE);
MPI_Finalize();
}
#include "mpi.h"
#include <stdio.h>
#define SIZE 4
int main(int argc, char *argv[]) {
int numtasks, rank, sendcount, recvcount, source;
float sendbuf[SIZE][SIZE] = {
{1.0, 2.0, 3.0, 4.0}, {5.0, 6.0, 7.0, 8.0}, {9.0, 10.0, 11.0, 12.0}, {13.0, 14.0, 15.0, 16.0}};
float recvbuf[SIZE];
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
if (numtasks == SIZE){
source = 0;
sendcount = SIZE;
recvcount = SIZE;
MPI_Scatter(sendbuf, sendcount, MPI_FLOAT, recvbuf, recvcount, MPI_FLOAT, source, MPI_COMM_WORLD);
printf(" Results (%d): %3.0f %3.0f %3.0f %3.0f\n",rank, recvbuf[0], recvbuf[1], recvbuf[2], recvbuf[3]);
} else
printf("Must specify %d processors. Terminating.\n",SIZE);
MPI_Finalize();
} int MPI_Scatter (void *sbuf,int scount,MPI_Datatype stype, void *rbuf,
OK!
buffer de envio buffer de envio
Rank 0 distribui para todos Rank 0 distribui para todos
buffer de recepção
buffer de recepção
29
MPI_Scatter
Job started at Mon Jul 17 18:38:05 BRST 2006 Results (1): 5 6 7 8
Results (3): 13 14 15 16 Results (2): 9 10 11 12 Results (0): 1 2 3 4
Job ended at Mon Jul 17 18:38:05 BRST 2006 Job started at Mon Jul 17 18:38:05 BRST 2006 Results (1): 5 6 7 8
Results (3): 13 14 15 16 Results (2): 9 10 11 12 Results (0): 1 2 3 4
Job ended at Mon Jul 17 18:38:05 BRST 2006
Output
OK!sendbuf
r3 r2 r1
r0
9 10 11 1216 15
14 13
8 7
6 5
4
2 3
1
processadores
Dados
processadores
Dados
Scatter
recvbuf recvbuf recvbuf recvbuf
r3 r2 r1 r0
16 15
14 13
12 11
10 9
8 7
6 5
4 3
2 1
MPI_Gather
Coleta mensagens dos processos Sintaxe:
int MPI_Gather (void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, int root, MPI_Comm comm)
Parâmetros:
sbuf - Endereço inicial do dado a ser coletado scount - Número de dados a serem coletados stype - Tipo do dado a ser coletado
rbuf - Endereço onde os dados serão armazenados rcount - Número de elementos recebidos por processo rtype - Tipo do dado recebido
root - Identifica o processo que irá efetuar a coleta comm - Identifica o Communicator
A2 A1
A3 A0
processadores
Dados
Gather
A3 A1 A2
A0
processadores
Dados
31
MPI_Gather
#include <stdio.h>
#include "mpi.h"
#define SIZE 4
int main(int argc, char *argv[]) {
int numtasks, rank, sendcount, recvcount, source, i;
float recvbuf[SIZE][SIZE];
float sendbuf[SIZE];
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
if (numtasks == SIZE){
for (i=0;i<SIZE;i++)
sendbuf[i]=(float) rank * SIZE + i;
source = 0;
sendcount = SIZE; recvcount = SIZE;
MPI_Gather(sendbuf, sendcount, MPI_FLOAT, recvbuf, recvcount, MPI_FLOAT, source, MPI_COMM_WORLD);
if (rank==source)
for (i=0;i<SIZE;i++)
printf("Results (%d): %3.0f %3.0f %3.0f %3.0f\n", rank,
recvbuf[i][0], recvbuf[i][1], recvbuf[i][2], recvbuf[i][3]);
} else
printf("Especifique %d processadores. Abortado.\n",SIZE);
MPI_Finalize();
}
#include <stdio.h>
#include "mpi.h"
#define SIZE 4
int main(int argc, char *argv[]) {
int numtasks, rank, sendcount, recvcount, source, i;
float recvbuf[SIZE][SIZE];
float sendbuf[SIZE];
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
if (numtasks == SIZE){
for (i=0;i<SIZE;i++)
sendbuf[i]=(float) rank * SIZE + i;
source = 0;
sendcount = SIZE; recvcount = SIZE;
MPI_Gather(sendbuf, sendcount, MPI_FLOAT, recvbuf, recvcount, MPI_FLOAT, source, MPI_COMM_WORLD);
if (rank==source)
for (i=0;i<SIZE;i++)
printf("Results (%d): %3.0f %3.0f %3.0f %3.0f\n", rank,
recvbuf[i][0], recvbuf[i][1], recvbuf[i][2], recvbuf[i][3]);
} else
printf("Especifique %d processadores. Abortado.\n",SIZE);
MPI_Finalize();
} int MPI_Gather (void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, int root, MPI_Comm comm)
OK!
Rank 0 recebe de todos Rank 0 recebe de todos
Prepara buffer de envio
Prepara buffer de envio
MPI_Gather
Job started at Mon Jul 17 19:24:01 BRST 2006 Results (0): 0 1 2 3
Results (0): 4 5 6 7 Results (0): 8 9 10 11 Results (0): 12 13 14 15
Job ended at Mon Jul 17 19:24:02 BRST 2006 Job started at Mon Jul 17 19:24:01 BRST 2006 Results (0): 0 1 2 3
Results (0): 4 5 6 7 Results (0): 8 9 10 11 Results (0): 12 13 14 15
Job ended at Mon Jul 17 19:24:02 BRST 2006
Output
OK!recvbuf
r3 r2 r1 r0
11 10
9 8
15 14
13 12
7 6
5 4
3
1 2
0
processadores
Dados
processadores
Dados
Gather
sendbuf Sendbuf Sendbuf Sendbuf
15 14
13
r3
1211 10
9
r2
87 6
5
r1
43 2
1
r0
033
MPI_Reduce
Realiza uma computação de todos os processos
– Todos os processos executam uma operação, onde o resultado parcial de cada processo é combinado e retornado para um processo específico
Sintaxe (em C)
int MPI_Reduce (void *sbuf, void *rbuf, int count,
MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)
Parâmetros
sbuf - Endereço do dado a ser enviado;
rbuf - Endereço do dado a ser recebido;
count - Número de elementos a serem distribuídos;
datatype - Tipo do dado a ser computado;
op - Operaçâo a ser executada;
root - Processo que irá receber o resultado da operação;
comm - Identifica o Communicator;
MPI_Reduce
int, float Valor mínimo de menor índice
MPI_MINLOC
int, float Valor máximo de maior índice
MPI_MAXLOC
OU-EXCLUSIVO (XOR) lógico a nível de BIT int MPI_BXOR
OU-EXCLUSIVO (XOR) lógico int MPI_LXOR
OU (OR) lógico a nível de BIT int MPI_BOR
OU (OR) lógico int MPI_LOR
E (AND) lógico a nível de BIT int MPI_BAND
E (AND) lógico int MPI_LAND
int, float Produtório dos valores
MPI_PROD
int, float Somatório dos valores
MPI_SUM
int, float Valor mínimo
MPI_MIN
int, float Valor máximo
MPI_MAX
C Significado
Função
Operações Possíveis
35
MPI_Reduce
#include "mpi.h"
#include <stdio.h>
#define SIZE 4
int main(int argc, char *argv[]){
int numtasks, rank;
int recvbuf, sendbuf;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
sendbuf=rank;
if (numtasks == SIZE){
MPI_Reduce(&sendbuf, &recvbuf, 1, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD);
if (rank==0)
printf("Maior = %d\n", recvbuf);
} else
printf("Especifique %d processadores. Abortado.\n",SIZE);
MPI_Finalize();
}
#include "mpi.h"
#include <stdio.h>
#define SIZE 4
int main(int argc, char *argv[]){
int numtasks, rank;
int recvbuf, sendbuf;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
sendbuf=rank;
if (numtasks == SIZE){
MPI_Reduce(&sendbuf, &recvbuf, 1, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD);
if (rank==0)
printf("Maior = %d\n", recvbuf);
} else
printf("Especifique %d processadores. Abortado.\n",SIZE);
MPI_Finalize();
}
int MPI_Reduce (void *sbuf, void *rbuf, int count,
MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm) OK!
Rank 0 recebe o valor máximo Rank 0 recebe o valor máximo Em cada processo sendbuf
recebe o valor de rank Em cada processo sendbuf
recebe o valor de rank
MPI_Reduce
Job started at Mon Jul 17 19:21:59 BRST 2006 Maior = 3
Job ended at Mon Jul 17 19:21:59 BRST 2006 Job started at Mon Jul 17 19:21:59 BRST 2006 Maior = 3
Job ended at Mon Jul 17 19:21:59 BRST 2006
Output
OK!recvbuf recvbuf recvbuf recvbuf
sendbuf sendbuf sendbuf sendbuf
r3 r2 r1 r0
?
?
?
?
2 1
3 0
processadores
Reduce
Dados
3 2 1 0 MAX
recvbuf recvbuf recvbuf recvbuf
sendbuf sendbuf sendbuf sendbuf
r3 r2 r1 r0
?
?
? 3
2 1
3 0
processadores
Dados
37
Outras Rotinas Avançadas
MPI_Wtime
– Retorna (em precisão numérica dupla) o número de segundos decorridos desde algum tempo no passado
MPI_Wtick
– Retorna (em valor real) a precisão de tempo computada pelo comando MPI_Wtime
MPI_Get_processor_name
– Retorna o nome da máquina onde um dado processo está executando
...
Sumário Aula 03
1. 1. Programa Programa ç ç ão Estruturada x OO ão Estruturada x OO 2. 2. Programa Programa ç ç ão em MPI ão em MPI
3. 3. Charm Charm ++ ++
4. 4. Laborat Laborat ó ó rios rios
39
Charm++
Linguagem de programação Paralela baseado no C++
Objetos se comunicam por mensagens
Portável para uma grande variedade de máquinas paralelas
Destinado à máquinas paralelas de alta performance fortemente acopladas Permite a escalablidade em milhares de processadores
NAMD project for Biomolecular Simulations
§1Usa array de objetos distribuídos em todos os processadores
Otimizado para operações com comunicações coletivas → e.g. reduções
Técnicas avançadas de balanceamento de carga
Quando um objeto esta esperando por algum dado, outros objetos que estão prontos podem ser executados
charm.cs.uiuc.edu
[1] Kale L. V. et al. , “NAMD: Biomolecular Simulation on Thousands of Processors”, Proceedings of Supercomputing (2002).
Charm++ ...
charm.cs.uiuc.eduNa programação com MPI usa-se no mínimo 6 rotinas básicas
• MPI_Init, MPI_Finalize, MPI_Comm_size, MPI_Comm_rank, MPI_Send, MPI_Recv
Charm++ é baseado no MPI porém é transparente ao programador Predição de performance em máquinas grandes
– Ajuste de performance sem acesso contínuo a máquinas grandes
– Desenvolvimento de aplicação paralela para uma máquina ainda inexistente
Ferramenta para Análise de Desempenho - Projections
41
Virtualização em Charm++
Visão do Programador
Programador especifica a interação entre objetos Sistema mapeia os objetos em processadores reais
Objetos só tem conhecimento de outros objetos (não dos processadores)
Mensagens
Implementação do Sistema
Ambiente de execução de Charm++ automatiza o mapeamento e agendamento de objetos em paralelos nos processadores
– Mapeamento: qual processador faz cada tarefa
pode re-mapear os objetos para balanceamento de carga dinamicamente
– Agendamento: seqüênciamento das tarefas em cada processador
Necessidade de Proxies
Considere:
– Obj “X” da classe “A” quer invocar o método m do Obj “Y” da classe “B”.
– “X” e “Y” estão em diferentes processadores – Como deve ser a sintaxe?
• Y->m(…) não funciona pois “Y” não é um ponteiro na máquina local
Necessita:
– Ao invés de “Y” deve ser usado um ID válido para todos processadores → Proxy – O sistema deve guardar estes parâmetros e enviá-los a todos os processadores – O processador deve chamar os métodos em outros processadores usando → Proxy
Proxy Classes:
– Para cada classe C, o sistema gera uma classe proxy
– (C : CProxy_C)
– Sintaxe: CProxy_C MyC = CProxy_C::ckNew(args);
– Global: válida para todos os processadores
– thisProxy informa qual o processador você está – Envio de mensagens
– Para o proxy MyC, pode se invocar os métodos por → MyC.method(msg);
43
Charm++ Hello with Array Obj
#include <stdio.h>
#include "hello.decl.h"
CProxy_Main mainProxy; /*readonly*/
int nElements; /*readonly*/
class Main : public Chare{ /*mainchare*/
public:
--- Main(CkArgMsg* m) {
nElements=5; //Process command-line arguments if(m->argc >1 ) nElements=atoi(m->argv[1]);
delete m;
//Start the computation
CkPrintf("Running Hello on %d processors
for %d elements\n", CkNumPes(),nElements);
mainProxy = thishandle;
CProxy_Hello arr = CProxy_Hello::ckNew(nElements);
arr[0].SayHi(17);
};
--- void done(void){
CkPrintf("All done\n");
CkExit();
};
};
#include <stdio.h>
#include "hello.decl.h"
CProxy_Main mainProxy; /*readonly*/
int nElements; /*readonly*/
class Main : public Chare{ /*mainchare*/
public:
--- Main(CkArgMsg* m) {
nElements=5; //Process command-line arguments if(m->argc >1 ) nElements=atoi(m->argv[1]);
delete m;
//Start the computation
CkPrintf("Running Hello on %d processors
for %d elements\n", CkNumPes(),nElements);
mainProxy = thishandle;
CProxy_Hello arr = CProxy_Hello::ckNew(nElements);
arr[0].SayHi(17);
};
--- void done(void){
CkPrintf("All done\n");
CkExit();
};
};
/*array [1D]*/
class Hello : public CBase_Hello{
public:
--- Hello(){
CkPrintf("Hello %d created\n",thisIndex);
}
--- Hello(CkMigrateMessage *m) {}
--- void SayHi(int hiNo){
CkPrintf("Hi[%d] from element (%d)\n",hiNo,thisIndex);
if (thisIndex < nElements-1) //Pass the hello on:
thisProxy[thisIndex+1].SayHi(hiNo+1);
else
//We've been around once-- we're done.
mainProxy.done();
} };
#include "hello.def.h"
/*array [1D]*/
class Hello : public CBase_Hello{
public:
--- Hello(){
CkPrintf("Hello %d created\n",thisIndex);
}
--- Hello(CkMigrateMessage *m) {}
--- void SayHi(int hiNo){
CkPrintf("Hi[%d] from element (%d)\n",hiNo,thisIndex);
if (thisIndex < nElements-1) //Pass the hello on:
thisProxy[thisIndex+1].SayHi(hiNo+1);
else
//We've been around once-- we're done.
mainProxy.done();
} };
#include "hello.def.h"
Charm++ Hello with Array Obj
Compilando: > charmc -c hello.ci hello.C -o a.out -language charm++
Executando: > charmrun ++p3 a.out 10 Job started at Mon Aug 15 11:27:23 BRST 2005 Running Hello on 3 processors for 10 elements Hello 0 created
Hello 3 created Hello 6 created Hello 9 created
Hi[17] from element 0 Hello 2 created
Hello 5 created Hello 8 created Hello 1 created Hello 4 created Hello 7 created
Hi[18] from element 1 Hi[19] from element 2 Hi[20] from element 3 Hi[21] from element 4 Hi[22] from element 5 Hi[23] from element 6 Hi[24] from element 7 Hi[25] from element 8 Hi[26] from element 9 All done
Job ended at Mon Aug 15 11:27:25 BRST 2005
>
45
Sumário Aula 03
1. 1. Programa Programa ç ç ão Estruturada x OO ão Estruturada x OO 2. 2. Programa Programa ç ç ão em MPI ão em MPI
3. 3. Charm Charm ++ ++
4. 4. Laborat Laborat ó ó rios rios
Organização dos Labs
Local: Prédio Min. João Alberto, 4º Andar, Laboratórios 1 e 3 Divisão da Turma em 2 Grupos
– Grupo 1: 14h – 15h – “A até J”
– Grupo 2: 15h – 16h – “L até Z”
Cluster dedicado à VI Escola: 6 CPUs
– Login: esc1 – Password: cbpf
Quinta (19/07)
– Equação do segundo grau – Compilação
– Script pbs
Sexta (19/07)
– MPI básico – MPI avançado – Integral em mpi – charm++
47
Integração Numérica
O conceito de integral está ligado ao problema da determinação da área de uma figura plana qualquer
Integral de uma função f(x) no intervalo [a,b]
a b
f(x)
x
∫
=
ba