• Nenhum resultado encontrado

2 Arquitetura Paralela

N/A
N/A
Protected

Academic year: 2021

Share "2 Arquitetura Paralela"

Copied!
73
0
0

Texto

(1)

Resumo

Esta monografia tem como objetivo o desenvolvimento de uma ferramenta para auxiliar no desenvolvimento de programa que utilizam a biblioteca MPI. Esta ferramenta mostra ao usuário qual a carga de dados passada entre os processos MPI na rede do cluster, e também quais computadores estão realizando a troca de dados. É tomada como base para a medição da carga da rede o protocolo TCP/IP utilizando uma placa FastEhternet de 100Mbits por segundo. Este protocolo foi escolhido por ser o protocolo utilizado para a ligação das estações do cluster do Departamento de Informática da UFES. A biblioteca MPI utilizada foi a LAM-MPI 6.5.6.

(2)

1 Introdução

As grandes preocupações na implementação de um programa que será executado em um cluster de computadores são: garantir que nenhum nó ficará sobrecarregado, executando uma tarefa maior que os outros nós do cluster, e garantir que a comunicação entre os computadores não causará uma perda de desempenho, ocorrida devido a algum gargalo na rede. Um das soluções para esse problema é o acompanhamento do andamento do programa durante a sua execução. Uma informação muito importante para o usuário é o tamanho dos dados passados entre os computadores do cluster. Se essa informação fosse passada ao usuário, seria instantânea a detecção de que um determinado nó do cluster está sobrecarregado, pois se ele estiver recebendo muito mais dados a serem processados que os outros nós do cluster estará realizando um processamento muito maior, ou se receber uma quantidade muito pequena de dados estará realizando um processamento muito menor.

Neste contexto, o objetivo deste trabalho é o desenvolvimento de uma ferramenta capaz de proporcionar um ambiente no qual o usuário poderá visualizar as chamadas das funções da biblioteca MPI para troca de dados entre os nós do cluster. O usuário irá visualizar o momento em que um nó no cluster está passando algum dado a outro computador do cluster, e qual o tamanho desse dado em relação a capacidade da rede. Esta informação é tomada com base na capacidade da rede, para ser possível ao programador identificar qual a utilização da rede para a comunicação entre os computadores do cluster, caso algum computador esteja utilizando mais a rede que os outros, isto significa que ele está recebendo mais dados que os demais. Com estas informações o programador poderá otimizar o desempenho do programa para que todos os computadores possam realizar um processamento uniforme, não havendo sobrecarga nem “falta de carga” em alguma máquina do cluster.

Esta monografia irá descrever a criação e funcionamento da ferramenta MPI_SPY nos próximos capítulos. No capítulo 2 são mostrados o modelo de cluster beowulf, e algumas ferramentas adicionais. Este modelo foi escolhido por ser o modelo utilizado na construção do cluster do Departamento de Informática da UFES. O capítulo 3 apresenta a biblioteca MPI, como é o seu funcionamento e algumas de suas funções. Também são mostradas neste capítulo quais dados referentes a biblioteca MPI poderão ser aproveitados pelo usuário para fazer o acompanhamento do programa no cluster. No capítulo 4 é mostrado o funcionamento do MPI_SPY, quais alterações foram feitas na biblioteca MPI, e como essas alterações se comportarão. No capítulo 5 é apresentado como foi feita a implementação do MPI_SPY para que ele pudesse funcionar em conjunto com a biblioteca MPI. O capítulo 6 mostra as conclusões obtidas e propostas de trabalhos futuros.

(3)

2 Arquitetura Paralela

Máquinas para computação paralela estão se tornando populares nos dias atuais, devido a crescente necessidade de maior capacidade computacional. Mas algumas destas máquinas têm um custo muito elevado, ou são difíceis de programar [1].

2.1 Modelos de Arquitetura

A tabela 2.1 abaixo ilustra a classificação de Flyn para máquinas paralelas:

SD (Single Data) MD (Multiple Data)

SISD SIMD

SI (Single Instruction) Máquinas VonNeumman convencionais

Máquinas Vetoriais (CM-2, MasPar)

MISD MIMD

MI (Multiple Instruction) Não existe atualmente Multiprocessadores e multicomputadores (nCube, Intel Paragon) Tabela 2.1: Classificação de Flyn

A classe SISD (Single Instruction Single Data), representa as classes das máquinas convencionais, existe apenas um único fluxo de dados que é manipulado por um único fluxo de instruções [1].

A classe MISD (Multiple Instruction Single Data) assume um múltiplo fluxo de instruções sobre um único fluxo de dados, o que na prática quer dizer que várias instruções manipulam um mesmo dado, o que não acontece em qualquer máquina projetada até hoje.

A classe SIMD (Single Instruction Multiple Data) corresponde à classe de equipamentos com processamento vetorial, onde um conjunto de processadores irá processar uma massa de dados. Neste caso cada processador executa a mesma instrução, mas operam sobre partes diferentes da massa de dados.

A classe MIMD (Multiple Instruction Multiple Data) abrange o grupo de máquinas que podem executar diferentes instrução em diferentes conjuntos de dados ao mesmo tempo. Dessa forma, qualquer grupo de máquinas, executando um processamento distribuído, pode ser considerado uma máquina MIMD. Um tipo de máquina que vem tendo uma maior participação no grupo de máquinas paralelas é o cluster, ou agregado, de computadores.

(4)

2.2 Cluster de Computadores

Um Cluster é constituído por várias estações de trabalho interligadas, projetadas com o objetivo de executar aplicações paralelas [1]. Sendo assim, cada máquina que compõem o cluster deverá ser modificada para esse fim, recebendo uma configuração adequada. São retirados o monitor, o teclado e mouse, mas as principais modificações são realizadas no software que será instalado em cada computador. O sistema operacional é reduzido, contendo apenas um conjunto básico de programas para poder ativar o computador no cluster.

Outro ponto que merece atenção em um cluster é a rede de conexão. Dependendo do tipo de cluster que se está construindo, pode-se ter um tipo de rede. Quando o mais importante é o poder de processamento do cluster, pode-se fazer a opção por uma rede padrão, por exemplo Ethernet, que não pesará no custo final do cluster, podendo ter uma considerável quantidade de máquinas, superior a 1000. Se for dada uma maior importância na comunicação entre os computadores, deverá ser utilizada uma rede de baixa latência, oferecida por empresas que fornecem placas de conexão de alto desempenho. Neste caso, o custo da rede será elevado, e o cluster não contará com uma grande quantidade de máquinas.

As vantagens na utilização de um cluster de computadores em relação a uma única máquina com vários processadores são:

• Custo x Benefício: apresenta uma ótima relação MFlops/$ em comparação à uma máquina com igual quantidade de processadores. • Configuração: por se tratar de uma arquitetura aberta, formada por estações de trabalho, a definição de seus componentes ( computadores e placas de rede ) possibilitam várias configurações. • Manutenção: os componentes necessários a sua manutenção são

facilmente encontrados, pois em sua construção são utilizados “produtos de prateleira”[1]

2.3 Cluster Beowulf

Um modelo de cluster que vem sendo utilizado hoje em dia é o Cluster Beowulf, pois esta máquina além de ser formada por equipamentos de baixo custo utiliza versões do Unix e programas de domínio público [4]. O princípio é: “Nenhuma máquina paralela comercial pode competir com um Beowulf, em relação ao custo.”

O cluster é formado por uma rede Ethernet e estações constituídos por PCs padrão. A quantidade de máquinas na rede, incluindo computador e placa de rede, irá definir o preço final do cluster, pois como foi dito, o software instalado nas máquinas é gratuito.

(5)

Figura 2.1: Cluster Beowulf

Na figura 2.1 pode ser visto um cluster formado por 16 máquinas.

O padrão Beowulf define a configuração mínima de um cluster. Pode-se partir de um cluster Beowulf para uma configuração mais segura, ou mais otimizada, dependendo da necessidade que se tem.

2.4 Configuração de um Cluster

No caso de se ter um número reduzido de computadores não há nenhuma necessidade maior que não possa ser sanada com um mínimo de recursos. Mas quando se possui um cluster com uma quantidade maior de máquinas, ou que será utilizado intensamente, outros equipamentos devem ser levados em consideração.

