Você está na página 1de 21

UNIVERSIDADE FEDERAL DO R IO G RANDE DO S UL

INSTITUTO DE INFORMÁTICA
PROGRAMA DE PÓS -GRADUAÇÃO EM CIÊNCIA DA COMPUTAÇÃO
INTRODUÇÃO AO PROCESSAMENTO PARALELO E DISTRIBUÍDO
PROF. NAVAUX

Algoritmo para resolução do problema


do Caixeiro Viajante

por
Alexandre Cervieri
Mônica Py

Porto Alegre, Julho de 2000.


Sumário

1. Introdução ________________________________________________________________ 3
2. Modelagem Inicial __________________________________________________________ 4
3. Implementação distribuída com troca de mensagens _______________________________ 6
4. Medida de Desempenho ______________________________________________________ 8
5. Dificuldades & Comentários __________________________________________________ 9
6. Referências Bibliográficas___________________________________________________ 10
7. Anexo ___________________________________________________________________ 10

2
1. Introdução
O Problema do Caixeiro Viajante é um dos mais clássicos problemas de
Complexidade. Se caracteriza por, dado um conjunto de n cidades e uma matriz de distâncias
distancias[1..n,1..n], fazer com que seja encontrado um caminho que tenha a menor distância a
ser percorrida para que sejam visitadas todas as cidades passando exatamente uma única vez em
cada cidade e retornando a cidade de origem. O problema do caixeiro viajante é considerado um
problema do otimização, além de exibir uma solução é requerida uma certa qualidade ótima da
solução. Esse problema é NP-completo pois não tem solução determinística polinomial.
Para solucionar o problema do Caixeiro Viajante, no cluster da sala 207 do Instituto
de Informática da UFRGS, foi utilizada a biblioteca para programação distribuída MPI. MPI é
uma biblioteca de troca de mensagens desenvolvida para ambientes de memória distribuída,
máquinas paralelas massivas, NOWs (network of workstations) e redes heterogêneas.
Cluster é uma máquina de alto desempenho que possui uma arquitetura baseada na
reunião de um conjunto de estações de trabalho independentes, interconectadas por uma rede de
comunicação, formando uma plataforma de execução de aplicações paralelas de alto
desempenho. O Grupo de Processamento Paralelo e Distribuído da UFRGS possui uma
plataforma de execução para aplicações paralelas um cluster formado por 4 nodos. Cada nodo é
um Dual Pentium Pro, com 64 M de memória RAM e clock de 200Mhz. Estes nodos estão
interconectados por duas redes de comunicação: uma rede Fast Ethernet e outra rede Myrinet. O
sistema operacional é Linux, com kernel 2.2.1 e compilador C++ (gcc).
Uma característica marcante do MPI é que não existem processos, existe um único
processo que pode ser rodado em várias máquinas (chamadas de nodos) já previamente montadas
na “máquina virtual” e não modificável em tempo de execução.
MPI define um conjunto de rotinas para facilitar a comunicação (troca de dados e
sincronização) entre processos paralelos. A biblioteca MPI é portável para qualquer arquitetura,
tem aproximadamente 125 funções para programação e ferramentas para analisar o desempenho.
A biblioteca MPI permite que os programas possam ser escritos nas linguagens Fortran, C e
C++. Alguns dos principais objetivos do MPI:
• Confiabilidade e facilidade de uso são os objetivos principais do MPI.
• Esforça-se para definir uma interface de troca de mensagens que seja possível de ser
implementada eficientemente.
• A semântica da interface MPI foi escrita para ser independente da linguagem.

3
Todo paralelismo é explícito, ou seja, o programador é responsável por identificar o
paralelismo e implementar o algoritmo utilizando chamadas aos comandos da biblioteca MPI.
No segundo capítulo apresenta-se a modelagem inicial do algoritmo utilizado. O
terceiro capítulo é descrito como é feita a implementação distribuída com troca de mensagens.
Na seção seguinte mostra-se medidas de desempenho do algoritmo sequencial comparado a
implementação feita em MPI. Nos capítulos cinco e seis são feitos comentários do trabalho,
juntamente com o código fonte desenvolvido, e as conclusões, respectivamente.

2. Modelagem Inicial

O algoritmo utilizado, “Replicated Workers with a Bag of Tasks”, se baseia em um


