Home   |   Structure   |   Research   |   Resources   |   Members   |   Training   |   Activities   |   Contact

EN | PT

BrBRCEEn0101-74382002000100007

BrBRCEEn0101-74382002000100007

variedadeBr
ano2002
fonteScielo

O script do Java parece estar desligado, ou então houve um erro de comunicação. Ligue o script do Java para mais opções de representação.

MPI: uma ferramenta para implementação paralela

1.     Introdução Muitos problemas interessantes de otimização não podem ser resolvidos de forma exata, utilizando a computação convencional (seqüencial) dentro de um tempo razoável, inviabilizando sua utilização em muitas aplicações reais na engenharia e na indústria. Embora os computadores estejam cada vez mais velozes, existem limites físicos e a velocidade dos circuitos não pode continuar melhorando indefinidamente. Por outro lado nos últimos anos tem-se observado uma crescente aceitação e uso de implementações paralelas nas aplicações de alto desempenho como também nas de propósito geral, motivados pelo surgimento de novas arquiteturas que integram dezenas de processadores rápidos e de baixo custo. Essas arquiteturas compõem ambientes com memória distribuída como, por exemplo, estações de trabalho ou PCs conectados em rede.

Segundo Tanenbaum (1999), os computadores paralelos podem ser divididos em duas categorias principais SIMD (Simple Instruction Multiple Data) e MIMD (Multiple Instructions Multiple Data). As máquinas SIMD executam uma instrução de cada vez, sobre diversos conjuntos de dados; nessa categoria estão as máquinas vetoriais e as matriciais. As máquinas MIMD rodam programas diferentes, em processadores diferentes, e podem ser divididas em multiprocessadores que compartilham a memória principal e os multicomputadores que não compartilham nenhuma memória. Os multicomputadores podem ser divididos em máquinas MPPs (Massive Parallel Processor) e COWs (Cluster of Workstations). Os MPPs são os supercomputadores que utilizam processadores padrão como o IBM RS/6000, a família Dec Alpha ou a linha Sun UltraSPARC. Uma outra característica dos MPPs é o uso de redes de interconexão proprietárias, de alta performance, baixa latência e banda passante alta.

Um COW é composto de algumas centenas de PCs ou estações de trabalho, ligados por intermédio de uma rede comercial. Este ambiente paralelo é uma realidade em empresas e universidades, tornando-se mais acessível que outros computadores paralelos de alto custo comercial. Deng & Korobka (2001) apresentaram um sistema COW chamado Galaxi, implementado sobre uma rede de alta velocidade (Fast e Gigabit Ethernet, com velocidades superiores a 100 MBPS). Nesse ano, a NTT Data Corp testará uma super-rede de computadores com a Intel, a Silicon Graphics (SGI), a Nippon Telegraph e a Telephone East Corp. (NTT East), envolvendo cerca de 1 milhão de PCs, com o intuito de criar um supercomputador virtual com capacidade de processamento cinco vezes maior que o mais rápido computador (IDGNow (2001)).

Além da rede de comunicação, é necessário uma camada de softwareque possa gerenciar o uso paralelo destas máquinas.Para tanto existem bibliotecas especializadas para tratamento da comunicação entre processos e a sincronização de processos concorrentes (Martins et al. (2002), Ignacio et al. (2000)). De uma forma geral, as bibliotecas são utilizadas sem maior dificuldade tanto nas máquinas MPP quanto nas máquinas COW, de maneira que as aplicações podem ser transferidas entre ambas plataformas. Os dois sistemas baseados na troca de mensagens mais usados em multicomputadores são o MPI (Message-Passing Interface) e PVM (Parallel Virtual Machine).O PVM é um sistema de mensagens de domínio público, projetado inicialmente para rodar em máquinas COW,  tendo diversas modificações implementadas para rodar em máquinas MPP. A seção de software de Pesquisa Operacional (vide Souza et al. (1998)) trouxe um artigo sobre o PVM (para maiores informações ver Geist et al. (1994)). Neste artigo é apresentado o MPI, que oferece mais recursos que o PVM, com mais opções e mais parâmetros por chamada, e que vem se tornando o padrão das implementações paralelas no MPP e no COW.

