Introdução a Computação Paralela:
Rotinas MPI em Clusters Beowulf
Miguel Dias Costa
João Viana Lopes
Estrutura do Curso
Motivação Conceitos de Computação Paralela
Rotinas MPI (Message Passing Interface)
Estrutura do Curso
Motivação
Conceitos de Computação Paralela
Rotinas MPI (Message Passing Interface)
Estrutura do Curso
Motivação
Conceitos de Computação Paralela
Rotinas MPI (Message Passing Interface)
Recursos Online
Livro “MPI - The Complete Reference”:
http://www.netlib.org/utk/papers/mpi-book/mpi-book.html
Distribuição MPICH:
http://www-unix.mcs.anl.gov/mpi/mpich/
Distribuição LAM:
http://www.lam-mpi.org
Standards MPI:
http://www.mpi-forum.org
Bibliotecas Matemáticas
ScaLAPACK
(Scalable LAPACK)
LAPACK
(Linear Algebra PACKage)
PBLAS
(Parallel BLAS)
BLACS
(Basic Linear Algebra Communications
Subprograms)
Limitações
Limitações da Computação Sequencial Aplicações científicas exigem (tipicamente)
velocidade de processamento memória
Lei de Moore não chega
Limitações
Limitações da Computação Sequencial Aplicações científicas exigem (tipicamente) velocidade de processamento
memória
Lei de Moore não chega
Limitações
Limitações da Computação Sequencial Aplicações científicas exigem (tipicamente)
velocidade de processamento memória
Lei de Moore não chega
Limitações
Limitações da Computação Sequencial Aplicações científicas exigem (tipicamente)
velocidade de processamento memória
Lei de Moore não chega
Limitações
Limitações da Computação Sequencial Aplicações científicas exigem (tipicamente)
velocidade de processamento memória
Lei de Moore não chega
Soluções
Máquinas Paralelas de Memória Partilhada
Máquinas Paralelas de Memória Distribuída
Soluções
Máquinas Paralelas de Memória Partilhada Máquinas Paralelas de Memória Distribuída
Vantagem:
Não é necessário paralelizar explicitamente
Soluções
Máquinas Paralelas de Memória Partilhada Máquinas Paralelas de Memória Distribuída
Desvantagem:
Preço
Soluções
Máquinas Paralelas de Memória Partilhada
Máquinas Paralelas de Memória Distribuída
Soluções
Máquinas Paralelas de Memória Partilhada Máquinas Paralelas de Memória Distribuída Vantagem:
Escalonável
Soluções
Máquinas Paralelas de Memória Partilhada Máquinas Paralelas de Memória Distribuída Desvantagens:
É necessário paralelizar explicitamente
O Cluster Beowulf do CFP
Especificações
Dual-Athlons MP 1.2 MHz Motherboards Tyan Tiger
1 GB de RAM DDR por máquina
Software livre (Linux)
Single System Image
Todo o cluster comporta-se como se fosse uma única máquina, com
adição “a quente” de nodos (Beoboot) espaço de processos partilhado (Bproc) fila de espera (Beowulf Batch Queue)
monitorização em tempo real (Beostatus)
Conceitos de Computação Paralela
Paralelismo - capacidade de várias tarefas
independentes avançarem simultaneamente no sentido de completar um cálculo
Paralelismo disponível - número máximo de tarefas que podem correr simultaneamente
Por vezes reduzido ou nulo
No entanto, isto é pouco frequente. Porquê?
Conceitos de Computação Paralela
Paralelismo - capacidade de várias tarefas
independentes avançarem simultaneamente no sentido de completar um cálculo
Paralelismo disponível - número máximo de tarefas que podem correr simultaneamente Por vezes reduzido ou nulo
No entanto, isto é pouco frequente. Porquê?
Conceitos de Computação Paralela
Paralelismo - capacidade de várias tarefas
independentes avançarem simultaneamente no sentido de completar um cálculo
Paralelismo disponível - número máximo de tarefas que podem correr simultaneamente
Por vezes reduzido ou nulo
No entanto, isto é pouco frequente. Porquê?
Conceitos de Computação Paralela
Paralelismo - capacidade de várias tarefas
independentes avançarem simultaneamente no sentido de completar um cálculo
Paralelismo disponível - número máximo de tarefas que podem correr simultaneamente
Por vezes reduzido ou nulo
No entanto, isto é pouco frequente. Porquê?
Origem do Paralelismo
Usamos máquinas paralelas porque uma dada aplicação demora demasiado tempo (CPU) ou é demasiado grande (RAM).
Componente grande:
Número de Tamanho de A componente grande é, tipicamente, a fonte de
paralelismo.
Origem do Paralelismo
Usamos máquinas paralelas porque uma dada aplicação demora demasiado tempo (CPU) ou é demasiado grande (RAM).
Componente grande:
Número de partículas Tamanho de
A componente grande é, tipicamente, a fonte de
paralelismo.
Origem do Paralelismo
Usamos máquinas paralelas porque uma dada aplicação demora demasiado tempo (CPU) ou é demasiado grande (RAM).
Componente grande:
Número de parâmetros Tamanho de
A componente grande é, tipicamente, a fonte de
paralelismo.
Origem do Paralelismo
Usamos máquinas paralelas porque uma dada aplicação demora demasiado tempo (CPU) ou é demasiado grande (RAM).
Componente grande:
Número de tarefas...
Tamanho de A componente grande é, tipicamente, a fonte de
paralelismo.
Origem do Paralelismo
Usamos máquinas paralelas porque uma dada aplicação demora demasiado tempo (CPU) ou é demasiado grande (RAM).
Componente grande:
Número de tarefas...
Tamanho de estrutura A componente grande é, tipicamente, a fonte de
paralelismo.
Origem do Paralelismo
Usamos máquinas paralelas porque uma dada aplicação demora demasiado tempo (CPU) ou é demasiado grande (RAM).
Componente grande:
Número de tarefas...
Tamanho de imagem A componente grande é, tipicamente, a fonte de
paralelismo.
Origem do Paralelismo
Usamos máquinas paralelas porque uma dada aplicação demora demasiado tempo (CPU) ou é demasiado grande (RAM).
Componente grande:
Número de tarefas...
Tamanho de base de dados...
A componente grande é, tipicamente, a fonte de
paralelismo.
Origem do Paralelismo
Usamos máquinas paralelas porque uma dada aplicação demora demasiado tempo (CPU) ou é demasiado grande (RAM).
Componente grande:
Número de tarefas...
Tamanho de base de dados...
Quantidade de Paralelismo
Paralelismo “embaraçoso” : Por exemplo, correr
o mesmo cálculo para diferentes condições iniciais ou parâmetros.
Paralelismo insuficiente:
Relação causal entre
sub-tarefas dificulta a paralelização.
Quantidade de Paralelismo
Paralelismo “embaraçoso” : Por exemplo, correr o mesmo cálculo para diferentes condições iniciais ou parâmetros.
Paralelismo insuficiente:
Relação causal entre
sub-tarefas dificulta a paralelização.
Quantidade de Paralelismo
Paralelismo “embaraçoso” : Por exemplo, correr o mesmo cálculo para diferentes condições iniciais ou parâmetros.
Paralelismo insuficiente: Relação causal entre
sub-tarefas dificulta a paralelização.
Lei de Amdahl
“Se apenas 1% de um problema não pode ser paralelizado, o problema nunca pode ser resolvido
mais de 100 vezes mais rápido do que no caso sequencial, qualquer que seja o paralelismo
disponível para o resto”
No entanto, os problemas escalam, tipicamente, à
custa das componentes paralelas.
Lei de Amdahl
“Se apenas 1% de um problema não pode ser paralelizado, o problema nunca pode ser resolvido
mais de 100 vezes mais rápido do que no caso sequencial, qualquer que seja o paralelismo
disponível para o resto”
No entanto, os problemas escalam, tipicamente, à
custa das componentes paralelas.
Paralelismo em Clusters
O cenário mais comum é um problema ser
paralelizável mas de uma forma difícil de explorar com um cluster.
Comunicações demasiado frequentes podem
neutralizar a vantagem da paralelização.
Decomposição de Problemas
Decomposição por Domínios Os dados são divididos em pedaços
aproximadamente iguais e divididos entre os processadores
Decomposição por Tarefas O problema é dividido em sub-tarefas que são
atribuídas aos processadores à medida que estes
ficam disponíveis
Decomposição de Problemas
Decomposição por Domínios
Os dados são divididos em pedaços
aproximadamente iguais e divididos entre os processadores
Decomposição por Tarefas O problema é dividido em sub-tarefas que são
atribuídas aos processadores à medida que estes
ficam disponíveis
Decomposição de Problemas
Decomposição por Domínios
Os dados são divididos em pedaços
aproximadamente iguais e divididos entre os processadores
Decomposição por Tarefas
O problema é dividido em sub-tarefas que são
Classificação de Algoritmos Paralelos
Regularidade estruturas de dados rectangulares?
Sincronicidade secções diferentes têm de permanecer
sincronizadas?
razão entre comunicação e computação
Classificação de Algoritmos Paralelos
Regularidade
estruturas de dados rectangulares?
Sincronicidade secções diferentes têm de permanecer
sincronizadas?
razão entre comunicação e computação
Classificação de Algoritmos Paralelos
Regularidade
estruturas de dados rectangulares?
Sincronicidade
secções diferentes têm de permanecer sincronizadas?
razão entre comunicação e computação
Classificação de Algoritmos Paralelos
Regularidade
estruturas de dados rectangulares?
Sincronicidade
secções diferentes têm de permanecer
sincronizadas?
Objectivos da Paralelização
O principal objectivo da paralelização é obter melhor performance do que no caso sequencial. Para tal, é necessário ter em conta os seguintes aspectos:
“load balancing”
minimizar a comunicação
sobrepor comunicação e computação
Objectivos da Paralelização
O principal objectivo da paralelização é obter melhor performance do que no caso sequencial. Para tal, é necessário ter em conta os seguintes aspectos:
“load balancing”
minimizar a comunicação
sobrepor comunicação e computação
Objectivos da Paralelização
O principal objectivo da paralelização é obter melhor performance do que no caso sequencial. Para tal, é necessário ter em conta os seguintes aspectos:
“load balancing”
minimizar a comunicação
sobrepor comunicação e computação
Objectivos da Paralelização
O principal objectivo da paralelização é obter melhor performance do que no caso sequencial. Para tal, é necessário ter em conta os seguintes aspectos:
“load balancing”
minimizar a comunicação
sobrepor comunicação e computação
Comunicação
Em geral, o tempo de comunicação de uma mensagem é dado por
com
lat
tamanho
largura de banda
largura de banda
10 MB/s
latência
200
Rotinas MPI
Processo MPI :
programa C ou Fortran
(MPI-2 suporta C++ e Fortran 90) +
biblioteca MPI
(mpicc (MPICH) ou hcc (LAM) em vez do gcc)
e
Código Fonte
Em geral, todos os processos de uma aplicação MPI têm o mesmo código fonte
Como evitamos que todos os processos façam exactamente a mesma coisa?
A função MPI_COMM_RANK diz-nos qual o número de ordem de um processo dentro do comunicador
MPI_COMM_WORLD (o conjunto de todos os
processos)
Código Fonte
Em geral, todos os processos de uma aplicação MPI têm o mesmo código fonte
Como evitamos que todos os processos façam exactamente a mesma coisa?
A função MPI_COMM_RANK diz-nos qual o número de ordem de um processo dentro do comunicador
MPI_COMM_WORLD (o conjunto de todos os
processos)
Código Fonte
Em geral, todos os processos de uma aplicação MPI têm o mesmo código fonte
Como evitamos que todos os processos façam exactamente a mesma coisa?
A função MPI_COMM_RANK diz-nos qual o número
de ordem de um processo dentro do comunicador
Inicialização e Finalização
...
MPI_Init(&argc,&argv);
MPI_Comm_Rank(MPI_Comm_World, &rank);
MPI_Comm_Size(MPI_Comm_World, &size);
...
rotinas de comunicação MPI ...
MPI_Finalize();
Comunicadores
A maior parte das rotinas de comunicação MPI requer a especificação de um comunicador que define o contexto em que a comunicação é feita.
As rotinas MPI_Comm_Size e MPI_Comm_Rank permitem escrever programas que se ajustam
dinamicamente, quando executados, ao número de
processadores disponíveis.
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Conteúdo da mensagem:
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Conteúdo da mensagem:
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Conteúdo da mensagem:
buffer: dados a enviar/receber
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Conteúdo da mensagem:
count: número de elementos do tipo datatype
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Conteúdo da mensagem:
datatype: tipo de variável MPI
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Envelope (selecção de mensagens):
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Envelope (selecção de mensagens):
src/dest: número do processador origem/destino
dentro do comunicador comm
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Envelope (selecção de mensagens):
tag: etiqueta para identificar a comunicação
Rotinas de Comunicação
MPI_Send e MPI_Recv
MPI_Send( buffer, count, datatype, dest, tag, comm ) MPI_Recv( buffer, count, datatype, src, tag, comm )
Envelope (selecção de mensagens):
comm: comunicador
Tipos de Dados
datatype tem de ser um dos tipos de dados MPI:
C:
MPI_CHAR
MPI_UNSIGNED_CHAR MPI_SHORT
MPI_INT MPI_LONG
MPI_UNSIGNED_SHORT
MPI_UNSIGNED
MPI_UNSIGNED_LONG MPI_FLOAT
MPI_DOUBLE
MPI_LONG_DOUBLE MPI_BYTE
Fortran:
Hello World
#include <mpi.h>
#include <stdio.h>
int main( int argc, char **argv){
int rank, size, partner, len;
char name[MPI_MAX_PROCESSOR_NAME];
char greeting[sizeof(name) + 100];
MPI_Init(&argc, &argv);
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação // Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master // imprimo a minha saudação
// e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// mas, se eu for um dos outros
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles // e imprimo-as
// só envio a minha saudação
Hello World
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Get_processor_name(name, &len);
sprintf(greeting, “Olá mundo: número %d de %d a correr no %s!\n”, rank, size, name);
if (rank == 0) {
fputs(greeting, stdout);
for(partner=1; partner < size; partner++) {
MPI_Recv(greeting, sizeof(greeting), MPI_BYTE, partner, 1, MPI_COMM_WORLD, &stat);
fputs(greeting, stdout);
// Quantos processadores?
// Qual sou eu?
// Onde estou a correr?
// Escrever uma saudação
// Se eu for o master
// imprimo a minha saudação // e para todos os outros
// recebo as saudações deles
// e imprimo-as
Hello World
$ mpicc -o hello hello.c
Numa única máquina:
$ mpirun -np 4 ./hello
Olá mundo: número 0 de 4 a correr no miu-1!
Olá mundo: número 1 de 4 a correr no miu-1!
Olá mundo: número 2 de 4 a correr no miu-1!
Olá mundo: número 3 de 4 a correr no miu-1!
No cluster:
$ mpirun -np 4 -nolocal ./hello
Olá mundo: número 0 de 4 a correr no .0!
Olá mundo: número 1 de 4 a correr no .0!
Olá mundo: número 2 de 4 a correr no .1!
Olá mundo: número 3 de 4 a correr no .1!
Handshake
E quando todos enviam e recebem?
fputs(greeting, stdout);
MPI_Send(greeting, ..., partner, ...);
MPI_Recv(greeting, ..., partner, ...);
fputs(greeting, stdout);
fputs(greeting, stdout);
MPI_Send(greeting, ..., partner, ...);
MPI_Recv(greeting, ..., partner, ...);
fputs(greeting, stdout);
Handshake
E quando todos enviam e recebem?
fputs(greeting, stdout);
MPI_Send(greeting, ..., partner, ...);
MPI_Recv(greeting, ..., partner, ...);
fputs(greeting, stdout);
fputs(greeting, stdout);
MPI_Recv(greeting, ..., partner, ...);
MPI_Send(greeting, ..., partner, ...);
fputs(greeting, stdout);
Bloqueio
Quando se usam estas funções MPI_Send e MPI_Recv standard, é necessário que o envio e recepção de mensagens coincidam, sob o risco de o
programa bloquear.
Soluções:
garantir explicitamente que para cada Send
existe um Recv antes de outras funções MPI
Funções Colectivas
Operações que envolvem todos os processos dentro de um comunicador
São chamadas da mesma forma dentro de todos os processos
Os argumentos determinam as diferenças de
comportamento
Funções Colectivas
MPI_Broadcast, MPI_Gather, MPI_Scatter, MPI_Reduce
MPI_Broadcast(buffer, count, datatype, sender_rank, comm)
MPI_Reduce(send_buffer, recv_buffer, count,
datatype, operation, receiver_rank, comm)
Broadcast
Dados
Processos
A A
A
A
Broadcast
Dados
Processos
A
A
A
A
Scatter
Dados
Processos
A B C D
B
C
D
Scatter
Dados
Processos
A B C D
B
C
D
Gather
Dados
Processos
A B C D
B
C
D
Gather
Dados
Processos
A B C D
B
C
D
Reduce
Dados
Processos
A B C D (..,..,..) = MPI_SUM, MPI_PROD, MPI_MAX,
MPI_MIN, MPI_BAND, MPI_BOR, MPI_BXOR,
MPI_LAND, MPI_LOR, MPI_LXOR
Reduce
Dados
Processos
A (A,B,C,D) B
C D (..,..,..) = MPI_SUM, MPI_PROD, MPI_MAX,
MPI_MIN, MPI_BAND, MPI_BOR, MPI_BXOR,
MPI_LAND, MPI_LOR, MPI_LXOR
Reduce
Dados
Processos
A (A,B,C,D) B
C D
(..,..,..) = MPI_SUM, MPI_PROD, MPI_MAX,
MPI_MIN, MPI_BAND, MPI_BOR, MPI_BXOR,
Reduce - Exemplo
MPI_Reduce (&hist_local, &hist_global, 100, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD)
global
Np
k=1
k local
Dados
Processos
[0,0]
[1,1]
[2,2]
[3,3]
Reduce - Exemplo
MPI_Reduce (&hist_local, &hist_global, 100, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD)
global
Np
k=1
k local
Dados
[0,0]
Reduce - Exemplo
MPI_Reduce (&hist_local, &hist_global, 100, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD)
global
Np
k=1
k local