conjunto de trabalhadores que buscam tarefas em uma sacola compartilhada. Cada tarefa consiste
em um caminho parcial das cidades contidas na matriz original. Essas tarefas são processadas
pelos trabalhadores de modo que seja acrescentada mais uma cidade, até que seja encontrado um
caminho que passe por todas as cidades do problema.
Quando o primeiro caminho é encontrado, ele é designado como mínimo. Assim que
novos caminhos completos são encontrados, eles substituem o primeiro como o caminho
mínimo, caso a sua distância seja realmente menor.
Os dados iniciais necessários para o algoritmo são o número de trabalhadores, o número
de cidades e o conjunto de distâncias que separam cada cidade. Neste caso, as cidades são
supostamente totalmente conectadas. Na figura 1 se vê um exemplo de uma configuração com 4
cidades e a matriz gerada. É possível representar um cenário onde as cidades não sejam
totalmente conectadas indicando a distância entre elas como um valor extremamente alto para
sua distância, dessa forma, esse caminho sempre será ignorado pelo algoritmo.

54 2 0 54 79 38
54 0 46 76
1 76 79 46 0 71
46
38 76 71 0
79
38
3 Matriz representando o
cenário de ligação entre cidades
4 71 mostrado ao lado.

Uma primeira implementação do algoritmo descrito acima foi feita na linguagem Java
com a utilização de memória compartilhada e threads para o paralelismo dos trabalhadores.

4
Cada trabalhador foi implementado como uma thread com fluxo próprio de execução, tendo
acesso a um conjunto de dados compartilhados representado por uma instância de uma classe
Java. Como essa instância é acessível por todos os processos trabalhadores ela exige um controle
de sincronismo. Os métodos de acesso aos dados compartilhados foram definidos como
synchronized, primitiva da linguagem Java que garante a execução do método como uma sessão
crítica.
Esta implementação está baseada em 3 classes básicas:
• classe Task: representa uma tarefa que irá ser processada por um dos trabalhadores.
Cada tarefa consiste no número de cidades visitadas, pelo conjunto de cidades deste
caminho e pela distância percorrida.
• classe Shared: é a classe responsável pela obtenção dos dados iniciais do problema
(número de cidades e matriz de distâncias) bem como pelo armazenamento desses
dados. Cada trabalhador tem acesso a instância dessa classe de modo a poder obter a
distância entre duas cidades específicas, obter tarefas para serem processadas, inserir
novas tarefas na sacola, e finalmente atualizar o caminho mínimo quando a distância
encontrada for menor.
• classe Worker: classe derivada da classe Thread, é a responsável pelo processamento
das tarefas, e pode te quantas instâncias quanto o usuário que dispara o programa
desejar. A condição de parada dos trabalhadores é não existir mais nenhuma tarefa a
ser processada bem como não existir mais nenhum trabalhador processando outra
tarefa.
Além das classes acima descritas, uma classe adicional, representando o programa
princ ipal é a responsável pelo disparo das instâncias das classes Shared, Worker. Na figura 2
pode ser visto um diagrama com a representação da comunicação entre cada classe. As setas
indicam a invocação de um método na outra classe.

5
SalesMan

instanciação
instanciação

RecvTask Shared
SendTask
Worker
Update
Bag :
getDistancia Task
Final

3. Implementação distribuída com troca de mensagens


A passagem de uma implementação com memória compartilhada para um modelo
distribuído com comunicação feita através de troca de mensagens implica em um esquema de
sincronismo e passagem de dados mais complexo. O número de mensagens também é alto.

6
Shared

Entrada de
distâncias dados
Workers

SEND distâncias
Distribui Recebe
distâncias distâncias

SENDRECV recvtask
Recebe Processa-
comandos SEND notask mento

SEND task
SEND task

SEND update

SEND stop

SEND nowork

SEND work

Resultad
o

Na figura acima pode ser visto um diagrama contendo o fluxo de troca de mensagens
entre os processos.
De modo a simular o comportamento das classes acima descrito, estabeleceu-se um
protocolo de troca de mensagens entre os processos, onde, a classe que anteriormente era

7
compartilhada passou a ser representada por um objeto servidor que se tornou o centro de
comunicação para os demais processos.
A programação foi feita utilizando-se ANSI C, com uso da biblioteca (middleware) de
troca de mensagens MPI.