Um dos principais objetivos do desenvolvimento de aplicações paralelas é a redução do tempo computacional. Contudo não se deve buscar simplesmente otimizar uma simples medida de aceleração (razão entre o tempo do programa seqüencial e o tempo de execução da versão paralela). Devem ser também consideradas outras medidas como: eficiência e escalabilidade. A eficiência é usada para medir a qualidade de um algoritmo paralelo e é definido como a fração do tempo em que os processadores estão ativos (razão entre a aceleração e o número de processadores) caracterizando a utilização dos recursos computacionais, independentemente do tamanho do problema. A escalabilidade é uma medida de desempenho que indica a variação do tempo de execução e da aceleração, com o acréscimo do número de processadores e/ou tamanho do problema.

O presente trabalho apresenta as principais características de uma padronização de interface para troca de mensagens MPI, bem como as implementações existentes e suas aplicações como ferramenta de solução nos problemas da área de Pesquisa Operacional.

2. Message Passing Interface (MPI) Segundo Gropp et al. (1994), Foster (1995) e Pacheco (1999), o MPI é um padrão de interface para a troca de mensagens em máquinas paralelas com memória distribuída e não se devendo confundi-lo com um compilador ou um produto específico.

Antes de se mostrar as características básicas do MPI, é apresentado um breve histórico do surgimento do mesmo.