2.4.1 Monitor de Funcionamento

Este equipamento é utilizado para verificar o funcionamento de cada máquina no cluster, e do cluster como um todo. Caso alguma máquina falhe, seja danificada fisicamente e deixe de funcionar, o Monitor deverá informar qual máquina está danificada, para que o Gerenciador de Processos não venha alocar algum programa na máquina danificada.

(6)

2.4.2 Controlador de Temperatura

Pelo fato de um cluster reunir um conjunto de computadores, que irão executar uma carga de processamento que os leve ao máximo de sua capacidade, alguns cuidados devem ser levados em relação a temperatura das máquinas, e do cluster. O controlador de temperatura é um equipamento que vem embutido em todos os processadores hoje em dia, e que ao entrar em funcionamento ( a temperatura atingiu o ponto crítico ) irá desligar o computador. Como os processadores dos computadores em m cluster estarão operando em capacidade máxima, a temperatura interna da CPU irá se elevar rapidamente, atingindo o ponto crítico e dependendo do caso, queimar a CPU antes de ativar o controle interno. O Controlador de Temperatura do cluster irá atuar como um mecanismo de resfriamento do cluster, assim que for detectado um aumento de temperatura, já que muitos computadores fazem parte do cluster, ou até mesmo desligando o cluster caso a temperatura não abaixe.

2.4.3 Gerenciador de Processos

Esta máquina ficará responsável em distribuir os processos no cluster, de tal forma que nenhuma máquina fique sobrecarregada em relação às outras. Este equipamento é muito importante, pois em determinados momentos pode-se ter uma série de pequenos programas pode-sendo executados no cluster, que pode-se não forem bem distribuídos podem ter sua execução, e a dos outros, prejudicada. Um exemplo de programa que pode ser utilizado para realizar esta tarefa é o SGE, Sun Grid Engine.

O SGE é um ambiente para a execução remota de programas UNIX em um grupo de computadores que operam em um cluster [9]. Os programas são enfileirados e executados remotamente nos computadores no momento em que esses computadores estão disponíveis, se não houver um número suficiente de computadores disponíveis para executar o programa, ele ficará esperando até que a quantidade de computadores necessária para o programa esteja disponível. O número de computadores utilizados no cluster é igual ao número de computadores que foram requeridos pelo programa, os outros computadores do cluster que não forem utilizados estarão disponíveis para um outro programa.

Os comandos utilizados para trabalhar com o SGE são:

• qacct: extrai informações de controle a partir do arquivo de log do cluster;

• qalter: modifica as características do programa submetido;

• qconf: possibilita ao usuário a configuração, modificação, deleção e consulta as filas de programas e a configuração do cluster; • qdel: este comando possibilita um meio para o usuário ou

administrador do cluster cancelar algum programa;

(7)

• qhost: exibe as informações sobre o estado de execução dos computadores no cluster;

• qlogin: inicia um terminal telnet ou uma sessão de login similar; • qmake: é um comando similar ao make do UNIX. Este comando

adiciona ao make a possibilidade de distribuir os passos pelos computadores do cluster;

• qmod: permite ao proprietário de uma fila suspender e ativar todas as filas associadas a uma máquina ( todos os programas ativos associados a máquina são sinalizados ), ou suspender e ativar todos os programas em execução daquela fila;

• qmon: este comando é uma interface para os comandos Motif para todas as funções do SGE;

• qresub: cria um novo programa através de uma cópia do processo atual em execução ou de algum processo na fila;

• qrls: libera os programas que foram guardados pelo qhold;

• qrsh: este comando pode ser utilizado para vários propósitos, como providenciar a execução remota de aplicações a partir do SGE, comparável ao comando rsh do UNIX;

• qselect: exibe uma lista com os nomes das filas que obedecem a um critério específico;

• qsh: abre um terminal de comando em um computador do cluster, similar ao xterm. Qualquer tipo de programa pode ser executado neste terminal;

• qstat: exibe uma lista com todos os programas e filas associadas ao cluster;

• qtcsh: este comando cria um ambiente totalmente compatível com o conhecido tcsh. Ele providencia um terminal com as extensões de execução distribuída de aplicações;

• qsub: este comando é utilizado para se submeter um programa ao cluster pelo SGE.

(8)

2.4.4 Monitor de Rede

Este programa será responsável por realizar a medição da carga de operação da rede do cluster. Ele irá informar qual a taxa de utilização de rede, e quais os computadores que estão realizando alguma transferência de dados pela rede. Este acompanhamento deve ser feito de tal forma que não interfira na comunicação dos computadores, e ao mesmo tempo deve ser feito em tempo real, no instante em que houver algum tráfego de dados na rede.

Um dado relevante do monitor de rede é que ele irá informar qual a carga de dados total passada em um determinado instante entre dois computadores. Em um cluster de computadores que tem como objetivo realizar um processamento em uma base de dados, seria mais relevante se o monitor de rede informasse ao usuário apenas a carga de dados pertencente a massa que será utilizada para efetuar o processamento entre os computadores do cluster, sem incluir os dados referentes ao controle feito pelo protocolo de rede. Para que isso ocorresse o monitor de rede deveria fazer a separação entre os dados referentes a aplicação do usuário e os dados referentes ao gerenciamento efetuado pelo protocolo de rede. Isso pode ser feito de duas formas: através de um sistema que fosse capaz de capturar um bloco de dados e reconhecer a parte referente ao protocolo de rede e a parte referente aos dados do usuário e contabilizar apenas a parte de dados do usuário, ou através de um sistema que ao detectar um envio de dados feito pelo programa do usuário para a rede, capturasse as informações referentes a esses dados e as exibisse ao usuário. A segunda maneira, a principio parece ser impraticável, pois como o monitor de rede irá detectar o instante do envio de dados para a rede ? Para contornar esse problema seria necessário que a aplicação que estiver sendo executada no cluster informasse ao monitor de rede que irá fazer uso da rede antes de enviar dados para a rede. Mas isso também é, a princípio, uma tarefa complicada para ser feita pela aplicação do usuário, já que o objetivo da aplicação é realizar um processamento programado pelo usuário e não o de ser um auxiliar do monitor de rede. Como uma aplicação executada em um cluster deve utilizar uma biblioteca para realizar o processamento, a solução para esse problema pode ser uma modificação na biblioteca de processamento para que essa biblioteca possa interagir com o monitor de rede. Dessa forma, a aplicação do usuário irá continuar da mesma forma, não necessitando de alteração para que o monitor de rede possa trabalhar.

Esta é a proposta desta monografia, o desenvolvimento de uma ferramenta que irá trabalhar como um monitor de rede para uma aplicação MPI. O monitor de rede, na verdade, é dividido em duas partes principais: uma biblioteca para execução de programas no cluster, e um aplicativo de visualização da carga da rede.

Uma das bibliotecas utilizadas em aplicações paralelas é a biblioteca MPI. A biblioteca MPI é um padrão para passagem de mensagens em ambientes com computadores distribuídos interligados por uma rede [10].

(9)

Desta forma a biblioteca MPI proporciona um ambiente onde toda a rede de computadores passa a funcionar como um supercomputador. Mas MPI não é um ambiente completo de computação paralela. Algumas características, como gerenciamento dos processos e depuração do programa, não estão presentes no MPI. O que a biblioteca MPI garante é um ambiente eficiente e seguro para troca de dados entre os computadores em um cluster.

Seria útil se a biblioteca MPI permitisse a visualização dos dados que são passados entre os processos no cluster, através de um monitor de rede que estivesse conectado ao cluster. Esta visualização permitiria ao programador acompanhar a execução do programa e identificar pontos que contenham algum tipo de anormalidade. Um depurador poderá realizar esta tarefa para o usuário, pois um depurador é uma ferramenta utilizada para localizar erros lógicos em um programa em execução, esses erros são conhecidos como bugs. Apesar de o programa ter sido compilado e linkado corretamente, ele não é executado com sucesso. Por exemplo, o programa retorna uma saída incorreta, ou entra em um loop infinito, ou termina de forma prematura. [2]