4. Medida de Desempenho
Para realizar os testes, foi criado arquivos contendo matrizes de distancias para as
cidades. Cada arquivo desenvolvido contém os valores apresentados no texto a seguir. Na
realização dos testes, primeiramente foi executado a versão sequencial do programa, e
consequentemente a versão paralela. Os resultados obtidos estão considerados nas tabelas. Em
cada tabela encontra-se o número de cidades, tempo necessário para a execução do programa
sequencial e o tempo de execução em paralelo. O número de cidades inicialmente testadas
foram 4.
Contatamos que para 10 cidades, a solução do algoritmo em JAVA, obteve tempo de
processamento 12826619 ms que equivale a 3h33m46s, com resultado 261 unidades, sendo o
caminho encontrado 0 1 6 2 7 4 5 8 9 3. (isso em ambiente Linux, pois em Windows NT a
Máquina JAVA não suportou a carga).
Segue as tabelas com as matrizes e os resultados obtidos em cada execução.

Arquivo: distancias_4.txt
Matriz de distâncias
0 77 12 70
77 0 49 57
12 49 0 1
70 57 1 0

Matriz Cidades
Número de Tempo Tempo
Cidades Seqüencial (ms) Paralelo (s)
4 930 0s
Tabela 1

8
Arquivo: distancias_5.txt
Matriz de distâncias
0 21 61 80 3
21 0 4 71 7
61 4 0 97 60
80 71 97 0 57
3 7 60 57 0

Matriz Cidades
Número de Tempo Tempo
Cidades Seqüencial (s) Paralelo (s)
5 1s21 0.2s
Tabela 2

Arquivo: distancias_6.txt
Matriz de distâncias
0 2 40 4 96 46
2 0 56 92 82 19
40 56 0 47 48 95
4 92 47 0 73 0
96 82 48 73 0 33
46 19 95 0 33 0

Matriz Cidades
Número de Tempo Tempo
Cidades Seqüencial (s) Paralelo (s)
6 2s42 0.4s
Tabela 3

5. Dificuldades & Comentários

O trabalho desenvolvido incentiva a utilização de arquiteturas paralelas e distribuídas


para a resolução de aplicações que necessitam de grande capacidade de processamento. Assim
conclui-se que os resultados de desempenho alcançados compensam o trabalho de adaptar o
código seqüencial para paralelo.
Em anexo encontra-se o código fonte do trabalho.

9
6. Referências Bibliográficas
[BUB97] BUBAK, M.; DONGARRA, J.J.;WASNIEWSKI, J. Recent Advances in Parallel
Virtual Machine and Message Passing Interface. EUROPEAN PVM/MPI USERS
GROUP MEETING (4). Berlin; Springer – Verlag, 1997. 518 p.

[MPI00] MPI/Pro – Help On-line . MPI Software Technology, Inc. Disponível por WWW em
http://www.mpi-softtech.com/docs/mpipro/index.html (29 jun de 2000).

[MPI00] The Message Passing Interface (MPI) standard. Disponível por WWW em
http://www-unix.mcs.anl.gov/mpi (01 jul de 2000).

[PAC97] PACHECO, P.Parallel Programming with MPI. San Francisco: Morgan Kaufman,
1997. 418 p.

[RIG99] RIGONI, Eduardo H.; ÁVILA, R. B.; BARRETO, M. E.; SCHLEMER, E.; DE ROSE,
C. A. F.; DIVERIO, T. A.; NAVAUX, P. O. A. Introdução à programação em clusters de alto
desempenho. RP – 305 Outubro / 1999. UFRGS-PPGC.

[TER90] TERADA, R. Introdução à complexidade de algoritmos paralelos. In: ESCOLA DE


COMPUTAÇÃO, VII 1990, São Paulo, Curso, São Paulo, IME-USP, 1990. 104p.

7. Anexo
Código fonte dos arquivos salesman.c e salesman.h

salesman.h