2.1  Histórico do MPI O MPI é o resultado do esforço de aproximadamente 60 pessoas, pertencentes a 40 instituições, principalmente dos Estados Unidos e Europa. A maioria dos fabricantes de computadores paralelos participou, de alguma forma, da elaboração do MPI, juntamente com pesquisadores de universidades, laboratórios e autoridades governamentais. O início do processo de padronização aconteceu no seminário sobre Padronização para Troca de Mensagens em ambiente de memória distribuída, realizado pelo Center for Research on Parallel Computing, em abril de 1992. Nesse seminário, as ferramentas básicas para uma padronização de troca de mensagens foram discutidas e foi estabelecido um grupo de trabalho para dar continuidade à padronização. O desenho preliminar, foi realizado por Dongarra, Hempel, Hey e Walker, em novembro 1992, sendo a versão revisada finalizada em fevereiro de 1993 (pode ser obtido em ftp://netlib2.cs.utk.edu/mpi/mpi1.ps).

Em novembro de 1992 foi decidido colocar o processo de padronização numa base mais formal, adotando-se o procedimento e a organização do HPF (the High Performance Fortran Forum). O projeto do MPI padrão foi apresentado na conferência Supercomputing93, realizada em novembro de 1993, do qual se originou a versão oficial do MPI (5 de maio de 1994). Essa  versão em postscript pode ser obtida na Netlib, enviando um e-mail a netlib@ornl.gov com a mensagem send mpi-report.ps from mpi (também pode ser obtida em ftp:// netlib2.cs.utk.edu/mpi/mpi-report.ps).

Ao final do encontro do MPI-1 (1994) foi decidido que se deveria esperar mais experiências práticas com o MPI. A sessão do Forum-MPIF de Supercomputing94 possibilitou a criação do MPI-2, que teve inicio em abril de 1995. No SuperComputing'96 foi apresentada a versão preliminar do MPI-2. Em abril de 1997 o documento MPI-2 foi unanimemente votado e aceito. Este documento está disponível via HTML em http://www.mpi-forum.org/docs/mpi-20-html/mpi2- report.html ou  em postscript em http://www.mpi-forum.org/docs/mpi-20.ps.

2.2  MPI No padrão MPI, uma aplicação é constituída por um ou mais processos que se comunicam, acionando-se funções para o envio e recebimento de mensagens entre os processos. Inicialmente, na maioria das implementações, um conjunto fixo de processos é criado. Porém, esses processos podem executar diferentes programas.

Por isso, o padrão MPI é algumas vezes referido como MPMD (multiple program multiple data).

Elementos importantes em implementações paralelas são a comunicação de dados entre processos paralelos e o balanceamento da carga. Dado o fato do número de processos no MPI ser normalmente fixo, neste texto é enfocado o mecanismo usado para comunicação de dados entre processos. Os processos podem usar mecanismos de comunicação ponto a ponto (operações para enviar mensagens de um determinado processo a outro). Um grupo de processos pode invocar operações coletivas (collective) de comunicação para executar operações globais. O MPI é capaz de suportar comunicação assíncrona e programação modular, através de mecanismos de comunicadores (communicator) que permitem ao usuário MPI definir módulos que encapsulem estruturas de comunicação interna.

Os algoritmos que criam um processo para cada processador podem ser implementados, diretamente, utilizando-se comunicação ponto a ponto ou coletivas. Os algoritmos que implementam a criação de tarefas dinâmicas ou que garantem a execução concorrente de muitas tarefas, num único processador, precisam de um refinamento nas implementações com o MPI.

MPI - Básico Embora o MPI seja um sistema complexo, um amplo conjunto de problemas pode ser resolvido usando-se apenas 6 funções, que servem basicamente para: iniciar, terminar, executar e identificar processos, enviando e recebendo mensagens.

Todos os procedimentos acima, exceto os dois primeiros, possuem um manipulador de comunicação como argumento.Esse manipulador identifica o grupo de processos e o contexto das operações que serão executadas.Os comunicadores proporcionam o mecanismo para identificar um subconjunto de processos, durante o desenvolvimento de programas modulares, assegurando que as mensagens, planejadas para diferentes propósitos, não sejam confundidas.

A seguir é apresentado um exemplo de um programa paralelo. Esse programa busca no espaço de soluções viáveis a melhor solução, segundo uma função objetivo. O ambiente paralelo tem 5 processos. O processo 0 é encarregado de controlar a busca. No inicio, ele faz uma amostragem do espaço de soluções viáveis para encontrar 4 diferentes soluções iniciais que são enviadas aos processos 1,2,3,4 utilizando-se o procedimento MPI_SEND. Esses processos, ao receber a mensagem, iniciam o processo de busca. Quando os processos de busca atingem seu critério de parada eles enviam o resultado ao processo 0, utilizando a função MPI_GATHER. O processo 0 então avalia a melhor solução encontrada para enviar essa nova solução a todos os outros processo utilizando, a função MPI_BCAST e se inicia um novo processo de busca, a partir de uma solução corrente melhor.

As funções MPI_INIT e MPI_FINALIZE são usadas, respectivamente, para iniciar e finalizar uma execução MPI. A MPI_INIT deve ser chamada antes de qualquer função MPI e deve ser acionada por cada processador. Depois de ser acionada a MPI_FINALIZE, não se pode acessar outras funções da biblioteca.

A função MPI_COMM_SIZE determina o número de processos da execução corrente e a MPI_COMM_RANK os identifica, usando um número inteiro. Os processos,pertencentes a um grupo, são identificados com um número inteiro precedido de um 0.As funções MPI_SEND e MPI_RECV,  são usadas para enviar e receber mensagens.

Antes de proceder com aspectos mais sofisticados do MPI, é importante assinalar que a programação de troca de mensagens não é determinística ou seja, a chegada das mensagens enviadas por dois processos A e B ao processo C não é definida.

Isto é, o MPI não garante que uma mensagem, enviada de um processo A e de um processo B, chegue na ordem que foi enviada. A responsabilidade disso depende do programador que deve assegurar a execução determinística, quando for solicitado.

A especificação da origem (source), no MPI_RECV, pode permitir o recebimento de mensagens vindas de um único processo específico, ou de qualquer processo.Esta segunda opção permite receber dados que tenha qualquer origem e isso algumas vezes é útil. Porém, a primeira opção é preferível porque as mensagens chegam na ordem do tempo em que foram enviadas.

Um mecanismo adicional para distinguir as mensagens é através do uso do parâmetro tag. O processo de envio deve associar uma tag (inteiro) a uma mensagem.O processo de recepção pode especificar que receber mensagens com um tagespecificoou com qualquer anytag.O uso a opção de um tag é preferível, devido à redução da possibilidade de erros.

Operações Globais Os algoritmos paralelos, freqüentemente, precisam de operações coordenadas envolvendo múltiplos processos. Por exemplo, todos os processos podem precisar transpor uma matriz distribuída ou somar um conjunto de números distribuídos em cada um dos processos. Claramente, as operações podem ser implementadas por um programador, utilizando-se funções de recebimento e envio de funções. Por comodidade e para permitir implementações otimizadas, o MPI também fornece uma serie de funções especializadas de comunicação que executam operações comuns desse tipo.

As funções são: *      Barreira (Barrier): sincroniza todos os processos.

*      Difusão (Broadcast): envia dado de um processo a todos os demais processos.

*      Juntar (Gather): junta dados de todos os processos em um único processador.

*      Espalhar (Scatters): distribui um conjunto de dados de um processo para todos os processos.

*      Operação de redução (Reduction operations): soma, multiplicação, etc., de dados distribuídos.

A função MPI_BARRIER( )é usada para sincronizar a execução de processos de grupo. Nenhum processo pode realizar qualquer instrução, até que todos os processos tenham passado por essa barreira. Essa é uma maneira simples de separar as duas etapas da execução para assegurar que as mensagens geradas não se misturem. Por exemplo, a função MPI_BARRIER pode ser inserida, antes de uma segunda operação de envio, para garantir um determinismo na execução. É evidente, nesse exemplo e em outros, que a necessidade de barreiras explícitas pode ser evitada pelo uso de tag, de origem e/ou contextos específicos.

As funções MPI_BCAST( ), MPI_GATHER( ), e MPI_SCATTER( ) são rotinas coletivas de movimentação de dados, nas quais todos os processos interagem com origens distintas para espalhar, juntar e distribuir os dados, respectivamente. Em cada caso, os primeiros três argumentos especificam o local e o tipo de dados a serem comunicados e o número de elementos a serem enviados para cada destino.

Outros argumentos especificam o local, o tipo de resultado e o número de elementos a serem recebidos de cada origem.

A função MPI_BCAST( ) implementa um processo de dispersão de dados do tipo um para todos, no qual um único processo origem envia um dado para todos os outros processos e cada processo recebe esse dado.

A função MPI_GATHER( ) implementa um processo de junção dos dados de todos os processos em um único processo. Todos os processos, incluindo a origem, enviam dados localizados na origem. Esse processo coloca os dados em locais contíguos e não opostos,com dados do processo "i", precedendo os dados do processo "i+l".

A função MPI_SCATTER( ) implementa uma operação de dispersão de um conjunto de dados para todos os processos; isto é, o reverso da MPI_GATHER. Note a sutil diferença entre essa função e a MPI_BCAST: enquanto na MPI_BCAST, todo processo recebe o mesmo valor do processo de origem, no MPI_SCATTER, todo processo recebe um valor diferente.

A MPI também fornece operações de redução. As funções MPI_REDUCE( ) e MPI_ALLREDUCE( ) implementam operações de redução. Eles combinam valores fornecidos no bufer de entrada de cada processo, usando operações especificas,e retorna o valor combinado, ou para o bufer de saída de um único processo da origem (MPI_REDUCE) ou para os bufer de saída de todos os processo (MPI_ALLREDUCE). Essas operações incluem o máximo, o mínimo (MPI_MAX, MPI_MIN), soma, produto (MPI_SUM, MPIPROD) e as operações lógicas.

Existem muitas outras funções MPI, que não foram descritas, neste texto que objetiva dar os princípios básicos de uma implementação. Algumas características não cobertas são: a possibilidade de comunicação assíncrona, quando um cálculo acessa elementos de uma estrutura de dados compartilhados em um modo não estruturado; a composição modular usada quando são implementados programas grandes de sistemas complexos onde módulos são implementados independentemente; o uso de ferramentas que ajudam a melhorar a performance dos algoritmos, investigando o ambiente dentro do qual estão sendo executados; o uso de diferentes tipos de dados, usados por exemplo para fazer a conversão entre diferentes representações de dados em ambientes heterogêneos.

Também é importante assinalar as diferenças do MPI para o MPI-2, dada pela incorporação de gerenciamento dinâmico de processos e na manipulação de Threads, o que sempre foi um problema para as bibliotecas de troca de mensagens. No MPI-2 tem-se a possibilidade de restringir o acesso à comunicação de algumas Threads, como por exemplo, apenas uma Thread (a principal) pode executar as funções do MPI.

Compilação e Execução Uma vez escritos os programas usando as funções do MPI é necessário compilá-los e executá-los. Os métodos de compilação e execução dependem da implementação utilizada, mas, de forma bastante genérica, os principais passos para compilar e executar um programa em MPI são: 1)Utilizar compiladores que reconheçam automaticamente os procedimentos MPI, ou incluir manualmente as bibliotecas MPI no processo de compilação; 2) Verificar quais são os nós disponíveis no ambiente paralelo; 3) Fixar os parâmetros para o ambiente paralelo: por exemplo, escolher os nós a serem usados e o protocolo de comunicação; 4) Executar o programa (que deverá ser executado em cada um dos nós escolhidos).