Para que a biblioteca MPI possa enviar os dados referentes as mensagens passadas no programa do usuário, deve-se alterar o modo de execução das instruções desta biblioteca. As instruções, ao serem executadas, devem também enviar ao visualizador os dados referentes a mensagem que elas estão manipulando. No capítulo 3, onde é mostrada a biblioteca MPI serão discutidos quais dados podem ser passados para o visualizador de acordo com a instrução que está sendo executada.

(10)

3 A Biblioteca MPI

Será mostrado neste capítulo o funcionamento da biblioteca MPI, algumas de suas funções, e como essas funções operam. Também será mostrado quais funções poderiam ser alteradas, e que tipo de alteração poderia ser feito, para que a biblioteca MPI possa ser utilizada para operar em conjunto com um monitor de rede.

3.1 A Arquitetura do Sistema MPI

Esta seção discute como é a arquitetura de execução de um programa MPI. Esta arquitetura consiste de um programa MPI sendo executado em um cluster de máquinas, cada uma com sua própria memória local. A figura (3.1) demonstra a organização da arquitetura de execução MPI:

Figura 3.1: Organização da arquitetura de execução MPI

• Programa MPI do Usuário: Nesta camada será feita a execução do programa MPI feito pelo usuário;

• Biblioteca MPI: Esta camada é responsável pelo gerenciamento das mensagens trocadas pelos computadores do cluster;

• Protocolo de Rede: Esta camada fornece um ambiente de comunicação confiável entre as máquinas do cluster;

• Rede Física: É nesta camada que a troca de mensagens se efetuará. Ela será responsável pelo envio dos dados entre os computadores do cluster.

Durante a execução de um programa MPI feito pelo usuário, este programa irá realizar uma série de trocas de mensagens entre os computadores do cluster. Estas trocas de mensagens são administradas pela biblioteca MPI, que irá, através do protocolo de rede, realizar a comunicação

(11)

entre os vários computadores em um cluster. Por sua vez, o protocolo de rede irá interagir com a rede física para que a comunicação entre os computadores possa ser efetivada.[5]

A dependência entre as camadas ocorre devido ao fato de que as camadas superiores não possuem os meios para realizar a tarefa da camada imediatamente inferior por si só. O programa MPI feito pelo usuário, não poderia efetuar a troca de mensagens entre os computadores do cluster se não fosse a biblioteca MPI. E a biblioteca MPI da mesma forma depende da camada de protocolo de rede, pois não pode identificar os computadores que compõem o cluster. E por sua vez a camada de protocolo de rede ficaria impossibilitada de completar uma transferência de dados se esta não fosse realizada pela rede física.

3.2 Funcionamento do MPI

A comunicação realizada entre os processos MPI é baseada em três conceitos básicos: grupos, contextos e comunicadores.

3.2.1 Grupos de Processos

Um processo é um programa em execução. A biblioteca MPI proporciona um modo de execução em que um mesmo programa é executado em diversas máquinas, em paralelo. Os programas distribuídos em um cluster, apesar de essencialmente serem apenas um programa, são executados de forma independente em cada máquina do cluster, ou iniciados como processos independentes quando se executa o programa em apenas uma máquina, como mostra a figura 3.2, e sincronizados através das funções oferecidas pela biblioteca MPI.

(12)

Um grupo é uma coleção ordenada de processos, onde cada processo é identificado por um rank. Para um grupo de N processos, o conjunto de ranks vai de 0 à N-1.

O grupo de processos pode ser utilizado sob dois aspectos: comunicação e paralelismo. No que diz respeito à comunicação, faz-se uso do grupo para podermos especificar quais processos estarão envolvidos em uma determinada troca de dados. E em relação ao paralelismo, pode ser especificado no programa qual grupo irá executar uma determinada seqüência do programa feito pelo usuário. Desta forma obtem-se um verdadeiro ambiente MIMD, Múltiplas Instruções e Múltiplos Dados.

A especificação da biblioteca MPI adota um modelo estático de processo [6], sendo que no momento em que a aplicação é iniciada, um número fixo de processos será designado, desde o início até o fim. Não haverá diminuição nem aumento do número de processos durante a execução do programa no cluster.

3.2.2 Contexto da Comunicação

O Contexto da Comunicação foi implementado para se possibilitar a criação de um ambiente distinto e separado para passagem de dados entre os processos, sendo que cada aplicação terá um único contexto [10]. Um exemplo típico que sugere o uso de contexto é a necessidade de se garantir que uma mensagem passada em um determinado momento da aplicação, não seja incorretamente interceptada em outro momento da aplicação. A principal questão aqui é garantir que uma determinada mensagem possa trafegar corretamente pelo cluster, sem haver perda em seu conteúdo. Uma aplicação MPI executada em um cluster possuirá o seu próprio contexto de comunicação, um ambiente fechado para realizar a troca de dados entre os processos desta aplicação, não interferindo em nenhum momento com uma outra aplicação MPI que possa estar sendo executada no mesmo cluster.

(13)

Figura 3.3: Contexto de uma aplicação MPI

A figura 3.3 mostra como é a separação feita pela biblioteca MPI para diferenciar duas aplicações MPI rodando em um mesmo cluster.

3.2.3 Comunicador

Na biblioteca MPI são definidos objetos denominados comunicadores, que irão definir o escopo de comunicação entre os processos MPI [10]. Os comunicadores serão utilizados dentro do contexto de um grupo. O escopo de uma operação de comunicação é definido pelo contexto de comunicação utilizado, e o grupo envolvido. Em uma comunicação ponto-a-ponto a especificação da origem e do destino da mensagem é feita pelo rank dos processos envolvidos nesta tarefa. Quando se utiliza mais de um grupo de processos na aplicação, tanto o rank dos processos quanto os grupos necessitam ser especificado.

A biblioteca MPI automaticamente define um comunicador padrão denominado MPI_COMM_WORLD. Este é o comunicador referente a todos os processos [8]. Utilizando este comunicador, qualquer par de processos pode realizar uma comunicação. O usuário pode definir novos comunicadores como sendo um subconjunto dentro do MPI_COMM_WORLD.

Para que haja uma execução correta do programa no cluster, isto é, para que ao final da execução de todos os programas no cluster, o resultado obtido seja correspondente ao esperado, assumindo que o programa foi escrito corretamente, as três propriedades descritas anteriormente devem estar trabalhando de forma integrada.

(14)

3.3 Utilização da Biblioteca MPI

A biblioteca MPI ao iniciar um programa em um cluster, inicia uma cópia do programa em cada máquina do cluster necessária para a execução do programa. Para tal, o programa deve ser passado ao executor de programas da biblioteca MPI, que pode ser denominado mpirun ou mpiprun [8], dependendo do desenvolvedor da biblioteca. Ambos os nomes estão de acordo com o padrão definido para a biblioteca MPI. Basta digitar o seguinte comando:

$ mpirun –np 8 a.out

Neste caso, serão iniciados 8 cópias do programa a.out em no máximo 8 computadores do cluster. Caso hajam menos que 8 computadores no cluster, haverá uma distribuição uniforme das cópias do programa a.out entre os computadores.

Ao serem iniciadas as 8 cópias do programa, cada uma não é capaz de se comunicar com qualquer outra cópia do programa. Isto ocorre porque não houve uma organização das cópias do programa que estão em execução no cluster. Para que haja uma organização das cópias do programa, de tal forma que possa existir uma comunicação confiável, são necessárias algumas chamadas a funções da biblioteca MPI. Estas funções são descritas nos tópicos a seguir.

3.3.1 Inicialização

A primeira instrução a ser executada em um programa MPI deve ser MPI_Init(). [8] Os argumentos passados para esta instrução são:

MPI_Init( int *argc, char **argv);

Após executar esta instrução todos os processos MPI no cluster têm seu rank definido. Seria útil ao programador ter nesse ponto alguma informação sobre a inicialização dos processos no cluster, saber, por exemplo, qual computador está executando um determinado processo. Desta forma ele teria um melhor controle sobre o programa.

3.3.2 Leitura da quantidade de processos e a identificação de cada processo no cluster

Duas instruções são fundamentais para uma boa utilização de uma aplicação paralela, e são: a quantidade total de processos em execução e a identificação de cada processo no cluster. Estas informações são obtidas a partir do comunicador global MPI_COMM_WORLD [6], utilizando-se a função MPI_Comm_size() e também a função MPI_Comm_rank(). Os argumentos para estas funções são:

(15)

MPI_Comm_rank( MPI_COMM_WORLD, int *rank );

Após a execução destas instruções têm-se os valores da quantidade total de processos e da identificação de cada processo nas variáveis quant_procs e rank, respectivamente. É importante observar que cada processo irá executar estas duas instruções, e em todos eles a variável quant_procs possuirá o mesmo valor, mas a variável rank será diferente para cada um, pois ela irá armazenar o valor do identificador de cada processo, que é específico para cada um.

3.3.3 Finalização

Para finalizar a execução de um processo MPI deve-se utilizar o comando MPI_Finalize(). Não existem parâmetros a serem passados a essa instrução.

A finalização de uma aplicação MPI é um processo que não ocorre no instante da chamada da instrução MPI_Finalize, pois a biblioteca MPI deve garantir que a aplicação termine corretamente, isto é, deve ser garantido que não existem pendências a serem atendidas pela biblioteca MPI. Para garantir isso é feita a seguinte verificação para o processo que executou a instrução MPI_Finalize:

• Verificar se existe mensagem no buffer a ser enviada para algum outro processo

o Caso existe alguma mensagem a ser enviada, enviar a mensagem antes de finalizar o processo MPI.

Após a execução da instrução MPI_Finalize, o rank dos processos no cluster é indefinido. Os processos só terminam de executar quando chegam ao final do código fonte. A depuração desta instrução também é interessante, para permitir ao usuário ter conhecimento de quais processos terminaram, e em que ordem.

3.3.4 Passagem de Mensagem

Um programa sendo executado em um cluster, através da biblioteca MPI, irá necessitar realizar uma série de troca de informações entre os computadores do cluster, para dividir o trabalho de processamento. Essa troca de informações é feita utilizando-se uma série de funções da biblioteca MPI que serão descritas nos tópicos a seguir.

3.3.4.1 Categorias de Funções

Existem duas categorias de funções para passagem de mensagem: funções bloqueantes e funções não bloqueantes.

(16)

• Funções Bloqueantes: A função não é concluída até o momento de sua finalização.

• Funções Não Bloqueantes: A função é finalizada imediatamente, apenas sendo iniciada a operação de transferência da mensagem, não esperando o seu término.

Figura 3.4: Passagem de Mensagem Bloqueante (a) e Passagem de Mensagem Não Bloqueante (b)

Representado na figura 3.4a está o exemplo de funcionamento de uma função bloqueante. Neste caso o processo 1 irá passar uma mensagem ao processo 2. A biblioteca MPI irá realizar esta passagem de mensagem mas só irá liberar o processo 1 quando o processo 2 tiver terminado de receber a mensagem. Na figura 3.4b a biblioteca libera o processo 1 instantaneamente, sem que o processo 2 tenha recebido ainda a mensagem.

3.3.4.2 Funções Bloqueantes na comunicação ponto a ponto entre processos

Esta seção irá descrever algumas das funções da biblioteca MPI que são bloqueantes na comunicação ponto a ponto entre processos:

• MPI_Send: Envia uma mensagem no modo padrão. A função termina quando o MPI coloca a mensagem no buffer local ou quando a mensagem é recebida por outro processo.

• MPI_Bsend: Envia a mensagem para o buffer de armazenamento local da aplicação, e finaliza a instrução. Caso a mensagem não caiba no buffer o resultado é indefinido. O usuário é responsável por alocar um buffer adequado para esta instrução, utilizando as funções MPI_Buffer_Attach e MPI_Buffer_Detach.[8]

• MPI_Ssend: Envia a mensagem em modo síncrono. A função termina quando for totalmente recebida por um outro processo. • MPI_Rsend: Envia a mensagem para o destina sem utilizar o

buffer. Neste caso deve existir uma instrução para receber a mensagem no destino no exato momento que for executado o

(17)

MPI_RSend, caso contrário o resultado é indefinido. Ao enviar a mensagem para o destino, a instrução termina.

• MPI_Recv: Esta instrução é utilizada para receber a mensagem. Pode ser utilizada qualquer um dos 4 tipos de instruções para enviar a mensagem. Só existe 1 modo de receber. Esta função termina quando a mensagem é recebida.

• MPI_Sendrecv: Esta função irá enviar a mensagem no modo padrão, e depois irá receber uma mensagem de outro processo. • MPI_Probe: Esta função irá checar se existe algum processo

tentando enviar uma mensagem. Ela só termina quando existir tal processo.

Os parâmetros utilizados para a execução do MPI_Send, MPI_Bsend, MPI_Ssend e MPI_Rsend são os mesmos:

MPI_Send(*buf, count, datatype, dest, tag, comm);

*buf refere-se ao endereço do mensagem a ser passada. count indica o número de elementos da mensagem,

datatype indica o tipo de dado armazenado na mensagem, dest refere-se ao processo que irá receber a mensagem, tag valor utilizado para diferenciar uma mensagem de outra, comm irá informar qual o comunicador utilizado.

Esse é mais um conjunto de instruções onde haveria um impacto positivo no desenvolvimento do programa, se houvesse a possibilidade de visualização dos parâmetros passados a função. Assim o programador saberia qual informação está sendo enviada para um processo, o tamanho dessa informação, o tipo de dado, e qual o rank do processo de destino que irá receber essa informação. Com isso ele poderá identificar se algum processo está mandando uma informação incorreta para outro processo, ou se a informação está correta mas o processo de destino não é o correto.

Quando se utiliza a instrução MPI_BSend deve-se garantir que um buffer com tamanho suficiente exista para armazenar a mensagem a ser passada. Para isso utiliza-se as instruções MPI_Buffer_attach() e MPI_Buffer_detach() [3]

int MPI_Buffer_attach(void* buffer, int size); int MPI_Buffer_detach(void* buffer, int size);

*buffer refere-se ao endereço do buffer,

size irá informar qual o tamanho a ser utilizado.

A instrução MPI_Recv irá utilizar os seguintes parâmetros:

MPI_Recv(*buf, count, datatype, source, tag, comm, *status);

*buf refere-se ao endereço do local que receberá a mensagem. count indica o número de elementos reservado para a mensagem, datatype indica o tipo de dado de cada elemento a ser armazenado,

(18)

source refere-se ao processo que irá enviar a mensagem, pode-se utilizar a variável MPI_ANY_SOURCE indicando que se deseja receber uma mensagem de qualquer processo,

tag valor utilizado para diferenciar uma mensagem de outra, pode-se utilizar a variável MPI_ANY_TAG indicando que se deseja receber uma mensagem com qualquer tag,

comm irá informar qual o comunicador utilizado,

*status indica o estado da mensagem. Se for necessário saber qual o rank do processo que enviou a mensagem, ou qual a tag utilizada, basta ler diretamente de status.MPI_SOURCE e status.MPI_TAG.

A instrução MPI_Recv só irá terminar se houver algum processo que enviou uma informação para o processo que a executou. Se houvesse uma maneira de o usuário visualizar os parâmetros desta instrução, ele poderia verificar se o valor do rank do processo que irá enviar a mensagem está correto ou não, pois se estiver incorreto poderá ocorrer um erro no processamento da informação, ou um travamento do programa, pois o processo iria ficar esperando por uma mensagem que nunca será enviada.

A instrução MPI_Sendrecv utiliza os seguintes parâmetros:

MPI_Sendrecv(*sendbuf, sendcount, sendtype, dest, sendtag,*recvbuf, recvcount, recvtype, source, recvtag, comm, *status);

*sendbuf refere-se ao endereço da mensagem a ser passada, sendcount indica a quantidade de elementos a serem enviados,

sendtype indica o tipo de dado de cada elemento armazenado na mensagem,

dest refere-se ao processo que irá receber a mensagem, sendtag valor da tag de envio da mensagem,

*recvbuf refere-se ao endereço do local que receberá a mensagem, recvcount indica o número de elementos reservado para a mensagem,

recvtype indica o tipo de dado de cada elemento a ser armazenado, source refere-se ao processo que irá receber a mensagem,

recvtag valor da tag de recebimento da mensagem, comm irá informar qual o comunicador utilizado, *status indica o estado da mensagem.