#include <time.h>
// --
// Definições para o sistema
#define MAX_MSG_SIZE 256 //tamanho máximo das mensagens
#define RANK_SERVER 0 //índice do processo que é servidor
#define MSG_UPDATE 0 //msg de worker para atualziar caminho mínimo
#define MSG_RECVTASK 1 //msg de worker pedindo por uma tarefa
#define MSG_SENDTASK 2 //msg de worker desejando enviar uma tarefa
#define MSG_TASK 3 //msg de server respondendo com uma tarefa
#define MSG_NOTASK 4 //msg de server respondendo que não tem tarefa
#define MSG_INIT 5 //msg de server com os dados iniciais do problema
#define MSG_STOP 6 //msg de worker dizendo que terminou
#define MSG_HAVEWORK 7 //msg de worker pedindo se ainda deve trabalhar
#define MSG_WORK 8 //msg de server dizendo para worker trabalhar
#define MSG_NOWORK 9 //server dizendo para worker parar de trabalhar
#define MSG_NONE 10
#define MSG_END '#' //marca de final de mensagem
// Forward das estruturas e funções do programa
void printPacket(char* packet);
struct Task{
int hops; //número de cidades nessa task
int totalhops; //número total de cidades do problema
int length; //tamanho do caminho dessa tarefa
int *path; //lista das cidades que estão nessa tarefa, nesse

10
caminho
struct Task *next;
};
//funções associadas
char *Task_toString(struct Task *task);
char *Task_toPacket(struct Task *task);
struct Task *Task_toTask(char *packet);
void Task_push(struct Task **list,struct Task *task);
struct Task *Task_pop(struct Task **list);
void Task_clonePath(int **dest,int *source,int size);
void Task_free(struct Task *task);
//...
struct Shared{
int numworkers; //númro de workers
int *endworkers; //indica quais workers estão parados
int numliveworkers; //número de processos que ainda estão rodando
int numcidades; //número de cidades do problema
int **distancias; //distância entre as cidades
int shortest; //tamanho do caminho mínimo encontrado
int *shortestpath; //o menos caminho
struct Task *bag; //lista com as tarefas a serem feitas
time_t timeInicial; //tempo de início do algoritmo
time_t timeFinal; //tempo de fim do algoritmo
};
//funções associadas
struct Shared *Shared_init(char *ndistfile,int nworkers);
char *Shared_toString(struct Shared *shared);
void Shared_finalize(struct Shared **shared);
void Shared_update(struct Shared *shared,char *message);
void Shared_recvTask(struct Shared *shared,int source);
void Shared_sendTask(struct Shared *shared,char *message);
void Shared_main(char* ndistfile,int nworkers);
void Shared_stop(struct Shared *shared,int source);
void Shared_haveWork(struct Shared *shared,int source);
//...
struct Worker{
int numcidades; //número de cidades do problema
int **distancias; //matriz das distâncias entre as cidades
};
//funções associadas
char *Worker_toString(struct Worker *worker);
char *Worker_toPacket(struct Worker *worker);
struct Worker *Worker_toWorker(char *packet);
int Worker_visit(struct Worker *worker,int city,struct Task *task);
void Worker_main();
// --

salesman.c

// --
// Introdução ao Processamento Paralelo e Distribuído
// Programa: 'The Salesman Problem: Replicated Workers and a Bag of Tasks'
// Implementação utilizando MPI: Messages Passsing Interface
// Última modifcação: 30 de junho de 2000
// --
// --
#define _MPI
//#define _DEBUG
// --
// --

