Message Passing Interface - MO601 Message Passing Interface (MPI) Gustavo Waku [email protected] 1 • • • • • • • • • • • Introdução e breve histórico Conceitos Básicos Comunicação ponto a ponto DeadLocks e Starvation Modos de comunicação e operações não bloqueantes Comunicação coletiva Topologia de Processos Entrada e Saída Paralela Fechamento Q&A Referências Bibliográficas Message Passing Interface - MO601 Agenda 2 • O que é o MPI? • É uma especificação que define um conjunto de apis para passagem de mensagens, estabelecendo padrões de comportamento. • Várias implementações, comportamentos diferentes => sistemas heterogêneos, diversos fabr, necessidade de convergência. • Adoção em ambientes hetetorêneos (portabilidade, facilidade de programar, grande nro de rotinas disponíveis). • Atualmente especificado pelo MPI forum [http://mpj-express.org/] • Utilizado em sistemas de alta performance. Message Passing Interface - MO601 Introdução e Breve histórico V1.0 Mai V1.1 Jun V1.2 Mai V2.1 Jun V1.2 Jul e V2.0 V2.2 Set V3.0 Set 1994 1995 1997 2008 2009 2012 3 • Processo (Process) • Identificado pelo rank (similar ao pid do linux, vai de 0 a N-1). • Grupos (Group) • Coleção de processos • Contexto (Context) • Particiona o espaço, e serve para isolar. • Communicador (Communicator) Message Passing Interface - MO601 Conceitos Básicos • Agrega os conceitos de Grupo + Contexto (MPI_COMM_WORLD, MPI_COMM_SELF) • Intra-Communicator (dentro do grupo), Inter-Communicator (entre grupos) 4 • Formação de pacotes e matching • Como funciona • Par send – receive • DEST/SOURCE+ TAG + COMM => envelope • matching • Variantes com MPI_ANY_SOURCE, MPI_ANY_TAG. • Assimetria entre send / receive. • Como identificar processos? ( comm + rank ). Message Passing Interface - MO601 Comunicação ponto a ponto 5 Comunicação ponto a ponto Processo 0 #include "mpi.h" Send int main( int argc, char *argv[]) { char message[20]; int myrank; MPI_Status status; MPI_Init( &argc, &argv ); MPI_Comm_rank( MPI_COMM_WORLD, &myrank ); if (myrank == 0) /* code for process zero */ { strcpy(message,"Hello, there"); MPI_Send(message, strlen(message)+1, MPI_CHAR, 1, 99, MPI_COMM_WORLD); } else if (myrank == 1) /* code for process one */ { MPI_Recv(message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD, &status); printf("received :%s:\n", message); } MPI_Finalize(); return 0; } Processo 1 Receive Message Passing Interface - MO601 • Sends/Receives Bloqueantes, unidirecional 6 Comunicação ponto a ponto • Sends/Receives Bloqueantes public class HelloWorldMPI { public static final int TAG = 99; public static void main(String[] args) throws Exception { MPI.Init(args) ; int myrank = MPI.COMM_WORLD.Rank(); int numprocs = MPI.COMM_WORLD.Size(); System.out.println("I am process <"+myrank+"> of total <"+ numprocs +"> processes."); char [] message1 = "Hello There, I`m process <0> speaking".toCharArray(); char [] buf1 = new char[message1.length]; char [] message2 = ("Hi, I`m process <" + myrank + "> speaking").toCharArray(); char [] buf2 = new char[message2.length]; if (myrank == 0) { for ( int i=1; i<numprocs; i++ ) { System.out.println(" process <0> sends message to: <" + i + ">"); MPI.COMM_WORLD.Send(message1, 0, message1.length, MPI.CHAR, i, TAG); } for ( int i=1; i<numprocs; i++ ) { System.out.println(" process <0> waits to receive messages from <" + i + ">"); MPI.COMM_WORLD.Recv(buf2, 0, buf2.length, MPI.CHAR, i, TAG); } } else { System.out.println(" process <"+ myrank +"> waits to receive from <0>"); MPI.COMM_WORLD.Recv(buf1, 0, buf1.length, MPI.CHAR, 0, TAG); System.out.println("process <"+ myrank +"> received: " + new String(buf1) + "!"); System.out.println(" process <"+ myrank +"> sends message back to <0>"); MPI.COMM_WORLD.Send(message2, 0, message2.length, MPI.CHAR, 0, TAG); } MPI.Finalize(); } } P0 P1 P2 P3 … Message Passing Interface - MO601 package p1; import mpi.*; PN Múltipos Processos: - P0 envia a mensagem e aguarda a resposta - Os demais recebem e enviam uma outra mensagem para P0 7 Comunicação ponto a ponto P1 (4) Inicia e aguarda P0 (8) Recebe msg de P0 (9) Envia msg para P0 P2 (3) Inicia e aguarda P0 (12) Recebe msg de P0 (13) Envia msg para P0 Message Passing Interface - MO601 MPJ Express (0.38) is started in the multicore configuration I am process <3> of total <4> processes. process <3> waits to receive from <0> P0 I am process <0> of total <4> processes. (2) Inicia e Envia msg process <0> sends message to: <1> para P1 I am process <2> of total <4> processes. (5) Envia msg para P2 process <2> waits to receive from <0> (6) Envia msg para P3 (7) Aguarda msg de P1 I am process <1> of total <4> processes. (14) Recebe msg de P1 process <1> waits to receive from <0> (15) Recebe msg de P2 process <0> sends message to: <2> (16) Recebe msg de P3 process <0> sends message to: <3> process <0> waits to receive messages from <1> process <1> received: Hello There, I`m process <0> speaking! process <1> sends message back to <0> process <3> received: Hello There, I`m process <0> speaking! process <3> sends message back to <0> process <2> received: Hello There, I`m process <0> speaking! process <2> sends message back to <0> process <0> received: Hi, I`m process <1> speaking process <0> waits to receive messages from <2> process <0> received: Hi, I`m process <2> speaking process <0> waits to receive messages from <3> process <0> received: Hi, I`m process <3> speaking P3 (1) Inicia e aguarda P0 (10) Recebe msg de P0 (11) Envia msg para P0 8 • Deadlocks • Em uma comunicação síncrona, podem ocorrer deadlocks caso o programa tenha sido projetado de forma equivocada. Um exemplo é a situação em que um processo 0 envia uma mensagem ao processo 1 e fica esperando, o processo 1 envia para o processo 0 que também fica esperando. Uma maneira de evitar isso é fazer com que um deles comece recebendo a mensagem, e o outro enviando. • Outras situações de deadlock podem ocorrer e devem ser observadas caso a caso. O padrão não especifica formas de identificar, apenas enumera alguns possíveis casos de deadlock. Processo 0 Processo 1 Processo 0 Processo 1 Send Send Send Receive Receive Receive Receive Send Message Passing Interface - MO601 Deadlocks e Starvation 9 • Starvation • O protocolo não garante justiça na comunicação. Suponha que um processo envie uma mensagem, o processo destino pode não receber a mensagem devido a mensagem ter sido sobreposta por outra mensagem enviada por outro processo. De maneira análoga, suponha vários processos receptores de mensagens produzidas por outro processo, é possível que algum deles nunca receba a mensagem produzida. É responsabilidade do programador garantir que não ocorra starvation. Processo 0 Send Processo 1 Send ?, X Processo 2 Processo 0 Receive Send Message Passing Interface - MO601 Deadlocks e Starvation Processo 1 ?, X Receive Processo 2 Receive Processo 3 Receive 10 • Bloqueantes e não bloqueantes (continuam ou não a execução) • Local ou não local (depende ou não de um comando correspondente). • Sends Bloqueantes: • Standard (sujeito à disponibilidade de memória do sistema se a mensagem vai ser armazenada em buffer ou não; se ela for armazenada em buffer, o programa pode continuar assim que a mensagem for transferida ao buffer. Se não houver memória (ou por questões de desempenho) e a mensagem não for armazenada em buffer, o programa não retoma sua execução até que o comando receive correspondente seja invocado. – não local • Buffered (copiada para um buffer temporário e não espera o comando receive correspondente para continuar a execução) – local • Synchronous (só termina quando o comando receive correspondente é alcançado e a mensagem começou a ser recebida) – não local • Ready (espera o comando receive correspondente ser alcançado para poder iniciar, semântica igual ao send standard e send synchronous.) – não local • Mesma idéia para Sends não bloqueantes. (mas dividida em start e complete). send_start, send_complete, receive_start, receive_complete • Receives não possuem tantas variantes, apenas bloq./não bloq. Message Passing Interface - MO601 Modos de comunicação e operações não bloqueantes 11 Message Passing Interface - MO601 Modos de comunicação e operações não bloqueantes • Todas as combinações de send-receives bloqueantes e não bloqueantes são possíveis. • Ex: send bloqueante – receive não bloqueante, etc… 12 • Objetivo: fornecer uma maneira de comunicação entre os processos de um grupo ou entre grupos de processos. Exemplos principais são barreiras e broadcasts. (APIS relacionadas: MPI_BARRIER, MPI_BCAST, MPI_SCATTER, MPI_GATHER, MPI_ALLGATHER, MPI_ALLTOALL ) • MPI-3.0 adicionou operações assíncronas: MPI_ISCATTER, MPI_IBARRIER, etc… Message Passing Interface - MO601 Comunicação Coletiva 13 • Exemplo de MPI_BCAST(buffer, count, datatype, root, comm) • Broadcast de 100 inteiros do processo 0 (root) para cada processo dentro do grupo (comm) …. MPI_Comm comm; int array[100]; int root=0; ... MPI_Bcast(array, 100, MPI_INT, root, comm); Message Passing Interface - MO601 Comunicação Coletiva 14 • Atributo extra e opcional dado ao intRA-comunicador • Ajuda a prover um mecanismo de referência conveniente para processos (dentro de um grupo) • Pode ajudar no mapeamento físico de processos • O padrão MPI não estabelece como devem ser mapeados os processos em hardware. Cada fornecedor possui uma impl. • Tipos suportados: Cartesiana, Grafal e Grafal Distribuída. Message Passing Interface - MO601 Topologia (Virtual) de Processos 15 • E/S tradicionalmente é feita de forma serial, uma possível otimização é criar vários processos fazendo uma computação e deixar um processo cuidando de toda a E/S. #include "mpi.h" #include <stdio.h> #define BUFSIZE 100 int main(int argc, char *argv[]) { int i, myrank, numprocs, buf[BUFSIZE]; MPI_Status status; FILE *myfile; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); MPI_Comm_size(MPI_COMM_WORLD, &numprocs); for (i=0; i<BUFSIZE; i++) buf[i] = myrank * BUFSIZE + i; if (myrank != 0) MPI_Send(buf, BUFSIZE, MPI_INT, 0, 99, MPI_COMM_WORLD); else { myfile = fopen("testfile", "w"); fwrite(buf, sizeof(int), BUFSIZE, myfile); for (i=1; i<numprocs; i++) { MPI_Recv(buf, BUFSIZE, MPI_INT, i, 99, MPI_COMM_WORLD, &status); } fwrite(buf, sizeof(int), BUFSIZE, myfile); } fclose(myfile); MPI_Finalize(); return 0; } • Vantagem: Message Passing Interface - MO601 E/S Paralela • Simplicidade (com a delegação de E/S para 1 único processo). • Desvantagem: • Processo 0 precisa esperar por todos, pouca paralelização. 16 • Escrita em um único arquivo com MPI_FILE_SET_VIEW(fh, offset, etype, filetype, datarep, info) e MPI_FILE_WRITE. #include "mpi.h" #include <stdio.h> #define BUFSIZE 100 int main(int argc, char *argv[]) { int i, myrank, buf[BUFSIZE]; MPI_File thefile; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); for (i=0; i<BUFSIZE; i++) buf[i] = myrank * BUFSIZE + i; MPI_File_open(MPI_COMM_WORLD, "testfile", MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); MPI_File_set_view(thefile, myrank * BUFSIZE * sizeof(int), MPI_INT, MPI_INT, "native", MPI_INFO_NULL); MPI_File_write(thefile, buf, BUFSIZE, MPI_INT, MPI_STATUS_IGNORE); MPI_File_close(&thefile); MPI_Finalize(); return 0; } Message Passing Interface - MO601 E/S Paralela 17 • Leitura paralela, setando o ponteiro na posição com MPI_FILE_SET_VIEW e lendo com MPI_FILE_READ #include "mpi.h" #include <stdio.h> int main(int argc, char *argv[]) { int myrank, numprocs, bufsize, *buf, count; MPI_File thefile; MPI_Status status; MPI_Offset filesize; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); MPI_Comm_size(MPI_COMM_WORLD, &numprocs); MPI_File_open(MPI_COMM_WORLD, "testfile", MPI_MODE_RDONLY, MPI_INFO_NULL, &thefile); MPI_File_get_size(thefile, &filesize); /* in bytes */ filesize = filesize / sizeof(int); /* in number of ints */ bufsize = filesize / numprocs + 1; /* local number to read */ buf = (int *) malloc (bufsize * sizeof(int)); MPI_File_set_view(thefile, myrank * bufsize * sizeof(int), MPI_INT, MPI_INT, "native", MPI_INFO_NULL); MPI_File_read(thefile, buf, bufsize, MPI_INT, &status); MPI_Get_count(&status, MPI_INT, &count); printf("process %d read %d ints\n", myrank, count); MPI_File_close(&thefile); MPI_Finalize(); return 0; } Message Passing Interface - MO601 E/S Paralela 18 • O padrão MPI define um conjunto de operações padrão + comportamento esperado • Portabilidade e Facilidade de Uso • Implementações • Fortran, C, C++, Java • Produtos Comerciais • • • • • SGI Message Passing Toolkit WMPI II Sun MPI Intel MPI HP-MPI Message Passing Interface - MO601 Fechamento 19 Message Passing Interface - MO601 Q&A ? 20 [1] MPI: A Message-Passing Interface Standard Version 3.0. Setembro 2012. Disponível em: http://www.mpi-forum.org/docs/mpi-3.0/mpi30-report.pdf. Acessado em outubro de 2012. [2] Message Passing Interface. Disponível em: http://en.wikipedia.org/wiki/Message_ Passing_Interface. Acessado em: outubro de 2012. [3] MPJ Express. Disponível em: http://mpj-express.org/. Acessado em: outubro de 2012. [4] MPJ Express: An Implementation of MPI in Java Linux/UNIX/Mac User Guide. Disponível em: http://mpj-express.org/docs/guides/linuxguide.pdf. Acessado em: outubro de 2012. [5] Culler D. E.; Singh J. P. Kaufmman M. Parallel Computer Architecture. Morgan Kaufmann Publishers, 1999. Message Passing Interface - MO601 Referências Bibliográficas [6] Gropp W.; Lusk E.; Thakur R. Using MPI-2 Advanced Features of the Message Passing Interface, MIT press, 1994. [7] Open MPI. Disponível em: http://www.open-mpi.org/. Acessado em: outubro de 2012. [8] Sun MPI 6.0 Software Programming and Reference Manual. Disponível em: http://docs.oracle.com/cd/E19061-01/hpc.cluster5/817-0085-10/. Acessado em: outubro de 2012. 21 Message Passing Interface - MO601 BACKUP 22 • Uma forma alternativa seria escrever em múltiplos arquivos. #include "mpi.h" #include <stdio.h> #define BUFSIZE 100 int main(int argc, char *argv[]) { int i, myrank, buf[BUFSIZE]; char filename[128]; MPI_File myfile; • Vantagem: Paralelização da escrita e computação. MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); • Desvantagens: for (i=0; i<BUFSIZE; i++) • os arquivos precisam ser juntados para utilização futura por um buf[i] = myrank * BUFSIZE + i; outro processo sprintf(filename, "testfile.%d", myrank); • se uma aplicação for escrita de MPI_File_open(MPI_COMM_SELF, filename, forma paralela, ela vai MPI_MODE_WRONLY | MPI_MODE_CREATE, necessariamente paralelizar até MPI_INFO_NULL, &myfile); no máximo o número de arquivos produzidos MPI_File_write(myfile, buf, BUFSIZE, MPI_INT, • é difícil manusear e trafegar na MPI_STATUS_IGNORE); rede vários arquivos, assim como MPI_File_close(&myfile); manter lógica deles. MPI_Finalize(); return 0; } Message Passing Interface - MO601 E/S Paralela 23