Esta instrução também poderia passar por um depurador, para o programador conferir se os parâmetros estão corretos, se não existe conflito entre os ranks de destino e origem, se esses ranks estão com os valores dos ranks dos processos corretos, se a informação a ser enviada está correta, se está ocorrendo deadlock.

A instrução MPI_Probe utiliza os seguintes parâmetros:

(19)

source refere-se ao processo que irá enviar a mensagem, pode-se utilizar a variável MPI_ANY_SOURCE.

tag valor utilizado para diferenciar uma mensagem de outra, pode-se utilizar a variável MPI_ANY_TAG.

comm irá informar qual o comunicador utilizado. *status indica o estado da mensagem.

Como esta função só termina quando houver um processo tentando enviar uma mensagem para o processo que a executou, o programa corre o risco de entrar em deadlock se for passado um rank errado como parâmetro. Um depurador que mostrasse ao usuário os parâmetros passados nesta instrução permitiria ao usuário evitar esse problema.

3.3.4.3 Funções Não Bloqueantes na comunicação ponto a ponto entre processos

Esta seção irá descrever algumas das funções da biblioteca MPI que não são bloqueantes na comunicação ponto a ponto entre processos:

• MPI_Isend: Inicia o envio a mensagem no modo padrão.

• MPI_Ibsend: Inicia o envio da mensagem para o buffer de armazenamento local da aplicação.

• MPI_Issend: Inicia o envio da mensagem em modo síncrono. • MPI_Irsend: Inicia o envia da mensagem para o destina sem

utilizar o buffer.

• MPI_Irecv: Inicia o recebimento da mensagem.

Estas instruções apenas iniciam o processo de envio ou recebimento de uma mensagem, não garantindo que a mensagem será enviada ou recebida corretamente. Para se garantir deve-se verificar se a instrução passada anteriormente foi executada, isso é feito por uma das funções descritas a seguir:

• MPI_Wait: Esta instrução finaliza uma instrução específica de passagem de mensagem pendente. Ela só retorna quando a instrução for concluída.

• MPI_Waitany: Esta instrução finaliza alguma instrução de passagem de mensagem pendente. Ela só retorna quando alguma instrução for concluída.

• MPI_Waitall: Esta instrução finaliza todas as instruções de passagem de mensagem pendentes. Ela só retorna quando todas as instruções forem concluídas.

• MPI_Waitsome: Esta instrução finaliza pelo menos uma das instruções de passagem de mensagem pendentes. Ela só retorna quando pelo menos uma das instruções forem concluídas.

• MPI_Test: Esta instrução verifica se uma determinada instrução de passagem de mensagem pendente foi concluída. Seu retorno é imediato.

(20)

• MPI_Testany: Esta instrução verifica se alguma instrução de passagem de mensagem pendente foi concluída. Seu retorno é imediato.

• MPI_Testall: Esta instrução verifica se todas as instruções de passagem de mensagem pendentes foram concluídas. Seu retorno é imediato.

• MPI_Testsome: Esta instrução verifica se pelo menos uma instrução de passagem de mensagem pendente foi concluída. Seu retorno é imediato.

Existem ainda as instruções:

• MPI_IProbe: Verifica se algum processo vai enviar uma mensagem.

• MPI_Cancel: Marca uma instrução de passagem de mensagem que está pendente para ser cancelada.

• MPI_Test_Cancelled: Verifica se a instrução já foi cancelada. Os parâmetros utilizados para a execução do MPI_Isend, MPI_Ibsend, MPI_Issend e MPI_Irsend são os mesmos:

MPI_Isend( *buf, count, datatype, dest, tag, comm, *request);

*buf refere-se ao endereço do mensagem a ser passada. count indica o número de elementos da mensagem,

datatype indica o tipo de dado armazenado na mensagem, dest refere-se ao processo que irá receber a mensagem, tag valor utilizado para diferenciar uma mensagem de outra, comm irá informar qual o comunicador utilizado.

*request armazena esta requisição.

Os parâmetros passados a função MPI_Irecv são:

MPI_Irecv(*buf, count, datatype, source, tag, comm, *request);

*buf refere-se ao endereço do local que receberá a mensagem. count indica o número de elementos reservado para a mensagem, datatype indica o tipo de dado de cada elemento a ser armazenado, source refere-se ao processo que irá enviar a mensagem, pode-se utilizar a variável MPI_ANY_SOURCE indicando que se deseja receber uma mensagem de qualquer processo,

tag valor utilizado para diferenciar uma mensagem de outra, pode-se utilizar a variável MPI_ANY_TAG indicando que se deseja receber uma mensagem com qualquer tag,

comm irá informar qual o comunicador utilizado, *request armazena esta requisição.

As instruções MPI_Isend, MPI_Ibsend, MPI_Issend, MPI_Irsend e MPI_Irecv são semelhantes as instruções MPI_Send, MPI_Bsend, MPI_Ssend, MPI_Rsend e MPI_Recv, a diferença é que as primeiras apenas iniciam o processo de envio da mensagem, retornando logo após a sua chamada. Um

(21)

depurador para as instruções não bloqueantes funcionaria do mesmo modo que um depurador para as instruções bloqueantes.

Os parâmetros passados para a instrução MPI_Wait são:

MPI_Wait(*request, *status);

*request requisição a ser processada. *status indica o estado da requisição.

Um depurador para esta instrução poderia exibir ao usuário as informações sobre a requisição passada para a função. Assim o programador poderia verificar se a requisição passada esta correta.

Os parâmetros passados para a instrução MPI_Waitany são:

MPI_Waitany( count, *array_of_requests, *index, *status);

count quantidade de elementos na lista de requisições.

*array_of_requests lista de requisições a serem processadas. *index índice da requisição que foi concluída.

*status indica o estado da requisição.

Na instrução MPI_Waitany, o depurador poderá exibir ao programador as informações referentes a lista de requisições, para que possa ser feita a verificação de que a requisição desejada corresponde ao valor do índice passado.

Os parâmetros passados para a instrução MPI_Waitall são:

MPI_Waitall( count, *array_of_requests, *array_of_statuses );

count quantidade de elementos na lista de requisições e estados. *array_of_requests lista de requisições a serem processadas. *array_of_statuses lista de estados da requisições.

Para esta instrução, o depurador iria permitir a visualização da lista de requisições e da lista de status, para que possa ser feita o acompanhamento das requisições e verificar em que momento todas são concluídas, e se existiu alguma que não foi concluída por algum motivo.

Os parâmetros passados para a instrução MPI_Waitsome são:

MPI_Waitsome( incount, *array_of_requests, *outcount, *array_of_indices, *array_of_statuses);

incount quantidade de elementos na lista de requisições, índices e estados.

*array_of_requests lista de requisições a serem processadas. *outcount quantidade de requisições que foram concluídas.

*array_of_indices lista com os índices das requisições que foram concluídas.

(22)

*array_of_statuses lista de estados da requisições.

Na instrução MPI_Waitsome, o depurador poderia permitir ao programador visualizar o conteúdo das listas de requisição, índice e status. As funções MPI_Test, MPI_Testany, MPI_Testall e MPI_Testsome poderiam ser utilizadas no depurador da mesma forma que as funções MPI_Wait, MPI_Waitany, MPI_Waitall e MPI_Waitsome.

Os parâmetros passados para a instrução MPI_Test são:

MPI_Test(*request, *flag, *status);

*request requisição a ser processada.

*flag indica se a operação foi concluída. Se a operação foi concluída retorna TRUE senão retorna FALSE.

*status indica o estado da requisição.

Os parâmetros passados para a instrução MPI_Testany são:

MPI_Testany( count, *array_of_requests, *index, *flag, *status);

count quantidade de elementos na lista de requisições.

*array_of_requests lista de requisições a serem processadas.

*index índice da requisição que foi concluída. Se nenhuma requisição foi concluída assume o valor MPI_UNDEFINED.

*flag se a operação foi concluída retorna TRUE senão retorna FALSE. *status indica o estado da requisição.

Os parâmetros passados para a instrução MPI_Testall são:

MPI_Testall(count, *array_of_requests, *flag, *array_of_statuses );

count quantidade de elementos na lista de requisições e estados. *array_of_requests lista de requisições a serem processadas.

*flag se todas as operações foram concluídas retorna TRUE senão retorna FALSE.