11
// Biliotecas utilizadas
#include "salesman.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _MPI
#include "mpi.h"
#endif
// --
// --
void printPacket(char* packet){
int i=0;
printf("# (");
while(packet[i]!=MSG_END){
printf(" %d",packet[i]);
i++;
}
printf(" )\n");
}
// Funções associadas a estrutura de dados Task
char *Task_toString(struct Task *task){
int i;
char *str, *tmp;
str = (char*) malloc(4*MAX_MSG_SIZE*sizeof(char));
tmp = (char*) malloc(10*sizeof(char));
strcpy(str,"\tPath:( ");
for(i=0;i<task->totalhops;i++){
sprintf(tmp,"%d ",task->path[i]); strcat(str,tmp);
}
sprintf(tmp,") Hops:[%d] Length:[%d]",task->hops,task->length);
strcat(str,tmp);
free(tmp);
return str;
}
char* Task_toPacket(struct Task *task){
int i;
char *str;
str = (char*) malloc(MAX_MSG_SIZE*sizeof(char));
str[0] = (char) MSG_NONE; //vai sempre o tipo da mensagem...
str[1] = (char) task->hops; //1: número de cidades na
task
str[2] = (char) task->totalhops;
str[3] = (char) ((task->length&0xFF000000)>>24); //3: length[0]
([0][1][2][3])
str[4] = (char) ((task->length&0x00FF0000)>>16); //4: length[1]
str[5] = (char) ((task->length&0x0000FF00)>>8); //5: length[2]
str[6] = (char) ((task->length&0x000000FF)>>0); //6: length[3]
for(i=0;i<task->totalhops;i++)
str[7+i] = (char) task->path[i];
str[7+i] = MSG_END;
return str;
}
struct Task *Task_toTask(char* packet){
int i,tmp;
struct Task *task;
task = (struct Task*) malloc(sizeof(struct Task));
task->hops = (int) packet[1];
task->totalhops = (int) packet[2];
Task_clonePath(&(task->path),NULL,task->totalhops);
task->length = 0;

12
tmp = (int) packet[6];
tmp = (tmp<0)?(256+tmp):tmp;
task->length += tmp;
tmp = (int) packet[5];
tmp = (tmp<0)?(256+tmp):tmp;
task->length += tmp<<8;
tmp = (int) packet[4];
tmp = (tmp<0)?(256+tmp):tmp;
task->length += tmp<<16;
tmp = (int) packet[3];
tmp = (tmp<0)?(256+tmp):tmp;
task->length += tmp<<24;
for(i=0;i<task->totalhops;i++)
task->path[i] = (int) packet[7+i];
return task;
}
void Task_push(struct Task **list,struct Task *task){
task->next = (*list);
(*list) = task;
}
struct Task *Task_pop(struct Task **list){
struct Task* task;
task = (*list);
if ((*list)!=NULL)
(*list) = (*list)->next;
return task;
}
void Task_clonePath(int **dest, int* source, int size){
int i;
(*dest) = (int*) malloc(size*sizeof(int));
if( source!= NULL )
for(i=0;i<size;i++)
(*dest)[i] = source[i];
else
for(i=0;i<size;i++)
(*dest)[i] = 0;
}
void Task_free(struct Task *task){
free(task->path);
free(task);
}
// --