3.  Implementações de MPI Muitas implementações de MPI foram desenvolvidas. Todas elas foram implementadas sobre uma máquina virtual formada por muitos computadores heterogêneos (workstations, PCs, Multiprocessadores), em cada um dos quais se executa um processo, usando-se um controle de mensagem entre essas máquinas.

Entre essas implementações, o MPICH foi desenvolvido pela Argonne National Laboratory, utilizando-se MPI 1.1. padrão. Esta implementação está disponível e pode ser obtido no endereço http://www-unix.mcs.anl.gov/mpi/mpich. Esta ferramenta trabalha sobre as seguintes máquinas paralelas: IB SP-2, Intel Paragon, Gray T3D, Meiko CS-2, TC C-5, NCUBE NCUBE-2, Convex Exemplar, SGI Challenge, Sun Multiprocessors, CRAY YMP e CRAY C-90. Pode também ser executado sobre PCs (utilizando Linux, freeBSD ou Windows), Sun, HPO, SGI, IB RS/6000 e DEC workstations. MPICH/NT é uma outra implementação para Windows NT e está disponível pela Mississippi State University. Essa versão suporta comunicação com memória compartilhada entre estações e comunicação TCP/IP entre processos de múltiplas estações executando concorrentemente. Outra implementação disponível de MPI é o LAM que pode ser obtida no endereço http:// www.mpi.nd.edu/lam, baseada no MPI 2.0 padrão e desenvolvida pelo laboratório de Computação Cientifica da Universidade de Notre Dame. Essa implementação fornece algumas funções de monitoramento, as quais são muito úteis para administrar processos. Esse software tem sido testado e verificado sobre o Solaris, IRIX, AIX, Linux e HP-UX. O MPI/Pro e um software comercializado por MPI Software Technology que pode ser executados sobre Windows NT, TCP/IP, SMP e VIA. O PaTENT MPI 4.0 é um software comercializado pela empresa Genias para cluster de PC's (intel x86 Win32).