*array_of_statuses lista de estados da requisições.

Os parâmetros passados para a instrução MPI_Testsome são:

MPI_Testsome( incount, *array_of_requests, *outcount, *array_of_indices, *array_of_statuses);

incount quantidade de elementos na lista de requisições, índices e estados.

*array_of_requests lista de requisições a serem processadas. *outcount quantidade de requisições que foram concluídas.

*array_of_indices lista com os índices das requisições que foram concluídas.

(23)

Os parâmetros passados à função MPI_Iprobe são:

MPI_Iprobe( source, tag, comm, *flag, *status);

source refere-se ao processo que irá enviar a mensagem, pode-se utilizar a variável MPI_ANY_SOURCE.

tag valor utilizado para diferenciar uma mensagem de outra, pode-se utilizar a variável MPI_ANY_TAG comm irá informar qual o comunicador utilizado.

comm comunicador utilizado.

*flag retorna TRUE se existir alguma mensagem a ser enviada, senão retorna FALSE.

*status indica o estado da mensagem.

Para a instrução MPI_Iprobe, um depurador iria exibir o conteúdo dos parâmetros passados a instrução, para o programador verificar se todos estão corretos, pois caso algum deles esteja errado o resultado final do programa estará incorreto.

Os parâmetros passados à instrução MPI_Cancel são:

MPI_Cancel( *request );

*request requisição a ser processada.

No caso da instrução MPI_Cancel, o valor da requisição poderia ser mostrado ao usuário pelo depurador, assim seria fácil identificar um provável erro dentro do programa, caso se verificasse que a requisição não é a correta, ou que não havia necessidade de ser cancelada.

Os parâmetros passados à instrução MPI_Test_Cancelled são:

MPI_Test_cancelled( status, *flag );

status refere-se estado de uma mensagem.

*flag retorna TRUE se a mensagem associada a variável status foi cancelada com sucesso, senão retorna FALSE.

Nesta instrução, o depurador seria utilizado para exibir o conteúdo de status e flag, para que o programador verificasse se a variável status se refere a requisição que ele quer verificar se foi cancelada.

3.3.4.4 Funções Bloqueantes na Comunicação Coletiva

Esta seção irá descrever algumas das funções da biblioteca MPI que são bloqueantes na comunicação coletiva:

• MPI_Barrier: Esta função paralisa o processo que a executou até que todos os processos do grupo executem esta mesma função, e então continua.

(24)

• MPI_Bcast: Esta função envia uma mensagem a todos os processos de um determinado grupo.

Figura 3.5: Exemplo do funcionamento da instrução MPI_Bcast

• MPI_Gather: Esta função é utilizada para receber uma mensagem de todos os processos e concatená-las no processo destino.

Figura 3.6: Exemplo do funcionamento da instrução MPI_Gather

• MPI_Allgather: Esta função realiza o MPI_Gather e depois o MPI_Bcast.

Figura 3.7: Exemplo de funcionamento da instrução MPI_Allgather

• MPI_Scatter: Esta função separa uma mensagem e então a distribui para todos os processos do grupo.

(25)

Figura 3.8: Exemplo de funcionamento da instrução MPI_Scatter

• MPI_Alltoall: Esta função executa a instrução MPI_Gather e depois executa a instrução MPI_Scatter.

• MPI_Reduce: Combina as mensagens dos processos obedecendo uma determinada função.

Figura 3.9: Exemplo da função MPI_Reduce realizando uma soma

• MPI_Allreduce: Esta função realiza um MPI_Reduce e depois realiza um MPI_Bcast.

• MPI_Reduce_scatter: Esta função realiza um MPI_Reduce e depois realiza um MPI_Scatter.

Os parâmetros passados a função MPI_Barrier são:

MPI_Barrier( comm);

comm comunicador utilizado pelo grupo.

Para a instrução MPI_Barrier, um depurador seria útil na verificação do comunicador, isso permitiria ao usuário checar se o comunicador passado como parâmetro é o comunicador correto.

(26)

Os parâmetros passados a função MPI_Bcast são:

MPI_Bcast( *buffer, count, datatype, root, comm);

*buf refere-se ao endereço da mensagem a ser passada. count indica o número de elementos da mensagem,

datatype indica o tipo de dado armazenado na mensagem, root refere-se ao processo que irá enviar a mensagem, comm comunicador utilizado pelo grupo.

Na instrução MPI_Bcast, um depurador seria utilizado para mostrar o conteúdo da variável buffer, para que o usuário verificasse se a informação que será passada está correta, e também poderia ser visualizado o valor de root, para verificar se seu valor corresponde ao processo que será utilizado para enviar a mensagem.

Os parâmetros passados a função MPI_Gather são:

MPI_Gather( *sendbuf, sendcount, sendtype,

*recvbuf, recvcount, recvtype, root, comm);

*sendbuf refere-se ao endereço da mensagem a ser enviada.

sendcount indica o número de elementos da mensagem a ser enviada.

sendtype indica o tipo de dado armazenado na mensagem a ser enviada.

*recvbuf refere-se ao endereço do local que receberá a mensagem. recvcount indica o número de elementos a serem recebidos.

recvtype indica o tipo de dado a ser armazenado. root refere-se ao processo que irá enviar a mensagem. comm comunicador utilizado pelo grupo.

Nessa instrução, o programador poderia utilizar um depurador para verificar o conteúdo da variável root, para checar se o processo onde se quer ter o resultado final realmente receberá esse resultado, também poderia ser exibido ao usuário o conteúdo da variável sendbuf, para checar se os dados dentro dela estão corretos, e também verificar o conteúdo da variável recvbuf, para verificar se o seu conteúdo está preenchido com os valores corretos. Isso se aplica também a instrução MPI_Allgather.

Os parâmetros passados a função MPI_Allgather são:

MPI_Allgather( *sendbuf, sendcount, sendtype,

*recvbuf, recvcount, recvtype, comm);

*sendbuf refere-se ao endereço da mensagem a ser enviada.

sendcount indica o número de elementos da mensagem a ser enviada.

sendtype indica o tipo de dado armazenado na mensagem a ser enviada.

*recvbuf refere-se ao endereço do local que receberá a mensagem. recvcount indica o número de elementos a serem recebidos.

(27)

recvtype indica o tipo de dado a ser armazenado. comm comunicador utilizado pelo grupo.

Os parâmetros passados a função MPI_Scatter são:

MPI_Scatter( *sendbuf, sendcount, sendtype,

*recvbuf, recvcount, recvtype, root, comm);

*sendbuf refere-se ao endereço da mensagem a ser enviada.

sendcount indica o número de elementos da mensagem a ser enviada.

sendtype indica o tipo de dado armazenado na mensagem a ser enviada.

*recvbuf refere-se ao endereço do local que receberá a mensagem. recvcount indica o número de elementos a serem recebidos.

recvtype indica o tipo de dado a ser armazenado. root refere-se ao processo que irá enviar a mensagem. comm comunicador utilizado pelo grupo.

Os parâmetros passados a função MPI_Alltoall são:

MPI_Alltoall( *sendbuf, sendcount, sendtype,

*recvbuf, recvcount, recvtype, comm);

*sendbuf refere-se ao endereço da mensagem a ser enviada.

sendcount indica o número de elementos da mensagem a ser enviada.

sendtype indica o tipo de dado armazenado na mensagem a ser enviada.

*recvbuf refere-se ao endereço do local que receberá a mensagem. recvcount indica o número de elementos a serem recebidos.

recvtype indica o tipo de dado a ser armazenado. comm comunicador utilizado pelo grupo.

As funções MPI_Scatter e MPI_Alltoall seriam utilizadas no depurador de uma forma semelhante, o depurador poderia mostrar ao programador o conteúdo das variáveis sendbuf e recvbuf em ambas as instruções, e no caso da instrução MPI_Scatter, também seria interessante visualizar o conteúdo da variável root.

Os parâmetros passados a função MPI_Reduce são:

MPI_Reduce( *sendbuf, *recvbuf, count, datatype, op, root, comm);

*sendbuf refere-se ao endereço da mensagem a ser enviada. *recvbuf refere-se ao endereço do local que receberá a mensagem. count indica o número de elementos a serem recebidos.