// --
// Funções associadas a estrutura de dados Shared
struct Shared *Shared_init(char* ndistfile, int nworkers){
int k;
struct Shared *shared;
shared = (struct Shared*) malloc(sizeof(struct Shared));
shared->numworkers = nworkers;
shared->numliveworkers = nworkers;
shared->endworkers = (int*) malloc(shared->numworkers*sizeof(int));
for(k=0;k<shared->numworkers;k++)
shared->endworkers[k] = 0;
{
int i=0,j=0;
int valor;
FILE* handle;
if( (handle = fopen(ndistfile,"r"))==NULL ){
printf("@ERRO FATAL: não foi possível abrir o arquivo.\n");
free(shared);

13
return NULL;
}else{
fscanf(handle,"%d\n",&valor);
shared->numcidades = valor;
shared->distancias = (int**) malloc(shared->numcidades*sizeof(int*));
for(i=0;i<shared->numcidades;i++){
shared->distancias[i] = (int*) malloc(shared-
>numcidades*sizeof(int));
for(j=0;j<shared->numcidades;j++){
fscanf(handle,"%d\n",&valor);
shared->distancias[i][j] = valor;
}
}
fclose(handle);
}
}
shared->shortest = 10000;
Task_clonePath(&(shared->shortestpath),NULL,shared->numcidades);
printf("%s\n",Shared_toString(shared));
return shared;
}
char* Shared_toString(struct Shared* shared){
int i,j;
char *str,*tmp;
str = (char*) malloc(2048*sizeof(char));
tmp = (char*) malloc(80*sizeof(char));
sprintf(str,"#Shared\n\t#num. cidades: %d\n",shared->numcidades);
sprintf(tmp,"\t#distancias: \n"); strcat(str,tmp);
for(i=0;i<shared->numcidades;i++){
sprintf(tmp,"\t\t"); strcat(str,tmp);
for(j=0;j<shared->numcidades;j++){
sprintf(tmp,"%d ",shared->distancias[i][j]); strcat(str,tmp);
}
strcat(str,"\n");
}
sprintf(tmp,"\t#shortest: %d\n",shared->shortest); strcat(str,tmp);
sprintf(tmp,"\t#shortestpath: ("); strcat(str,tmp);
for(i=0;i<shared->numcidades;i++){
sprintf(tmp," %d",shared->shortestpath[i]); strcat(str,tmp);
}
sprintf(tmp," )\n"); strcat(str,tmp);
return str;
}
void Shared_finalize(struct Shared **shared){
int i=0;
{
printf("\n#algoritmo finalizado.\n");
printf("#tempo inicial: %d\n",(*shared)->timeInicial);
printf("#tempo final: %d\n",(*shared)->timeFinal);
printf("#tempo de processamento: %f(us)\n",difftime((*shared)-
>timeFinal,(*shared)->timeInicial));
printf("%s\n",Shared_toString((*shared)));
}
for(i=0;i<(*shared)->numcidades;i++)
free((*shared)->distancias[i]);
free((*shared)->distancias);
free(*shared);
}
void Shared_update(struct Shared *shared,char *message){
int i;
struct Task *task;

14
task = Task_toTask(message);
#ifdef _DEBUG
printf("S_update: %s\n",Task_toString(task));
#endif
if( task->length<shared->shortest){
shared->shortest = task->length;
for(i=0;i<task->totalhops;i++)
shared->shortestpath[i] = task->path[i];
}
Task_free(task);
}
void Shared_recvTask(struct Shared *shared,int source){
struct Task *task;
char *message;
if ( ( task = Task_pop(&(shared->bag)) ) != NULL ){
message = Task_toPacket(task);
message[0] = (char) MSG_TASK;
#ifdef _MPI

MPI_Send(message,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,source,MSG_NONE,MPI_COMM_WORL
D);
#ifdef _DEBUG
printf("S_recvtask(%d): ",source);
printPacket(message);
#endif
#endif
Task_free(task);
free(message);
}else{
message = (char*) malloc(2*sizeof(char));
message[0] = (char) MSG_NOTASK;
message[1] = MSG_END;
#ifdef _MPI

MPI_Send(message,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,source,MSG_NONE,MPI_COMM_WORL
D);
#ifdef _DEBUG
printf("S_recvtask(%d): ",source);
printPacket(message);
#endif
#endif
free(message);
}
}
void Shared_sendTask(struct Shared *shared,char* message){
struct Task *task;
task = Task_toTask(message);
#ifdef _DEBUG
printf("S_sendtask: %s\n",Task_toString(task));
#endif
if( task->length<shared->shortest )
Task_push(&(shared->bag),task);
else
Task_free(task);
}
void Shared_stop(struct Shared *shared,int source){
shared->endworkers[source] = 1;
}
void Shared_haveWork(struct Shared *shared,int source){
int i,end=1;
char *message;

15
message = (char*) malloc(2*sizeof(char));
for(i=1;i<=shared->numworkers;i++)
end &= shared->endworkers[i];
if( end==1 ){
message[0] = (char) MSG_NOWORK;
message[1] = MSG_END;
#ifdef _MPI

MPI_Send(message,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,source,MSG_NONE,MPI_COMM_WORL
D);
#ifdef _DEBUG
printf("S_havework: ");
printPacket(message);
#endif
#endif
shared->numliveworkers--;
}else{
message[0] = (char) MSG_WORK;
message[1] = MSG_END;
#ifdef _MPI

MPI_Send(message,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,source,MSG_NONE,MPI_COMM_WORL
D);
#ifdef _DEBUG
printf("S_havework: ");
printPacket(message);
#endif
#endif
}
free(message);
}
void Shared_main(char* ndistfile,int nworkers){
struct Shared *shared;
if ( (shared = Shared_init(ndistfile,nworkers))!=NULL ){
int i,j;
struct Task *task;
for(i=1;i<shared->numcidades;i++){
task = (struct Task*) malloc(sizeof(struct Task));
task->hops = 2;
task->totalhops = shared->numcidades;
task->length = shared->distancias[0][i];
Task_clonePath(&(task->path),NULL,shared->numcidades);
task->path[0] = 0;
task->path[1] = i;
Task_push(&(shared->bag),task);
}
{//Enviando dados iniciais do problema, número de cidades e matriz de
distâncias
struct Worker *worker;
char *message;
int i;
worker = (struct Worker*) malloc(sizeof(struct Worker));
worker->numcidades = shared->numcidades;
worker->distancias = shared->distancias;
message = Worker_toPacket(worker);
message[0] = (char) MSG_INIT;
shared->timeInicial = time(NULL);
for(i=1;i<=shared->numworkers;i++){
#ifdef _MPI

MPI_Send(message,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,i,MSG_NONE,MPI_COMM_WORLD);

16
#endif
}
}
{//Iniciando servidor de tarefas, inicialmente fica esperando requisição
char *message;
int origem=0,tipomsg=0,source=0;
#ifdef _MPI
MPI_Status status;
#endif
message = (char*) malloc(MAX_MSG_SIZE*sizeof(char));
while(shared->numliveworkers!=0){
for(i=1;i<=shared->numworkers;i++){
#ifdef _MPI

MPI_Recv(message,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,i,MSG_NONE,MPI_COMM_WORLD,&st
atus);
#ifdef _DEBUG
printf("S_main (%d): ",i);
printPacket(message);
#endif
#endif
tipomsg = (int) message[0];
switch(tipomsg){
case MSG_UPDATE:{
Shared_update(shared,message);
}break;
case MSG_RECVTASK:{
Shared_recvTask(shared,i);
}break;
case MSG_SENDTASK:{
Shared_sendTask(shared,message);
}break;
case MSG_STOP:{
Shared_stop(shared,i);
}break;
case MSG_HAVEWORK:{
Shared_haveWork(shared,i);
}break;
}
}//END: for
}//END: while
}//Fim do algoritmo
shared->timeFinal = time(NULL);
Shared_finalize(&shared);
}
}
// --

