Aula 5
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 05
1. 1. Programa C++ (Orienta Programa C++ (Orienta ç ç ão Objeto) ão Objeto)
2. 2. Laborat Laborat ó ó rios MPI rios MPI
BBáásico e Avansico e Avanççadoado
3. 3. Charm Charm ++ ++
3
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();
}
VI Escola do CBPF – Rio de Janeiro
OK!
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
5
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
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{
#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{
7
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);
...
} }
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
9
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, int dest, int tag, MPI_Comm comm) // 1234567890123
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
11
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,
int source, int tag, MPI_Comm comm, MPI_Status status)
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)
13
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!
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!
15
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
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();
OK!
Rank 0 define a mensagem a ser enviada Rank 0 define a mensagem a ser enviada
17
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
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
Dados
A0
Dados
19
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, int rcount,MPI_Datatype rtype,int root, MPI_Comm comm)
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
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
r1
r0 9 10 11 12
16 15
14 13
8 7
6 5
4
2 3
1
Dados
adores
Dados
Scatter
recvbuf recvbuf
r1 r0
12 11
10 9
8 7
6 5
4 3
2 1
21
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
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,
#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]);
OK!
Rank 0 recebe de todos Rank 0 recebe de todos
Prepara buffer de envio Prepara buffer de envio
23
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 12
r3
11 10
9 8
r2
7 6
5 4
r1
3 2
1 0
r0
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;
25
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
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);
#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);
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
27
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
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
...
29
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++
Cluster do CBPF – andar térreo do prédio anexo
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]
f(x) F(x) =
∫
ab f (x) dx Em determinados casos não podemos calcular F(x) ou a suaobtenção não é simples
Em situações práticas nem sempre se tem a forma analítica da função f(x) a
ser integrada, mas sim uma tabela de pontos que descreve o comportamento da função
Em determinados casos não podemos calcular F(x) ou a sua
obtenção não é simples
Em situações práticas nem sempre se tem a forma analítica da função f(x) a
ser integrada, mas sim uma tabela de pontos que descreve o comportamento da função
31
Regra dos Trapézios
A idéia básica é a substituição da função f(x) por uma aproximação no intervalo [a,b]
Integrando no intervalo [x0 , x1] teremos
x1
f(x)
x0 x
O que é a fórmula da área do trapézio, como mostrado na figura
onde h = x1 − x0
A = (B + b) * h / 2
[
( ) ( )]
) 2
( 0 1
1
0
x f x
h f dx
x
x f
x ≅ +
∫
B b
h
Regra dos Trapézios ...
Quanto maior for o intervalo, maior será o erro do método
O que se utiliza é subdividir o intervalo em vários pedaços, calcular a área de cada um deles e em seguida somar todos
[ ]
)) ( ) ( 2( ...
)) ( ) ( 2( )) ( ) ( 2( ) (
) ( ) 2 (
) (
1 2
1 1
0 1
0
1
n n
n
i
i i
x f x
h f x
f x
h f x
f x
h f x F
x f x h f
x F
+ +
+ +
+ +
≈
+
≈
−
−
= +
∑
f(x)
n = 8
33
Implementação em MPI
x4 x x0 x1 x2 x3
f(x)
Passo p
rank 1
rank 1 rank 2rank 2 rank 3rank 3 rank 4rank 4
+ ResultadoFinal rank 0 rank 0
Cálculos parciais
result_parcial=0;
for ( i = (rank-1)*delta; i+p-precisao < (rank)*delta; i+=p ){
result_parcial += p * 0.5 * ( f(i)+ f(i+INC-precisao) );
}
result_parcial=0;
for ( i = (rank-1)*delta; i+p-precisao < (rank)*delta; i+=p ){
result_parcial += p * 0.5 * ( f(i)+ f(i+INC-precisao) );
}
P
#
0
4 x
x −
=
∆
... ...
x0 x0 + p precisão
Atenção
Σ r4 Σ r3 Σ r2 Σ r1
Cálculo da integral com f(x) = x no intervalo [ 0,10 ]
) 2
...
2 2 (
) (
)) 1 ( 9
, 0 ( 2 ...
) 1 , 0 ( 2 )
0 ( 2 (
) (
1 , 10 0
1 9
, 0 1
, 0
0 + + + +
≈
+ +
+ +
≈
− =
=
e e
e h e
x F
f f
f h f
x F
a h b
Exemplo
1 50
0 =
=
∫
x dxCálculo da integral com f(x) = ex no intervalo [ 0,1 ]
35
Hello
Hello
SayHi(int n) Hello
SayHi(int n)
# processadores = 6 2 Classes – Main e Hello
nElements = 10 objetos Hello (array objetos)
Main
Main done Main done
arr[0]
Hello
SayHi(n) SayHi(n)
Main
Main done Main done
arr[1]
Hello
SayHi(n) SayHi(n)
arr[9]
Hello
SayHi(n) SayHi(n)
...
arr[0].SayHi(17) arr[1].SayHi(17+1) arr[9].SayHi(17+9)
Main.done()
Hello Charm++ (Objects Array)
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);
};
#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);
};
/*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();
/*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();
37
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
>
Aula 5
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