datatype indica o tipo de dado a ser armazenado.

op indica a operação a ser realizada entre as mensagens dos processos.

(28)

root indica o processo que receberá a mensagem final depois de combinada.

comm comunicador utilizado pelo grupo.

As operações predefinidas que podem ser utilizadas para combinas a mensagem são:

Operação Descrição Operação Descrição

MAX Máximo MIN Mínimo

SUM Soma PROD Produto

LAND AND Lógico BAND AND Binário

LOR OR Lógico BOR OR Binário

LXOR XOR Lógico BXOR XOR Binário

MINLOC Calcula o mínimo e o índice do processo que contém o menor valor MAXLOC Calcula o máximo e o índice do processo que contém o maior valor Tabela 3.1: Operações padrão para a instrução MPI_Reduce

Os parâmetros passados a função MPI_Allreduce são:

MPI_Allreduce(*sendbuf, *recvbuf, count, datatype, op, root, comm);

*sendbuf refere-se ao endereço da mensagem a ser enviada. *recvbuf refere-se ao endereço do local que receberá a mensagem. count indica o número de elementos a serem recebidos.

datatype indica o tipo de dado a ser armazenado.

op indica a operação a ser realizada entre as mensagens dos processos.

root indica o processo que receberá a mensagem final depois de combinada.

comm comunicador utilizado pelo grupo.

Nessas duas instruções, MPI_Reduce e MPI_Allreduce, o programador utilizaria o depurador para visualizar o conteúdo dos parâmetros dessas funções, checando qualquer conflito que possa ocorrer, seja um deadlock ou uma informação incorreta.

Os parâmetros passados a função MPI_Reduce_scatter são:

MPI_Reduce_scatter(*sendbuf,*recvbuf,*recvcounts,datatype,op,com m);

*sendbuf refere-se ao endereço da mensagem a ser enviada. *recvbuf refere-se ao endereço do local que receberá a mensagem. *recvcount indica o número de elementos no resultado que serão distribuídos para cada processo. Deve ser igual ao número de processos no grupo.

(29)

datatype indica o tipo de dado a ser armazenado.

op indica a operação a ser realizada entre as mensagens dos processos.

comm comunicador utilizado pelo grupo.

O depurador poderia, na instrução MPI_Reduce_scatter, mostrar ao programador, o conteúdo das variáveis sendbuf e recvbuf, para que se faça a checagem dos seus conteúdos, e também da variável recvcount para verificar se a quantidade está correta.

(30)

4 Funcionamento do MPI_SPY

O MPI_SPY é dividido em dois programas: SPY_View e SPY_Server. O SPY_View é o visualizador propriamente dito, é a interface vista pelo usuário. O SPY_Server é um servidor de mensagens, que recebe as mensagens vindas do programa MPI e as repassa para o SPY_View, como pode ser visto na Figura 4.1. Foi feito isto devido às características que um servidor de mensagens possui, que inviabilizam a criação de um servidor no próprio SPY_View.

Figura 4.1: Funcionamento do SPY_View

Tanto o programa MPI, quanto o SPY_Server e o SPY_View, poderão estar sendo executados em uma mesma máquina, ou em máquinas diferentes.

Durante a execução do SPY_View é feita a interceptação de algumas instruções de uma aplicação MPI para se ter um acompanhamento de sua execução, e também para se ter uma visualização da transferência de mensagens entre os computadores do cluster, tendo assim uma visualização do uso da rede.

(31)

4.1 Alterações feitas na biblioteca MPI

Para tornar possível o desenvolvimento do SPY_View a biblioteca MPI foi alterada, para permitir que as instruções referentes a inicialização de uma aplicação MPI e as transferências de dados entre os computadores do cluster fossem interceptadas de uma maneira mais fácil. Essas instruções foram alteradas, para que, quando fossem executadas, o SPY_Server fosse acionado, e conseqüentemente o SPY_View fosse capaz de mostrar ao programador alguma informação referente a instrução que foi executada.

O comando da biblioteca MPI alterado foi:

• mpirun: comando utilizado para executar o programa do usuário no cluster.

As instruções que foram modificadas são:

• MPI_Init: função utilizada para inicializar o processo MPI;

• MPI_Send: função utilizada para enviar uma mensagem de um processo para outro processo;

• MPI_Recv: função utilizada para que um processo possa receber uma mensagem vinda de outro processo;

• MPI_Finalize: função utilizada para finalizar um processo MPI. Quando um programa MPI executa alguma dessas instruções, uma mensagem é enviada ao SPY_Server com os dados da instrução, e do computador que executou esta instrução. Ao passar uma mensagem para o SPY_Server, uma nova instância do SPY_Server é criada para atender esta requisição, como pode ser visto na Figura 4.2.

Figura 4.2: Atendimento de uma conexão pelo SPY_Server

Um servidor de mensagens, representado por SPY_Server (p) está sendo executado em uma determinada máquina da rede aguardando o envio de uma mensagem por um programa MPI. Quando um programa MPI executar uma das instruções que serão interceptadas, ele envia uma mensagem ao SPY_Server (p). O SPY_Server (p) então cria uma nova instância do servidor, representada por SPY_Server (f) para atender esta requisição e libera o programa MPI para continuar, isto é, o programa MPI não irá ficar esperando o envio da mensagem do SPY_Server para o SPY_View. A nova instância do SPY_Server (p), que está representado como SPY_Server (f) ficará

(32)

responsável por realizar o envio da mensagem do programa MPI para o SPY_View, liberando assim o SPY_Server (p) para atender outras requisições.

4.1.1 Definição da Mensagem a ser enviada

A mensagem a ser enviada a partir da execução de uma das instruções da biblioteca MPI que foram alteradas, possuirá algumas informações que serão tratadas pelo SPY_View. Cada instrução terá um conjunto de dados que serão interpretados pelo SPY_View e exibidos ao usuário.

A instrução mpirun é a primeira instrução da biblioteca MPI a ser executada, pois é esta instrução que será responsável em carregar o programa do usuário no cluster. As informações passadas por esta instrução ao SPY_Server são:

• Nome da Instrução;

• Número de computadores utilizados; • Nome da aplicação MPI.

O nome da instrução é a informação básica a ser passada para o SPY_View, pois será ela que irá identificar cada instrução, para que cada uma possa ser interpretada corretamente. Na instrução mpirun foi necessário a inclusão de dois identificadores, pois nessa instrução ocorrerá o envio da mensagem ao SPY_Server em dois pontos distintos, como pode ser visto na Tabela 4.1.

Instrução Nome

mpirun MPI_RUN

MPI_END

Tabela 4.1: Instrução mpirun

As outras instruções que serão interceptadas pelo SPY_Server são descritas na Tabela 4.2. Instrução Nome MPI_Init MPI_Init MPI_Send MPI_Send MPI_Recv MPI_Recv MPI_Finalize MPI_Finalize

Tabela 4.2: Nome das instruções da biblioteca MPI

O nome das instruções MPI_Init, MPI_Send, MPI_Recv e MPI_Finalize são idênticos aos nomes verdadeiros das funções na biblioteca MPI.

Existe um conjunto básico de dados a ser passado por cada instrução, que são:

• Nome da instrução;

(33)

• Rank do processo que executou a instrução;

O nome do computador é uma informação que irá identificar qual computador no cluster executou alguma das instruções da Tabela 4.2, dessa forma será possível distinguir mais claramente qual computador está executando uma determinada instrução. E acompanhado a execução do programa no cluster, poderá ser verificado qual computador está mais sobrecarregado dentre os que estão sendo utilizados pelo programa.

O rank do processo que executou a instrução é uma informação que irá diferenciar os processos MPI que estiverem sendo executados em um mesmo computador.

Todas as instruções irão passar pelo menos essas informações ao SPY_Server.

Informações adicionais são necessárias quando se executa uma instrução MPI_Send ou MPI_Recv. Nessas duas instruções são necessários os dados:

1. O tamanho da mensagem que está sendo executada pelo cluster 2. O tipo de cada elemento no vetor de mensagens.

4.1.2 Momento do Envio da Mensagem do Programa MPI para o SPY_Server

Devido a algumas características das funções que são interceptadas pelo SPY_Server, elas não são interceptadas em um mesmo ponto de execução, isto é, no início de cada instrução ou antes do término.