// --
// Funções associadas a estrutura de dados Worker
char *Worker_toString(struct Worker *worker){
int i,j;
char *str,*tmp;
str = (char*) malloc(2048*sizeof(char));
tmp = (char*) malloc(80*sizeof(char));
sprintf(str,"#Worker\n\t#num. cidades: %d\n",worker->numcidades);
sprintf(tmp,"\t#distancias: \n"); strcat(str,tmp);
for(i=0;i<worker->numcidades;i++){
sprintf(tmp,"\t\t"); strcat(str,tmp);
for(j=0;j<worker->numcidades;j++){
sprintf(tmp,"%d ",worker->distancias[i][j]); strcat(str,tmp);

17
}
strcat(str,"\n");
}
return str;
}
char *Worker_toPacket(struct Worker *worker){
int i,j,k=2;
char *str = (char*) malloc(MAX_MSG_SIZE*sizeof(char));
str[0] = (char) MSG_NONE;
str[1] = (char) worker->numcidades; //0: número de cidades
for(i=0;i<worker->numcidades;i++)
for(j=0;j<worker->numcidades;j++){
str[2+(worker->numcidades*i)+j] = (char) worker->distancias[i][j];
k++;
}
str[k]=MSG_END;
return str;
}
struct Worker *Worker_toWorker(char *packet){
int i,j;
struct Worker *worker;
worker = (struct Worker*) malloc(sizeof(struct Worker));
worker->numcidades = (int) packet[1];
worker->distancias = (int**) malloc(worker->numcidades*sizeof(int*));
for(i=0;i<worker->numcidades;i++){
worker->distancias[i] = (int*) malloc(worker->numcidades*sizeof(int));
for(j=0;j<worker->numcidades;j++)
worker->distancias[i][j] = (int) packet[2+(worker->numcidades*i)+j];
}
return worker;
}
int Worker_visit(struct Worker *worker,int city,struct Task *task){
int i=0,newlength=0,javisitada=0;
for(i=1;i<task->hops;i++){
if(task->path[i]==city){
newlength = 0;
javisitada = 1;
}
}
if( javisitada == 0 ){
newlength = task->length + worker->distancias[task->path[task->hops-
1]][city];
task->path[task->hops] = city;
}
return newlength;
}
void Worker_main(){
char *sendbuff, *recvbuff;
int i,msgtipo,city,newlength,working=1;
time_t inicio;
#ifdef _MPI
MPI_Status status;
#endif
struct Task *task,*newTask;
struct Worker *worker;
sendbuff = (char*) malloc(MAX_MSG_SIZE*sizeof(char));
recvbuff = (char*) malloc(MAX_MSG_SIZE*sizeof(char));
{//Recebendo dados iniciais do problema (instância de worker)
#ifdef _MPI

MPI_Recv(recvbuff,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,RANK_SERVER,MSG_NONE,MPI_COM

18
M_WORLD,&status);
#endif
worker = Worker_toWorker(recvbuff);
#ifdef _DEBUG
printf("%s\n",Worker_toString(worker));
#endif
}
while(working==1){
sendbuff[0] = (char) MSG_RECVTASK;
sendbuff[1] = MSG_END;
#ifdef _MPI
#ifdef _DEBUG
printf("W_main: \n");
printPacket(sendbuff);
#endif

MPI_Sendrecv(sendbuff,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,RANK_SERVER,MSG_NONE,

recvbuff,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,RANK_SERVER,MSG_NONE,MPI_COMM_WORLD,&
status);
#ifdef _DEBUG
printPacket(recvbuff);
#endif
#endif
msgtipo = (int) recvbuff[0];
if( msgtipo==MSG_TASK){ //tem tarefa para processar
task = Task_toTask(recvbuff);
#ifdef _DEBUG
printf("W_task: %s\n",Task_toString(task));
#endif
for(city=1;city<task->totalhops;city++){
newlength = Worker_visit(worker,city,task);
if( newlength==0 ){
//não faz nada, cidade já visitada
}else{
if( (task->hops+1)<task->totalhops ){
char *l_sendbuff;
newTask = (struct Task*) malloc(sizeof(struct Task));
newTask->hops = task->hops+1;
newTask->totalhops = task->totalhops;
newTask->length = newlength;
Task_clonePath(&(newTask->path),task->path,task->totalhops);
l_sendbuff = Task_toPacket(newTask);
l_sendbuff[0] = (char) MSG_SENDTASK;
#ifdef _MPI
#ifdef _DEBUG
printf("W_sendtask: ");
printPacket(l_sendbuff);
printf("%s\n",Task_toString(newTask));
#endif

MPI_Send(l_sendbuff,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,RANK_SERVER,MSG_NONE,MPI_C
OMM_WORLD);
#ifdef _DEBUG
printf("sendtask enviado\n");
#endif
#endif
}else{
if( (task->hops+1)==task->totalhops ){
char *l_sendbuff;
newTask = (struct Task*) malloc(sizeof(struct Task));

19
newTask->hops = task->hops+1;
newTask->totalhops = task->totalhops;
Task_clonePath(&(newTask->path),task->path,task->totalhops);
newTask->length = newlength + worker->distancias[newTask-
>path[newTask->totalhops-1]][0];
l_sendbuff = Task_toPacket(newTask);
l_sendbuff[0] = (char) MSG_UPDATE;
#ifdef _MPI
#ifdef _DEBUG
printf("W_update: ");
printPacket(l_sendbuff);
#endif

MPI_Send(l_sendbuff,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,RANK_SERVER,MSG_NONE,MPI_C
OMM_WORLD);
#ifdef _DEBUG
printf("update enviado\n");
#endif
#endif
}
}
}//END: else newlength=0
}//END: for city
}else{ //não tem nenhuma tarefa no momento para processar
sendbuff[0] = (char) MSG_STOP;
sendbuff[1] = MSG_END;
#ifdef _MPI
#ifdef _DEBUG
printf("W_stop: ");
printPacket(sendbuff);
#endif

MPI_Send(sendbuff,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,RANK_SERVER,MSG_NONE,MPI_COM
M_WORLD);
#ifdef _DEBUG
printf("stop enviado ");
#endif
#endif
inicio = time(NULL);
while( time(NULL)-inicio<1 );
sendbuff[0] = (char) MSG_HAVEWORK;
sendbuff[1] = MSG_END;
#ifdef _MPI
#ifdef _DEBUG
printf("W_havetask: ");
printPacket(sendbuff);
#endif

MPI_Sendrecv(sendbuff,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,RANK_SERVER,MSG_NONE,

recvbuff,MAX_MSG_SIZE,MPI_UNSIGNED_CHAR,RANK_SERVER,MSG_NONE,MPI_COMM_WORLD,&
status);
#ifdef _DEBUG
printPacket(recvbuff);
#endif
#endif
msgtipo = (int) recvbuff[0];
if(msgtipo==MSG_WORK) working = 1;
else working = 0;
}
}//END: while

20
}
// --

// --
// Programa principal
int main(int argc,char** argv){
int i;
printf("-----\n");
for(i=0;i<argc;i++)
printf("%d: %s\n",i,argv[i]);
printf("-----\n");
{
int mpi_myrank; //número do processo atual
int mpi_numprocess; //número de processos rodando
#ifdef _MPI
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&mpi_myrank);
MPI_Comm_size(MPI_COMM_WORLD,&mpi_numprocess);
#endif
if( mpi_myrank==RANK_SERVER ){
Shared_main(argv[1],mpi_numprocess-1);
}else{
Worker_main();
}
#ifdef _MPI
MPI_Finalize();
#endif
}
return 0;
}
// --

21

Você também pode gostar