• Nenhum resultado encontrado

Aula 3 Computação Distribuída de Alto Desempenho

N/A
N/A
Protected

Academic year: 2021

Share "Aula 3 Computação Distribuída de Alto Desempenho"

Copied!
52
0
0

Texto

(1)

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

(2)

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)

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

(4)

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)

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

(6)

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)

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

(8)

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)

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

(10)

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)

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

(12)

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)

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.

(14)

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)

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

...

} }

(16)

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)

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

(18)

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)

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

(20)

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)

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)

(22)

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)

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!

(24)

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)

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

(26)

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)

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

(28)

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)

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 12

16 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

(30)

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)

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

(32)

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

12

11 10

9

r2

8

7 6

5

r1

4

3 2

1

r0

0

(33)

33

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;

(34)

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)

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

(36)

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)

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

...

(38)

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)

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

§1

Usa 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).

(40)

Charm++ ...

charm.cs.uiuc.edu

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

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

(42)

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)

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"

(44)

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)

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

(46)

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: esc1Password: cbpf

Quinta (19/07)

Equação do segundo grauCompilação

Script pbs

Sexta (19/07)

MPI básicoMPI avançadoIntegral em mpicharm++

(47)

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

=

b

a

f x dx

x

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

Nestes casos, utilizamos a integração numérica

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

Nestes casos, utilizamos a integração

numérica

Referências

Documentos relacionados