4.1.2.1 Interceptando a Instrução mpirun

A instrução mpirun é utilizada para se iniciar a execução de um programa no cluster, e ela é interceptada em dois pontos durante a sua execução: no início da instrução e no fim da instrução.

A interceptação no início é feita para se saber o instante em que a aplicação começou a ser executada. Na verdade a mensagem é passada ao SPY_Server é a primeira operação dessa instrução. Por ser a primeira requisição feita ao SPY_Server, esta ocorre sem problemas, pois no início não haverá qualquer outra requisição sendo atendida

E ao final da instrução é feita uma nova interceptação, para se saber se houve a conclusão da aplicação MPI, como pode ser visto na Figura 4.3.

(34)

Figura 4.3: Interceptação da instrução MPI_RUN

A mensagem MPI_END, por ser enviada ao final da aplicação MPI, poderá encontrar o SPY_Server sobrecarregado atendendo outras instruções MPI. Nesse caso, a mensagem só será recebida pelo SPY_Server, quando este não estiver mais sobrecarregado. E ao receber a mensagem contendo o MPI_END, o SPY_Server só poderá enviar esta mensagem ao SPY_View, quando já tiver enviado todas as outras que estavam sendo enviadas, para garantir que a mensagem MPI_END realmente indique o fim da aplicação MPI.

Ao se executar o comando:

$ mpirun –v 4 progmpi

Será iniciado o processo de execução do programa progmpi pela biblioteca MPI. Antes de realizar qualquer procedimento da biblioteca MPI para administrar o programa progmpi, será enviada uma mensagem ao SPY_Server sinalizando o início da execução do programa progmpi, correspondente a passo 1 da Figura 4.3. Após isso são iniciados 4 processos referentes ao progmpi no cluster, e ao terminarem de executar será enviada uma nova mensagem ao SPY_Server indicando o fim da execução do programa progmpi, que está indicado na Figura 4.3 como passo 2.

(35)

4.1.2.2 Interceptando a Instrução MPI_Init

A instrução MPI_Init é a primeira a ser executada em um programa MPI. Sendo assim durante a sua execução algumas variáveis que serão incluídas na mensagem ainda não foram inicializadas, portanto a mensagem não pode ser interceptada logo no início da execução desta instrução. O ponto onde a mensagem é enviada ao SPY_Server fica próximo da metade da execução do MPI_Init. Caso o SPY_Server não esteja tratando alguma outra mensagem, a operação de interceptação ocorre normalmente, e a instrução MPI_Init continua sendo executada. Caso não haja uma disponibilidade do SPY_Server em atender esta requisição no instante que foi solicitado, a instrução MPI_Init ficará temporariamente paralisada, esperando o atendimento ser feito pelo SPY_Server.

4.1.2.3 Interceptando a Instrução MPI_Send

A instrução MPI_Send é utilizada para se enviar uma mensagem de um processo a outro em uma aplicação MPI. Nesta instrução, a interceptação da mensagem ocorre no início da instrução, sendo a primeira operação realizada durante sua execução.

Apesar de se utilizar uma instrução bloqueante, não é feita uma checagem verificando se os dados da instrução foram passados corretamente, e também não é feita uma verificação de quando a instrução é concluída, pois estas duas verificações são feitas e garantidas pela biblioteca MPI. O SPY_Server recebe apenas uma mensagem indicando que a instrução MPI_Send foi utilizada pela aplicação MPI.

As condições para o atendimento desta mensagem pelo SPY_Server se mantêm, ou seja, a mensagem só é passada da aplicação MPI para o SPY_Server se houver menos de 5 requisições sendo atendidas, caso contrário, a instrução MPI_Send ficará aguardando o SPY_Server.

Quando houver uma disponibilidade do SPY_Server para o atendimento desta instrução, a mensagem será enviada da aplicação MPI para o SPY_Server e então o a biblioteca MPI seguirá normalmente com o procedimento de envio dos dados de um processo MPI para outro.

4.1.2.4 Interceptando a Instrução MPI_Recv

A instrução MPI_Recv é utilizada para se receber uma mensagem em um processo vinda de outro em uma aplicação MPI. A interceptação da instrução é análoga a interceptação do MPI_Send, ou seja, ocorre no início da instrução. As regras para o atendimento desta mensagem pelo SPY_Server também são os mesmos.

(36)

Da mesma forma que acontece com a instrução MPI_Send, existem mecanismos de controle que garantem o funcionamento adequado da instrução MPI_Recv, não havendo necessidade de existir uma verificação do conteúdo da mensagem pelo SPY_Server.

Também não existe uma verificação checando se para cada instrução MPI_Send houve a execução de uma instrução MPI_Recv, pois isto será uma verificação visual do próprio usuário ao visualizar a interface do SPY_View.

4.1.2.5 Interceptando a Instrução MPI_Finalize

A instrução MPI_Finalize é executada ao final da execução da aplicação MPI. A interceptação desta mensagem é feita no início de sua execução.

As regras para o atendimento desta requisição pelo SPY_Server são as mesmas regras válidas para as instruções MPI_Init, MPI_Send e MPI_Recv, ou seja, caso o limite de 5 conexões ao SPY_Server não tenha sido atingido, a mensagem será enviada ao SPY_View, caso contrário, o processo MPI ficará aguardando a disponibilidade do SPY_Server em receber essa conexão para enviar a mensagem informando a execução da instrução MPI_Finalize ao SPY_View. Não existe um controle feito pelo SPY_Server para verificar se existe alguma mensagem pendente, esta verificação pe feita pela biblioteca MPI.

A finalização da aplicação MPI é verificada quando a instrução mpirun envia a mensagem MPI_END ao SPY_Server.

4.1.3 Comportamento da aplicação MPI durante o envio da instrução

Uma aplicação MPI é um conjunto de processos sendo executados em um cluster de computadores, ou em uma única máquina. Como cada processo é uma aplicação independente, a ordem de execução das instruções poderá ser diferente, pois cada processo poderá estar executando uma parte diferente do código da aplicação, portanto a ordem em que as mensagens são enviadas ao SPY_Server não obedecerão a uma ordem, nem critério pré-estabelecidos, serão encaminhadas ao SPY_Server e enviadas ao SPY_View à medida que forem sendo executadas pelos processos MPI.

Durante o envio da instrução MPI para o SPY_Server, o processo MPI ficará temporariamente paralisado neste ponto, caso o SPY_Server esteja em execução na máquina que irá receber os dados da instrução. Caso contrário, se o SPY_Server não estiver sendo executado nesta máquina, a instrução MPI irá prosseguir normalmente, como pode ser visto na Figura 4.4.

Na Figura 4.4a o processo MPI esta executando uma parte do código que em determinado momento utiliza a instrução MPI_Send da biblioteca MPI. Como esta instrução foi modificada para enviar uma mensagem ao SPY_Server, no instante de sua execução uma mensagem será enviada ao

Referências

Documentos relacionados

“Porque quando um homem fala pelo poder do Espírito “Porque quando um homem fala pelo poder do Espírito Santo, o poder do Espírito Santo leva as suas palavras ao Santo, o poder

ensino superior como um todo e para o curso específico; desenho do projeto: a identidade da educação a distância; equipe profissional multidisciplinar;comunicação/interatividade

-Primeiramente o estudante ou o professor deverá dirigir-se até o centro de saúde/serviço desejado portando a Declaração de Ciência e

Még épp annyi idejük volt, hogy gyorsan a fejük búbjáig húzták a paplant, és Mary Poppins már nyitotta is az ajtót, és lábujjhegyen átosont a

Os estudos originais encontrados entre janeiro de 2007 e dezembro de 2017 foram selecionados de acordo com os seguintes critérios de inclusão: obtenção de valores de

As variáveis canônicas explicaram a maior parte da variância total dos dados, mas a den- sidade básica da madeira, característica funda- mental na avaliação de clones de

Para tal, constou de pesquisa de natureza aplicada à 4 sujeitos, os quais foram selecionados devido às suas condições de paraplegia Os dados coletados foram analisados de

antecede a percepção moderna de escravidão negra a partir dos Europeus, já a partir dos Europeus, já que esse processo que esse processo iniciou se muito antes do mundo islâmico