Baixe o app para aproveitar ainda mais
Prévia do material em texto
1 Message Passing Interface (MPI) Gustavo Mitsuyuki Waku1 1Instituto de Computação (IC) – Universidade Estadual de Campinas (UNICAMP) Campinas, SP – Brazil gustavo.waku@gmail.com Abstract. The programming model based on message passing is very versatile, having portability as it’s major feature. This paper describes the specification of the MPI and does an investigation about it. Resumo. O modelo de programação baseado em passagem de mensagem é bastante versátil tendo como principal vantagem a portabilidade. Neste contexto este trabalho realiza a investigação e a descrição do padrão MPI. 1. Introdução Message Passing Interface (MPI) é um padrão que especifica um modelo (um conjunto de interfaces) e não sua implementação (como). O modelo de programação de passagem de mensagem é bastante poderoso por ser fácil de usar e possui alta portabilidade. A facilidade de uso se justifica pelo uso de interfaces simples claramente especificadas quanto ao seu comportamento, uso e parâmetros para operação. A portabilidade pelo fato de que com o uso passagem de mensagem, é possível escrever programas que irão funcionar em máquinas de tipos variados como por exemplo multiprocessadores com memória distribuída, redes de estações de trabalhos ou ambientes híbridos dos mais variados tipos. A idéia geral é permitir escrever aplicações que usem passagem de mensagem como modelo de programação de forma padronizada possibilitando interoperabilidade entre os programas e consequente aumento de escalabilidade. Atualmente, o padrão é utilizado principalmente em máquinas paralelas e clusters (conjuntos) de estações de trabalho. O modelo de programação usando passagem de mensagem era uma relativa novidade no início da década de 90 e vários fabricantes tinham sua própria implementação de passagem de mensagem, faltava uma especificação que “padronizasse” o segmento, sobretudo para permitir um uso mais uniforme e permitir uma interoperabilidade maior entre os diversos sistemas. Após várias reuniões com desenvolvedores, fabricantes, em 1994 a primeira versão do padrão MPI-1 foi proposta. A especificação é mantida por um fórum ( o MPI fórum http://www.mpi-forum.org/ ) e atualmente a versão mais recente é a MPI-3.0. O MPI-1 tratava basicamente de comunicação ponto a ponto, o MPI-2.0 apresentou novas áreas como Entrada/Saída Paralela, operações remotas de memória e gerenciamento dinâmico de processos, além de corrigir e adicionar novas funções ao MPI-1. O MPI-3.0 extendeu as operações sobre conjuntos para adicionar operações não bloqueantes e pequenas correções ao padrão proposto anteriormente. 2 Fig 1. Linha do tempo de evolução de versões Este trabalho apresenta as os conceitos básicos do padrão MPI mostrando exemplos de utilização, e está dividido nos seguintes tópicos: Conceitos Básicos, Comunicação ponto a ponto, Deadlocks, Starvation, Modos de comunicação e operações não bloqueantes, Comunicação coletiva, Topologia de Processos, Entrada e Saída Paralela. 2. Conceitos Básicos Para entender melhor a comunicação básica ponto a ponto, alguns conceitos iniciais são necessários como processo, grupo, contexto e comunicador. Processo é um módulo executável identificado por um rank (similar o pid do Linux e varia de 0 a N-1). O grupo é uma coleção ordenada de processos. O contexto é uma entidade que serve para separar o espaço, por exemplo mensagens de diferentes contexto não podem interferir uma na outra. O comunicador é um conceito que encapsula o conceito de grupo e contexto, definem como será a interação entre os processos. Os comunicadores mais comuns são MPI_COMM_WORLD (pode colaborar com outros processos), MPI_COMM_SELF (modo privado que inviabiliza colaboração). Existem basicamente dois tipos de comunicadores: intra e inter. Os Intra- comunicadores definem a comunicação dentro do grupos de processos, e inter- comunicadores, entre grupos de processos. 3. Comunicação ponto a ponto A comunicação básica ponto a ponto no padrão ocorre por operações básicas de send e receive. Estas operações podem ser síncronas (ou seja, esperam a operação completar antes de prosseguir com a execução) ou assíncronas (também chamadas de não-bloqueantes, i.e., não esperam o término da operação para prosseguir). O exemplo abaixo ilustra um exemplo de comunicação síncrona em C. #include "mpi.h" int main( int argc, char *argv[]) { char message[20]; int myrank; MPI_Status status; V 1 . 1 J u n 9 5 3 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; } O exemplo acima foi projetado para ser executado com 2 processos. O processo 0 envia uma mensagem “Hello, there” e o outro processo 1 a recebe. Cada processo é identificado pelo rank (variável myrank). Para enviar o processo 0 utiliza a api MPI_SEND sendo necessário indicar aonde está armazenada a mensagem (send buffer), o seu tamanho (em número de elementos), o tipo de informação que será enviado, o número do processo que irá receber, o tag, e o comunicator (espécie de grupo). Para receber uma mensagem o processo 1 utiliza o MPI_RECV, sendo necessário indicar o recipiente aonde será armazenada a mensagem (receive buffer), o tamanho máximo, o tipo de dado, quem está enviando (é o rank ou pid), o tag, comunicator, e status da operação. A api MPI_SEND é especificada da seguinte forma: MPI_SEND(buf, count, datatype, dest, tag, comm) Parameter type (IN, OUT or INOUT) Parameter name desc IN buf Initial address of send buffer IN count Number of elements in send buffer(non-negative integer) IN datatype Datatype of each send buffer elemento (handle) IN dest Rank of destination (integer) IN tag Message tag (integer) IN comm Communicator (handle) A tag é um identificador para rotular uma determinada mensagem, isso permite sua distinção de possíveis outras mensagens enviadas/recebidas para um mesmo processo. Communicator especifica um conjunto de processos que compartilham o mesmo contexto. Dentro deste grupo cada processo é identificado pelo seu rank. O mais comum é que seja utilizado o parâmetro pré-definido MPI_COMM_WORLD que permite comunicação entre todos os processos entre si. O conjunto source, dest, tag e comm é também chamado de envelope, o source é um valor implícito, dest é o valor do rank de destino, tag é um valor que varia de 0 a MPI_TAG_UB (constante menor que 32767). A operação bloqueante de receive é definida como: MPI_RECV (buf, count, datatype, source, tag, comm, status) Parameter type (IN, Parameter name desc 4 OUT or INOUT) OUT buf Initial address of receive buffer IN count Number of elements of receive buffer(non-negative integer) IN datatype Datatype of each send buffer elemento (handle) IN source Rank of source (integer) or MPI_ANY_SOURCE IN tag Message tag (integer) or MPI_ANY_TAG IN comm Communicator (handle) OUT status Status object O buffer de receive é um conjunto de count elementos consecutivos que começam em buf e são do tipo datatype. Se o conteúdo não couber em buf, um erro de overflow será gerado. Somente os count elementos são modificados em buf, os demais não são alterados, ou seja, é preciso que a manipulação seja feita de forma cuidadosa caso contrário o usuário poderá acabar lendo lixo de buf que não faz parte da mensagem original. Quando uma mensagem é recebida, o seu tag, source e comm devem “casar” com a mensagem enviada. Alternativamente, pode ser utilizados parâmetros como MPI_ANY_SOURCE e MPI_ANY_TAG, para especificar receptores genéricosindicando que qualquer valor de source ou de tag são aceitos. Podemos notar que a operação de send deve especificar necessariamente quem vai receber, enquanto a operação de receive pode aceitar dados de qualquer emissor, por isso essas operações são ditas assimétricas. O parâmetro status pode ser utilizada para consultar sobre possíveis erros, em C essa estrutura possui 3 campos básicos: MPI_SOURCE, MPI_TAG, MPI_ERROR. É importante especificar o tipo de dados que está sendo enviado e recebido para evitar erros de conversão de tipos dentro de um mesmo programa. Entre sistemas heterogêneos, muito embora exista a necessidade de conversão de tipos devido a representação para comunicação, o padrão não especifica uma regra de conversão de representação, apenas se espera que uma conversão de ponto flutuante seja feita ao valor mais representativo no sistema destino, visando preservar ao máximo seu valor inteiro, lógico e de caractere. A seguir segue um outro exemplo de comunicação ponto a ponto usando 4 processos. A implementação foi feita em java usando a biblioteca MPJ express que implementa a especificação do padrão MPI. /** * Universidade Estadual de Campinas * Programa desenvolvido para a disciplina MO601 como parte do trabalho sobre MPI * @author Gustavo Waku * */ package p1; import mpi.*; 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) { /* process zero broadcasts to all of the rest it`s message, * and waits to receive their ack */ for ( int i=1; i<numprocs; i++ ) { 5 System.out.println(" process <0> sends message to: <" + i + ">"); MPI.COMM_WORLD.Send(message1, 0, message1.length, MPI.CHAR, i, TAG); /* void Send(java.lang.Object buf, int offset, int count, * Datatype datatype, int dest, int tag) * Blocking send operation. */ } 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); System.out.println("process <0> received: " + new String(buf2)); /* Status Recv(java.lang.Object buf, int offset, int count, * Datatype datatype, int source, int tag) * Blocking receive operation. */ } } else { /* receive from rank 0: */ 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) + "!"); /* send to rank 0 */ System.out.println(" process <"+ myrank +"> sends message back to <0>"); MPI.COMM_WORLD.Send(message2, 0, message2.length, MPI.CHAR, 0, TAG); } MPI.Finalize(); } } O programa acima foi projetado para funcionar com N processos. O processo 0 envia mensagens aos demais N-1 processos e depois aguarda que os mesmos retornem uma resposta. Os outros N-1 processos recebem as mensagens do processo 0 e em seguida enviam uma outra mensagem para o mesmo. Todas as chamadas de envio e recebimento são síncronas. A seguinte saída é obtida quando executada com N=4. 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> I am process <0> of total <4> processes. process <0> sends message to: <1> I am process <2> of total <4> processes. process <2> waits to receive from <0> I am process <1> of total <4> processes. process <1> waits to receive from <0> process <0> sends message to: <2> 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 4. 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 e também fica esperando. Uma maneira de evitar isso é fazer com que um 6 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. 5. Starvation e Fairness (Justiça) 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. 6. Modos de comunicação e operações não bloqueantes Os exemplos de operações de send mostrados são bloqueantes, ou seja, o programa não executa a próxima linha se a mensagem não foi armazenada completamente em um buffer, esse buffer pode ser o receive buffer ou um buffer temporário. Ou seja, o programa poderia continuar a execução assim que a mensagem foi armazenada em um buffer de saída. O padrão MPI estabelece 4 tipos de comunicação para cada send/receive bloqueante, são eles: standard, buffered, synchronous e ready. Na comunicação standard fica à critério da 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. O fato de essa operação poder depender de um comando receive correspondente torna a operação send standard não local. A comunicação buffered é aquela em que a mensagem é copiada para um buffer temporário e não espera o comando receive correspondente para continuar a execução, por isso a operação é dita local. Uma operação send synchronous é aquela que só termina quando o comando receive correspondente é alcançado e a mensagem começou a ser recebida (i.e., a operação de receive começou a receber a mensagem). Essa operação é útil para indicar que o receptor alcançou um determinado ponto de execução (que já começou a receber a mensagem). Por depender do receptor alcançar um determinado estado a operação é dita não local. Um send ready é aquele que espera o comando receive correspondente ser alcançado para poder iniciar. O send ready possui semântica igual a operação de send standard e send synchronous. As operações para buffered, synchronous e ready são indicadas por um prefixo B, S ou R na frente da assinatura da função, exemplo: MPI_BSEND, MPI_SSEND, MPI_RSEND. Alternativamente à execução bloqueante, existea não bloqueante que começa a operação (por exemplo, pode começar a copiar para o buffer), mas não termina e 7 prossegue com sua execução. O propósito de permitir operações não bloqueantes é otimizar a execução de programas. No entanto se faz necessário a percepção de chamada iniciada (send_start) e chamada completada (send_complete). Está claro que as chamadas não bloqueantes operações de send podem iniciar sem dependência alguma, mas para serem completadas podem depender do estado do receptor e portanto podem ser não locais. A única chamada local não bloqueante é o send buffered, que não depende de outro sinal para completar, ou seja, a chamada inicia e completa localmente. A tabela abaixo resume as características de cada tipo de operação. Tipo de operação Variante Local ou não local Comando MPI Bloqueante Standard Não local MPI_(SEND | RECV) Bloqueante Buffered Local MPI_BSEND Bloqueante Synchronous Não local MPI_SSEND Bloqueante Ready Não local MPI_RSEND Não bloqueante Standard Local ou Não local *(depende do buffer) MPI_I(SEND | RECV) Não bloqueante Buffered Local MPI_IBSEND Não bloqueante Synchronous (send start) Local (send complete) Não local MPI_ISSEND Não bloqueante Ready (send start) Local (send complete) Não local MPI_IRSEND O padrão MPI estabelece o prefixo I (imediato) juntamente com as variantes B (buffered), S (synchronous) ou R (ready) como assinatura das funções, exemplo: MPI_ISEND(standard), MPI_IBSEND, MPI_ISSEND, MPI_IRSEND. 7. Comunicação Coletiva É possível definir grupos e fazer com que esses grupos troquem informação entre si, ou estabelecer como a comunicação será feita dentro do grupo. A figura abaixo ilustra os tipos de comunicação coletiva, cada linha representa um processo, e cada coluna representam os dados. 8 Na broadcast, um dado é replicado para os demais processos do grupo. No scatter, ocorre a distribuição dos dados, a centralização ocorre no gather. O allgather centraliza a informação em cada um dos processos, e o complete exchange realiza uma espécie de transposição de dados. 8. Topologia Virtual de Processos O MPI faz uma distinção clara entre topologia física e virtual. O padrão não especifica como os processos são mapeados fisicamente, no entanto em diversas situações é conveniente atrelar uma topologia específica para facilitar sua referência. (como por exemplo a cartesiana, para um programa que faz operações recorrentes com processos vizinhos). Topologias são atributos opcionais dos intra-comunicadores. Os tipos suportados de topologia são: Cartesiana, grafal e grafal distribuída. 9. Entrada e Saída (E/S) paralela A versão 1.1 o MPI não suportava escrita paralela, e a maneira mais comum de escrever um programa que fizesse entrada e saída de dados era deixar um processo único tomando conta do arquivo e os demais fazendo computação e fornecendo informação ao processo que manuseia o arquivo. 9 A seguir vamos demonstrar como é possível utilizar o MPI e paralelizar essa entrada e saída de dados. Figura 2. Diagrama de uma escrita sendo manuseada por 1 processo. #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; } No exemplo acima o processo 0 é o responsável por manusear o arquivo, os demais somente fazem computação e devolvem ao processo 0. É possível fazer um incremento neste esquema, fazendo com que os arquivos sejam escritos de forma independente por cada um dos processos. Ou seja, cada processo abre, escreve e fecha o arquivo. As principais desvantagens dessa abordagem são: a) os arquivos precisam ser juntados para utilização futura por um outro processo, b) se uma aplicação for escrita de forma paralela, ela vai necessariamente paralelizar até no máximo o número de arquivos produzidos, c) é difícil manusear e trafegar na rede vários arquivos, assim como manter lógica deles. 10 Figura 3. Escrevendo 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; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &myrank); for (i=0; i<BUFSIZE; i++) buf[i] = myrank * BUFSIZE + i; sprintf(filename, "testfile.%d", myrank); MPI_File_open(MPI_COMM_SELF, filename, MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, &myfile); MPI_File_write(myfile, buf, BUFSIZE, MPI_INT, MPI_STATUS_IGNORE); MPI_File_close(&myfile); MPI_Finalize(); return 0; } Similar a manipulação de arquivos do Unix, o MPI também precisa abrir, escrever e fechar um arquivo. Como cada processo utiliza seu próprio arquivo a api MPI_File_open é utilizada. O primeiro argumento MPI_COMM_SELF estabelece o comunicador entre os processos. O segundo argumento é o nome do arquivo. O terceiro parâmetro da chamada é o MPI_MODE_WRONLY | MPI_MODE_CREATE, isso força a sobrescrita caso já exista um arquivo, ou cria um novo se não existe. O quarto argumento define um objeto nulo para o MPI_Info object (um objeto utilizado para entrada em outras chamadas MPI). O quinto argumento é o endereço da variável MPI_File. MPI_File_open retorna um código que pode ser comparado com MPI_SUCCESS. A tabela a seguir mostra a assinatura do método e seus parâmetros. MPI_FILE_OPEN (comm, filename, amode, info, fh) Parameter type (IN, OUT or INOUT) Parameter name desc IN comm Communicator (handle) IN filename Name of the file (String) IN amode Way to access the file (integer) IN info Info object OUT fh New file handle 11 Para escrever o arquivo, o programa utiliza a api MPI_FILE_WRITE, com parâmetros similares ao MPI_RECV. MPI_FILE_WRITE (fh, buf, count, datatype, status) Parameter type (IN, OUT or INOUT) Parameter name desc INOUT fh File handle IN buf Initial address of receive buffer IN count Number of elements of receive buffer(non-negative integer) IN datatype Datatype of each send buffer elemento (handle) OUT status Status object Finalmente, depois que o arquivo foi modificado, MPI_FILE_CLOSE é necessário para fechar o arquivo de forma segura. MPI_FILE_CLOSE (fh) Parameter type (IN, OUT or INOUT) Parameter name desc INOUT fh File handle É possível escrever em um único arquivo de modo que os processos possam colaborar na construção do arquivo, a figura abaixo ilustra a estratégia utilizada. Figura 4. Processos escrevendo em um único arquivo. #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; } 12 O programa acima é bem parecido com o anterior, mas a chamada de MPI_FILE_OPEN define MPI_COMM_WORLD, isto permite a colaboração entre os processos. Para permitir que os processos vejam segmentos diferentes dentro do arquivo utilizados a api MPI_FILE_SET_VIEW. MPI_FILE_SET_VIEW(fh, offset, etype, filetype, datarep, info) Parameter type (IN, OUT or INOUT) Parameter name desc INOUT fh File handle (MPI_File) IN offset Displacement/OffSet (integer) IN etype Elementary datatype (handle) IN filetype Filetype (handle) IN datarep Data representation (string) IN Info Info object (MPI_INFO object) O primeiro argumento fh é o MPI_File definido anteriormente, o segundo argumento é o offset aonde se iniciará a escrita [myrank * BUFSIZE * sizeof(int)], o terceiro argumento é o etype que define o tipo dos elementos manipulados, o quarto argumento é o filetype que pode ser um etype ou conjuntos derivados de etypes, o quinto argumento é o data representation que especifica o tipo de representação do arquivo, e finalmente o último argumento é um MPI_INFO object. Não é difícil imaginar um outro possível cenário agora ao invés de escrita, ocorre a leitura paralela, os exemplos são análogos, apenas substituindo a api MPI_FILE_WRITE por MPI_FILE_READ, a assinatura dos dois métodos são análogos, e também é necessário indicar aonde o processo deve iniciar sua escrita com MPI_FILE_SET_VIEW. A seguir um outro programa que escrito em C++ que faz a leitura de um arquivo com múltiplos processos. #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; } 13 10. Considerações e Conclusões O padrão MPI define um conjunto de operações padrão para operar com passagem de mensagem. Ao definir comportamento esperado, ele permite que diversas implementações em diversas linguagens sejam disponíveis (Fortran, C, C++, Java), assim como padronização de diversos produtos comerciais como (Scali MPI Connect, WMPI II,SGI Message Passing Toolkit, Intel MPI, Sun MPI, HP-MPI, MPI/Pro). 11. Referências Bibliográficas [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. [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.
Compartilhar