Outras companhias têm desenvolvido implementações específicas para máquinas paralelas, tais como Alpha, Cray, IB, SGI, DEC, e HP. O Cornell Theory Center fornece muitos tutoriais para o MPI, no endereço http://www.tc.cornell.edu/ UserDoc/SP/.

4.  Uso do MPI em Problemas de Pesquisa Operacional Existem muitas implementações paralelas de algoritmos exatos e metaheurísticas para se resolver problemas de otimização, utilizando-se o MPI. Algumas delas são apresentadas a seguir: Ribeiro et al. (2002) desenvolvem uma heurística GRASP paralela para o problema de projeto de redes de dois caminhos (2 - path network design problem) que consiste em encontrar dois caminhos entre pares de pontos origem-destino.

Aplicações desse tipo de problema podem ser encontradas em projetos de redes de computadores, nos quais os caminhos com poucos arcos são buscados a fim de forçar uma alta confiabilidade e maior rapidez. Os resultados computacionais mostram a eficiência do algoritmo paralelo e uma aceleração linear para até 32 processadores.

Sarma & Adeli (2001) apresentam uma heurística de algoritmos genéticos, na otimização de estruturas de aço da construção civil, sujeita às normas americanas de construção com aço. Utilizam dois esquemas inovadores na implementação paralela, com respeito à evolução dos indivíduos nos processadores e aos mecanismos de migração, cujos resultados computacionais apresentam acelerações eficientes.

Marco & Lanteri (2000) apresentam dois níveis de estratégia para a paralelização de um Algoritmo Genético, aplicados a problemas de desenho ótimo de formas que minimizem o choque aerodinâmico. Os algoritmos são executados no sistema SGI Origin 2000, IBM SP-2 e um cluster de PCs Pentium pro interconectados, através de uma rede FastEthernet de 100 Mbits/s.

Morales et al. (2000) desenvolvem três algoritmos paralelos para um problema simples de alocação, utilizando uma simples topologia de anel para os processadores, a fim de tornar o algoritmo portável. Os resultados computacionais mostram que o algoritmo fornece a solução ótima dos problemas.

Batoukov & Serevik (1999) apresentam um esquema geral para o algoritmo branch-and-bound, utilizando balanceamento de carga dinâmico. Na implementação utilizam a capacidade ociosa de uma rede de computadores para formar um supercomputador virtual. Os autores ressaltam a eficiência desse algoritmo pela baixa taxa de comunicação entre os processos e a estrutura de dados utilizada.

Resultados computacionais são apresentados para diversas instâncias do problema da mochila.

Marzetta et al. (1999) desenvolvem uma biblioteca orientada a objetos baseada no C++ e no MPI, para resolver diversos problemas de otimização combinatória como planejamento de vôos nas empresas de linha aéreas, considerando alocação da frota com janela de tempo o algoritmo heurístico proposto para este problema encontra a solução com uma qualidade de 5.8% (limite superior ' limite inferior/limite inferior * 100%).

Martins et al. (1998, 2000) desenvolvem uma implementação paralela de GRASP para resolver problemas de Steiner em grafos. O balanceamento de carga é usado para maximizar o uso das capacidades dos processadores, num entorno heterogêneo de clusters de estações, e os resultados computacionais mostram que, de 40 problemas testados, a heurística paralela encontra a solução ótima em 33 deles.

Homer (1997) desenvolve e implementa um algoritmo paralelo para o problema de corte de peso máximo em um grafo não direcionado. A implementação é iniciada com o algoritmo serial de Goemans e Williamson, a partir do qual diferentes versões são implementadas, variando-se a parte de pontos-interiores do algoritmo.

Eckstein et al. (1997) desenvolvem um conjunto de bibliotecas para a implementação paralela do algoritmo branch-and-bound.

Brüngger et al. (1997) mostram o uso da biblioteca para resolver algumas instâncias do problema quadrático de alocação.

Fachat & Hoffmann (1997) descrevem a implementação de conjunto de algoritmos de Simulated Annealingem paralelo e que utilizam balanceamento de carga dinâmico com o intuito de fazer uso da máxima potência de processamento.

5.  Conclusões Nos dias de hoje o conceito de máquinas paralelas formadas por cluster de PCs ou estações de trabalho (COW) é uma realidade e podem ser encontradas em qualquer instituição por menor que seja. As melhoras significativas no desempenho, na confiabilidade e na alta velocidade de transmissão as tornam uma alternativa barata para aplicações paralelas junto ao desenvolvimento de bibliotecas para trocas de mensagem como o MPI. O MPI tem se tornado um padrão de comunicação para troca de mensagens, permitindo que cada fabricante implemente, da melhor maneira possível, a exploração dos recursos específicos de sua arquitetura. Isso possibilita o desenvolvimento de ferramentas eficientes e portáveis para diversos sistemas comerciais e de domínio público como: Windows, NT, AIX, Linux freeBSD etc. Os sistemas COW e MPP, juntamente com o MPI, estão sendo usados para resolver, com êxito, problemas desafiadoras na área da Pesquisa Operacional que anteriormente não podiam ser tratados, mas se tornaram viáveis quando olhados do ponto de vista da programação paralela.

Independentemente da qualidade do algoritmo paralelo usado, alguns aspectos da implementação podem conduzir a resultados pobres, como por exemplo gargalos na plataforma paralela ou a escolha inadequada do ambiente paralelo COW. Também deve-se ter em conta que o ganho de uma implementação paralela em geral é mais notório em grandes instâncias de problemas difíceis, podendo até mesmo ser desfavorável em problemas "fáceis".


transferir texto