Você está na página 1de 279

Computação Paralela e Distribuída

Profa. Cristina Boeres e Lúcia Drummond

Rio de Janeiro - 26 de fevereiro de 2007

Em convênio com:

IC – Instituto de Computação
UFF – Universidade Federal Fluminense
Introdução e Conceitos Básicos

• Por que computação paralela e distribuída


• Computação de Alto Desempenho
• Arquitetura de computadores
• Ambientes de programação paralela
• Modelos de programação paralela
Por que computação paralela e distribuída?

Sistemas de computadores seqüenciais cada vez mais velozes


– velocidade de processador
– memória
– comunicação com o mundo externo

Quanto mais se tem, mais se quer......


– Demanda computacional está aumentando cada vez mais: visualização, base de
dados distribuída, simulações, etc.

limites em processamento seqüencial


– velocidade da luz, termodinâmica
Por que computação paralela e distribuída?

que tal utilizar vários processadores?

dificuldades encontradas
– mas como?
– paralelizar uma solução?

E x ist e m v á rio s d e sa f io s e m C o m p u t a ç ã o P a ra le la e D ist rib u íd a


Computação de Alto Desempenho
Os grandes desafios (Levin 1989):
– química quântica, mecânica estatística e física relativista;
– cosmologia e astrofísica;
– dinâmica e turbulência computacional dos fluídos;

– projeto de materiais e supercondutividade;


– biologia, farmacologia, seqüência de genomas, engenharia genética, dobramento
de proteínas, atividade enzimática e modelagem de células;

– medicina, modelagem de órgãos e ossos humanos;

– clima global e modelagem do ambiente


Computação de Alto Desempenho

utilizando modelagem, simulação e análise computacional

Aerospace
Life Sciences Internet & Ecommerce

CAD/CAM Digital Biology Military Applications


cortesia de RajKumar Buyya
Definindo melhor alguns conceitos
• Concorrência
 termo mais geral, um programa pode ser constituído por mais de um
thread/processo concorrendo por recursos

• Paralelismo
 uma aplicação é executada por um conjunto de processadores em um ambiente
único (dedicados)

• Computação distribuída
 aplicações sendo executadas em plataformas distribuídas
Definindo melhor alguns conceitos

Qualquer que seja o conceito, o que queremos?

 estabelecer a solução do problema


 lidar com recursos independentes
 aumentar desempenho e capacidade de memória
 fazer com que usuários e computadores trabalhem em espírito de
colaboração
O que paralelizar?

Concorrência pode estar em diferentes níveis de sistemas computacionais


atuais
– hardware
– Sistema Operacional
– Aplicação

As principais questões que são focadas são


– Desempenho
– Corretude
– possibilidade de explorar o paralelismo
Por que paralelizar?

Aplicação Paralela

– várias tarefas

– vários processadores
• redução no tempo total de execução
Modelos de Programação Paralela

Criação e gerenciamento de processos


– estático ou dinâmico

Comunicação
– memória compartilhada
• visão de um único espaço de endereçamento global
– memória distribuída
• troca explícita de mensagens
Modelos de Programação Paralela

Expressão de Paralelismo: Paradigmas


– SPMD
– MPMD

Metas
– aumento no desempenho
– maior eficiência
Objetivos

Visão geral
– arquitetura de computadores
– ambientes de programação paralela
– modelos de programação paralela

Motivar ⇒ Sistemas de Alto Desempenho


Arquitetura de Computadores

Classificação de Computadores

– Computadores Convencionais

– Memória Centralizada

– Memória Distribuída
Arquitetura de Computadores

Sistema Paralelo

– vários processadores

– vários módulos de memória

– comunicação: estruturas de interconexão


Plataforma de Execução Paralela

Conectividade ⇒ rede de interconexão

Heterogeneidade ⇒ hardware e software distintos

Compartilhamento ⇒ utilização de recursos

Imagem do sistema ⇒ como usuário o percebe

Escalabilidade ⇒ + nós > desempenho/eficiência


Classificação de Sistemas Paralelos

Proposta por Flynn


– quantidade de instruções e dados processados em um determinado momento

SISD (single instruction single data)


– Um contador de programa
– Computadores seqüenciais

SIMD (single instruction multiple data)


– Um contador de programa, uma instrução executada por diversos processadores
sobre diferentes dados
– Computadores
Classificação de Sistemas Paralelos

Proposta por Flynn

MISD (multiple instructions single data)


– Não aplicável

MIMD (multiple instructions multiple data)


– Vários contadores de programa
– Diferentes dados
– Os vários computadores paralelos e distribuídos atuais
Plataforma de Execução Paralela

Diferentes plataformas do MIMD de acordo com os seguintes critérios

– espaço de endereçamento
– mecanismo de comunicação

Podem ser agrupadas em quatro grupos


SMPs (Symmetric MultiProcessors)

MPPs (Massively Parallel Processors)

Cluster ou NOWs (Network Of Worstations)

Grades Computacionais
SMPs

SMPs ou Multiprocessadores

– único espaço de endereçamento lógico


• mecanismo de hardware (memória centralizada)

– comunicação ⇒ espaço de endereçamento compartilhado


• operações de loads e stores
– Acesso a memória é realizada através de leitura (load) e escrita (store),
caracterizando desta forma, a comunicação entre processadores
SMPs

Sistema homogêneo
Compartilhamento
– Compartilhamento total da mesma memória
Uma única cópia do Sistema Operacional
Imagem única do sistema
Excelente conectividade
– fortemente acoplados
Não escalável
Exemplos:
– Sun HPC 10000 (StarFire), SGI Altix, SGI Origin, IBM pSeries, Compac
AlphaServer
SMPs

Multiprocessadores

CPU

Memória CPU

...
CPU
MPPs (Multicomputadores)

Diferem quanto a implementação física


Módulos ou elementos de processamento contendo:
– múltiplos processadores com memória privativa
– computadores completos
Espaço de endereçamento
– não compartilhado - memória distribuída
Comunicação
– troca de mensagens
Rede de interconexão
– diferentes topologias
Fracamente acoplados
Escaláveis
MPPs

Sistema homogêneo ou heterogêneo


Interconexão: redes dedicadas e rápidas
Cada nó executa sua própria cópia do Sistema Operacional
Imagem única do sistema
– visibilidade dos mesmos sistemas de arquivo

Um escalonador de tarefas
– partições diferentes para aplicações diferentes
MPPs

Partições dedicadas a cada aplicação


Aplicações não compartilham recursos
– Pode ocorrer que uma aplicação permaneça em estado de espera

Exemplos:
– Cray T3E, IBM SP2s, clusters montados pelo próprio usuário, com
propósito de ser um MPP
MPPs
 Multicomputadores

Escalonador

CPU CPU CPU


... requisições

Mem. Mem. Mem.


Cluster de computadores ou NOWs

Conjunto de estações de trabalho ou PCs

Interconexão: redes locais

Nós: elementos de processamento = processador + memória

Diferenças em relação a MPPs:


– não existe um escalonador centralizado
– redes de interconexão tendem a ser mais lentas
Cluster de computadores ou NOWs

Resultado das diferenças:


– Cada nó tem seu próprio escalonador local
– Compartilhamento de recursos ⇒ sem partição dedicada a uma aplicação
– Aplicação ⇒ deve considerar impacto no desempenho
⇒ não tem o sistema dedicado

– Possibilidade de compor um sistema de alto desempenho e um baixo custo


(principalmente quando comparados com MPPs).
Cluster ou NOWs

requisições requisições requisições

CPU CPU CPU


...
Mem. Mem. Mem.
Grades Computacionais (Computational Grids)

Utilização de computadores
– independentes
– geograficamente distantes

Diferenças: clusters X grades

– heterogeneidade de recursos
– alta dispersão geográfica (escala mundial)
– compartilhamento
– múltiplos domínios administrativos
– controle totalmente distribuído
Grades Computacionais
Componentes
– PCs, SMPs, MPPs, clusters
– controlados por diferentes entidades ⇒ diversos domínios administrativos

Não têm uma imagem única do sistema a princípio


– Vários projetos tem proposto o desenvolvimento de middlewares de
gerenciamento ⇒ camada entre a infra-estrutura e as aplicações a serem
executadas na grade computacional

Aplicação deve estar preparada para:


– Dinamismo
– Variedade de plataformas
– Tolerar falhas
Grades Computacionais

Sistema não dedicado e diferentes plataformas


– Usuários da grades devem obter autorização e certificação para acesso aos
recursos disponíveis na grade computacional

Falhas nos recursos tanto de processamento como comunicação são mais


freqüentes que as outras plataformas paralelas
– Mecanismos de tolerância a falhas devem tornar essas flutuações do ambiente
transparente ao usuário

Para utilização eficiente da grade computacional


– Gerenciamento da execução da aplicação através de políticas de escalonamento
da aplicação ou balanceamento de carga
– Escalonamento durante a execução da aplicação se faz necessário devido as
variações de carga dos recursos da grade
Grades Computacionais

usuário usuário usuário

Escalonador Escalonador
de Aplicação de Aplicação

Escalonador Escalonador Escalonador


SMP
de Recursos de Recursos de Recursos

MPP SMP Cluster


Grades Computacionais

Computador
convencional
Workstation MPP
Cluster
Workstation

Internet
SMP

SMP MPP
Servidor
Resumo
Plataformas de Execução Paralela

Características SMPs MPPs NOWs Grids


Conectividade excelente muito boa boa média/ruim
Heterogeneidade nula baixa média alta
Compartilhamento não não sim sim
Imagem do Sistema única comum comum múltipla
Escalabilidade 10 1.000 1.000 100.000
Top500 Supercomputer (atualizada)
Site Computer Procs Year Rmax Rpeak

DOE/NNSA/LLNL BlueGene/L - eServer Blue Gene Solution


1 United States IBM
131072 2005 280600 367000

NNSA/Sandia National Laboratories Red Storm - Sandia/ Cray Red Storm, Opteron 2.4 GHz
2 United States
dual 26544 2006 101400 127411
Cray Inc.

3 IBM Thomas J. Watson Research Center BGW - eServer Blue Gene Solution
40960 2005 91290 114688
United States IBM

4 DOE/NNSA/LLNL
United States
ASC Purple - eServer pSeries p5 575 1.9 GHz
IBM
12208 2006 75760 92781

MareNostrum - BladeCenter JS21 Cluster, PPC 970, 2.3


5 Barcelona Supercomputing Center
Spain
GHz, Myrinet 10240 2006 62630 94208
IBM
NNSA/Sandia National Laboratories
6 United States
Thunderbird - PowerEdge 1850, 3.6 GHz, Infiniband
Dell
9024 2006 53000 64972.8

7 Commissariat a l'Energie Atomique (CEA) Tera-10 - NovaScale 5160, Itanium2 1.6 GHz, Quadrics
9968 2006 52840 63795.2
France Bull SA

NASA/Ames Research Center/NAS Columbia - SGI Altix 1.5 GHz, Voltaire Infiniband
8 United States SGI
10160 2004 51870 60960

STSUBAME Grid Cluster - Sun Fire x4600 Cluster, Opteron


GSIC Center, Tokyo Institute of Technology
9 Japan
2.4/2.6 GHz and ClearSpeed Accelerator, Infiniband 11088 2006 47380 82124.8
NEC/Sun
Oak Ridge National Laboratory Jaguar - Cray XT3, 2.4 GHz
10 United States Cray Inc.
10424 2006 43480 54204.8

Rmax Maximal LINPACK performance achieved Rpeak Theoretical peak performance


GFlpos
Top500 Supercomputer (Máquinas Brasileiras)
Site Computer Procs Year Rmax Rpeak

Petroleum Company (C) xSeries Cluster Xeon 3.06 GHz – Gig-E


273 Brazil IBM
1024 2004 3755 6266.88

Rbwr1 Cluster platform 3000 DL140G3 Xeon 3.06 GHz


275 PETROBRAS
GigEthernet 1300 2004 3739 7956
Brazil
Hewlett-Packard

363 University of San Paulo BladeCenter JS21 Cluster, PPC970, 2.5 GHz, Myrinet
448 2006 3182.38 4480
Brazil IBM

bw7 – Cluster platform 3000 DL140G3 Xeon 3.06 GHz


418 PETROBRAS
GigEthernet 1008 2004 2992 6169
Brazil Hewlett-Packard

Rmax Maximal LINPACK performance achieved Rpeak Theoretical peak performance


GFlpos
Computação em Cluster

• Um conjunto de computadores (PCs)


• não necessariamente iguais  heterogeneidade
• Filosofia de imagem única
• Conectadas por uma rede local

Para atingir tais objetivos, necessidade de uma camada de software ou


middleware
Computação em Grid

• Computação em Cluster foi estendido para computação ao longo dos sites


distribuídos geograficamente conectados por redes metropolitanas

Grid Computing
• Heterogêneos
• Compartilhados
• Aspectos que devem ser tratados
• Segurança
• Falhas de recursos
• Gerenciamento da execução de várias aplicações
Computação em Grid

O sonho do cientista (The Grid Vision)

Computação em Grid adota tanto o nome quanto o conceito semelhantes


aqueles da Rede de Potência Elétrica para capturar a noção ou a visão de:
− Oferecer desempenho computacional eficientemente;
− De acordo com a demanda;
− A um custo razoável;
− Para qualquer um que precisar.

O sucesso da computação em grid depende da comunidade de


pesquisadores
– A possibilidade de construir tal ambiente (hardware e software)
– Necessidade de atingir seus objetivos.
Computação em Grid
SETI@home: Search for Extraterrestrial Intelligence at Home
Computação em Grid

• Grid middlewares: tem como objetivo facilitar a utilização de um ambiente


grid
• APIs para isolar usuários ou programas da complexidade deste ambiente
• Gerenciar esses sistemas automaticamente e eficientemente para executar
aplicações no ambiente grid (grid-enabled applications)

E as aplicações não habilitadas a execução em ambiente grids?


Computação em Grid

Como o usuário (dono da aplicação) escolhe?


Vários middlewares existem, qual o mais apropriado?
Vários estão ainda sendo desenvolvidos
Não há a garantia de suporte
Pouca comparação entre os middlewares, por exemplo, desempenho,
grau de intrusão.
É difícil encontrar grids com o mesmo tipo de software instalado
Algoritmos Paralelos
Definindo melhor alguns conceitos

Processamento Paralelo: processamento de informação concorrente que


pertencem a um ou mais processos que resolvem um único problema.

Processamento Distribuído: processamento de informações em um


sistema cujos recursos estão sendo compartilhados por vários programas

Computador Paralelo: computador de múltiplos processadores capaz de


realizar processamento paralelo

Supercomputador: computador de propósito geral capaz de resolver


problemas em alta velocidade, comparando com outras máquinas da
mesma época.
Terminologia
A vazão de um dispositivo é o número de resultados produzidos por unidade
de tempo. (throughtput)

Speedup (aceleração): razão entre o tempo de execução necessário para o


algoritmo sequencial mais eficiente e o tempo necessário para se realizar a
mesma computação numa máquina paralela

Existem diversas definições de speedup:


– speedup absoluto(n)= T(melhor alg. seq. )/ T( prog // c/ n proc.)
– speedup aproximado(n)= T(prog.// c/1 proc.)/T( prog // c/ n proc.)
– speedup relativo (n)= T(prog. // c/(n-1) proc.)/T(prog.// c/ n proc.)
Computação concorrente

Duas formas de se explorar a concorrência em computação em computação


– Paralelismo de controle e de dados

tempo t

7+3 string
10 +==“casa”?
200 32 +
33 * 329
14
Paralelismo de Controle

alcançado aplicando-se operações diferentes a diferentes dados


simultaneamente.
– fluxo de dados entre processos pode ser arbitrariamente complexo

Exemplo:

– Pipelining: cada estágio trabalha em velocidade plena sobre uma parte particular
da computação. A saída de um estágio é a entrada do estágio seguinte
Paralelismo de Controle

E1 E2 E3 E4
tempo

info 1

info 2 info 1

info 3 info 2 info 1

info 4 info 3 info 2 info 1


Paralelismo de Dados

uso de vários processadores para executar a mesma operação ao mesmo


tempo sobre elementos distintos de dados

Um aumento de k vezes no número de unidades funcionais levaria a um


aumento de k vezes a vazão do sistema

7+3 10 + 200 33 + 329


Aplicações

Aplicações ou programas podem ser executados mais rapidamente,


considerando duas formas de paralelismo

– tratar o programa seqüencial como uma série de tarefas


– cada tarefa = uma instrução ou blocos de instruções

OU

– especificar um programa paralelo, que resolve o problema através da


especificação de tarefas ou processos concorrentes
Exploração de paralelismo

Particionamento
– identificar em um programa, as tarefas que possam ser executadas em
paralelo (simultaneamente em mais de um processador)

– caso extremo: cada linha do programa correspondendo a uma tarefa

um bom desempenho só é alcançado se um número máximo de comandos são


executados simultaneamente

É preciso considerar dependências de dados

problema: se quase todos os comandos são dependentes


Exemplo: programa seqüencial  paralelismo de instruções
program nothing(){
input (A,B,C);
if A>B then {
C=A-B;
output (C);
} else {
C = B-A;
output (A,B,C)
}
A=0; B=1;
for i=1 to 3 {
input(C);
A=A+C;
B=B*C;
}
output (A,B,C);
}
Exemplo

Tarefa T1 Tarefa T2
input (A,B,C); A = 0;
if A>B then{
C=A-B; Tarefa T3
output (C); B=1;
}else{
C = B-A; Tarefa T4
output (A,B,C) for i=1 to 3 {
} input(C);
A=A+C;
B=B*C;
}
output (A,B,C)
Exemplo

T2 1 T3 1
T1
4

T4
16
Exemplo: Soma n números

P r o b l e m a : s o m a r nn ú m e r o s q u a i s q u e r

Programa sequencial:

read (n, vetor);


soma = 0;
for (i = 0; i < n; i++)
soma = soma + vetor[i];

Como paralelizar o problema?


Terminologias
• Um algoritmo é escalável se o nível de paralelismo aumenta no mínimo
linearmente com o tamanho do problema.
• Uma arquitetura é dita escalável se continua a alcançar o mesmo
desempenho por processador, mesmo para problemas maiores, com o
aumento de processadores.

– Se aplicável, o usuário pode resolver problemas maiores no mesmo intervalo de


tempo através da compra de uma máquina paralela com maiores dimensões

Algoritmos paralelos-de-dados são mais escaláveis do que algoritmos com


paralelismo de controle
– o nível de paralelismo de controle é geralmente constante, independente do
tamanho do problema, enquanto o nível do paralelismo de dados é uma função
crescente do tamanho do problema
Convergência entre Computação Paralela & Distribuída

• Existia uma grande dificuldade de manter máquinas paralelas a frente aos


projetos de chip-único
– Computadores paralelos se tornaram mais difíceis de se construir e se usar do
que se esperava.

Motivo desta dificuldade: software


Paralelizar e gerenciar algoritmos paralelos não é uma tarefa fácil
Programação Paralela

• No início dos anos 90: processamento paralelo foi retomado.


• O problema de programação paralela seria bem mais difícil de atacar do
que se imaginava.
• Pesquisa na área de programação em paralelo tem sido um tópico
importante.

Mas ao mesmo tempo....


Internet

• Enquanto muitos estudavam processamento paralelo associado


processadores ligados internamente em uma máquina, um outro grupo se
concentrava em paralelismo externo através de redes de computadores

• a internet possibilitou a especificação de um tipo de paralelismo através de


uma conexões entre processadores
Sistema Paralelo e Distribuído

processamento distribuído renasceu como uma forma de paralelismo mais


lenta

computação distribuída e paralela são dois extremos num espectro de


computação concorrente

Sistema paralelo e distribuído é uma coleção de componentes de hardware


e software que otimizam o desempenho por problema, vazão de múltiplos
problemas e confiabilidade, ou uma combinação destes
Sistema Paralelo e Distribuído

• Problemas: a aglomeração de muitos processadores através de canais


de comunicação: o desempenho pode cair muito.

• Para alcançar o máximo dos sistemas paralelos e distribuídos:


projetistas e desenvolvedores de software precisam compreender a
interação entre hardware e software
Sistema Paralelo e Distribuído

computação distribuída é mais abrangente e universal do que


computação paralela

Paralelismo - forma restrita de computação distribuída

sistema paralelo : voltado à solução de um problema único no menor


tempo possível (otimização)

Computação distribuída é mais geral e reúne outras formas de


otimização...
Paralelismo é interessante ?
Na natureza, os eventos ocorrem em paralelo

programação seqüencial ordena eventos paralelos


essa ordenação dificulta a trabalho do compilador
mas, maior desempenho com o uso de paralelismo
– no entanto, não é trivial atingir alto desempenho em
computadores paralelos

aplicações como os Grandes Desafios são limitados


computacionalmente
Investindo em paralelismo

Dificuldades encontradas

pensar em paralelo é difícil

conhecimento em paralelismo é recente

pouco conhecimento sobre representações abstratas de computações


paralelas
Investindo em paralelismo
Em relação a características da máquina

Relação entre a velocidade do processador


– Elementos de processamento heterogêneos
Desempenho da interconexão de comunicação
– Latência de comunicação
– Congestionamento
– Topologia da rede
Hierarquia de memória
Tendência hoje:
– memória compartilhada distribuída
– superprocessadores distribuídos
Portabilidade

transferência de programas seqüenciais entre diferentes máquinas não é


tão custoso

mesmo não pode ser afirmado para sistemas paralelos

pode significar um re-projeto e re-implementação do software

qual é a melhor arquitetura paralela?


Quais características importantes das aplicações ?

Processos/tarefas

existe comunicação entre essas tarefas


– pode ser vantajoso executar várias tarefas em um só processador
– a tarefa perde o processador quando precisa de um dado a ser
comunicado

paralelismo virtual X real


– Por exemplo: existem 100 processadores disponíveis
– cada processador pode estar executando vários processos: virtualmente
existem um total de 300 processos
Quais características importantes das aplicações ?

difícil definir processos/tarefas totalmente independentes


– comunicação entre processos pode gerar um tráfego de mensagens
pesado

comunicação entre processos:


– troca de mensagens: considera a topologia da rede de interconexão
– memória compartilhada: utilização de semáforos para proteção de
regiões críticas
– direct remote-memory access: existência de processadores dedicados à
comunicação
Modelos de Computação Paralela

interface - uma máquina abstrata

a abstração se faz necessária para facilitar a programação sem se


preocupar com detalhes da máquina
– um modelo deve ser estável para ser um padrão

um modelo contém os aspectos importantes tanto para os projetistas


de software quanto para os da máquina

as decisões de implementações são feitas para cada máquina destino,


não havendo necessidade de se refazer programas
Aspectos Explorados pelo Modelo

Independência da arquitetura

Fácil entendimento
Aspectos Explorados pelo Modelo

Facilidade de programação

O programador não deve se preocupar com detalhes da máquina destino


– modelos abstratos: programação mais fácil

O compilador é que deve traduzir para uma estrutura do programa em


execução considerando o sistema computacional destino
– tarefa do compilador: mais árdua

nível de abstração

nível de máquina
Aspectos Explorados pelo Modelo

O modelo deve ser capaz de oferecer facilidades tais que seja fácil:
Decompor o programa em tarefas paralelas
Mapear as tarefas nos processadores físicos
– custo de comunicação
– heterogeneidade dos processadores
Sincronização entre tarefas: é preciso ter conhecimento do estado global da
estrutura de execução do programa (quando é necessário sincronizar?)
Abstraindo para Programar

Maior facilidade de programação: o esforço intelectual é reduzido quando


nos concentrarmos em "uma coisa de cada vez”
duas dimensões:
– dimensão espacial
– dimensão temporal
Dimensão Espacial
A cada momento, conjuntos de tarefas independentes são
implementadas
– cada tarefa ou processador não sabe o que acontecerá "a seguir"
detalhamento de informações globais levam a uma programação difícil

Dimensão Temporal

programas são composições de ações seqüenciais que preenchem o


sistema computacional como um todo:
pode-se definir com maior conhecimento o que vai acontecer a seguir
Níveis de Paralelismo

Dependendo do nível considerado, a exploração do paralelismo é diferente

nível de aplicações ou fases de aplicações


a nível de tarefas
a nível de instruções - a execução da instrução necessita da busca, análise
e execução propriamente dita
dentro dos circuitos aritméticos
Algoritmos
Quando queremos resolver um problema computacionalmente, temos
que analisar a complexidade deste. No domínio seqüencial, se procura
definir um algoritmo que resolva o problema em tempo mínimo.

Mas quando se tratando de algoritmos paralelos, mais um parâmetro


– número de processadores
– operações independentes devem ser executadas em paralelo.

qual o tamanho dos processos? noção de granulosidade (granularity)


– a razão entre o tempo de computação necessário para executar
uma tarefa e a sobrecarga de comunicação durante essa
computação.
Modelos de Computação
Modelo de Computação Sequencial: von Neumann
plataforma base para que usuários e projetistas
– complexidade de tempo do pior caso: tempo máximo que o
algoritmo pode levar para executar qualquer entrada com n
elementos
– complexidade de tempo esperado: complexidade média
– critério de custo uniforme: qualquer instrução RAM leva uma
unidade de tempo para ser executada e também o acesso a
registradores
Modelo de Computação Paralela
O desempenho do programa paralelo depende de certos fatores
dependentes da máquina:
– grau de concorrência;
– escalonamento e alocação de processadores;
– comunicação e sincronização.
Modelo PRAM – modelo ideal

conjunto de p processadores operando sincronamente sob o controle


de um único relógio, compartilhando um espaço global de memória

é possível que diferentes fluxos de instruções sejam executados


– aspecto não muito explorado pelos algoritmos

algoritmos desenvolvidos para este modelo geralmente são do tipo


SIMD
– todos os processadores executam o mesmo conjunto de
instruções, e ainda a cada unidade de tempo, todos os
processadores estão executando a mesma instrução mas usando
dados diferentes.
Modelo PRAM – modelo ideal

propriedades chaves:
– execução síncrona sem nenhum custo adicional para a
sincronização
– comunicação realizada em uma unidade de tempo, qualquer que
seja a célula de memória acessada
– comunicação é feita usando a memória global
Passo do algoritmo PRAM

fase de leitura: os processadores acessam simultaneamente locais de


memória para leitura. Cada processador acessa no máximo uma
posição de memória e armazena o dado lido em sua memória local

fase de computação: os processadores executam operações


aritiméticas básicas com seus dados locais

fase de gravação: os processadores acessam simultaneamente locais


de memória global para escrita. Cada processador acessa no máximo
uma posição de memória e grava um certo dado que está armazenado
localmente
Modelo PRAM

análise e estudo de algoritmos paralelos


definição de paradigma de programação paralela
avaliação do desempenho desses algoritmos independentemente das
máquinas paralelas
se o desempenho de um algoritmo paralelo para o modelo PRAM não é
satisfatório, então não tem sentido implementá-lo em qualquer que seja a
máquina paralela
se eficiente, no entanto, podemos simulá-lo em uma máquina real :
simulação deve ser eficiente
Padrões de Acesso no Modelo PRAM
Exclusive Read (ER): vários processadores não podem ler ao mesmo
tempo no mesmo local
Exclusive Write (EW): vários processadores não pode escrever no
mesmo local de memória
Concurrent Read (CR): vários processadores podem ler ao mesmo
tempo o mesmo local de memória
Concurrent Write (CW): vários processadores podem escrever no
mesmo local de memória ao mesmo tempo

Combinações são usadas para formar as variantes do PRAM:


EREW, CREW, ERCW e CRCW
Prioridades do CRCW
Para resolver conflitos no caso de vários processadores tentarem escrever
ao mesmo tempo no mesmo local de memória global:

Comum - vários processadores concorrem a escrita no mesmo local de


memória global durante o mesmo instante de relógio - todos devem
escrever o mesmo valor;

Arbitrário - dentre os vários processadores, um é selecionado


arbitrariamente e seu valor armazenado no local de memória
disputado;

Prioridade - dentre os vários processadores, aquele com o menor índice é


escolhido para escrever o seu valor no local concorrido.
Memória Global

P1 P2 P3 P4 Pn
Comunicação em uma máquina PRAM

Comunicação através da memória global: Pi quer passar x para Pj

– Pi escreve x em um local de memória global em um determinado


passo

– Pj pode acessar o dado naquele local no próximo passo


Memória compartilhada

d1

P1 P2 P3 Pn
d1 d2 d3 dn

PASSO 12
Observações

os processadores operam sincronamente: a cada passo, todas os


processadores executam a mesma instrução sobre dados distintos

uma instrução pode ser simplesmente uma operação aritmética ou uma


comparação de dois números

processadores ativos: somente um subconjunto de processadores


executem uma instrução e processadores restantes ficam ociosos/inativos
Exemplo
V ⇒ vetor com n elementos.
x ⇒ um dado valor

Problema: x∈V?
Ambiente: P processadores tipo EREW PRAM

Analisando o problema:

todos os processadores tem que saber o valor de x


não podem acessar a célula de x simultaneamente
depois, cada processador tem que olhar os elementos de V
sinalização da localização do valor x no vetor V
Solução
todos os processadores devem saber sobre x: broadcasting ou difusão

Pior caso deste procedimento log2 P passos


– P1 acessa a memória global:
– P2 comunica com P1 ou seja, de alguma forma, P1 informa x para P2
– P1 e P2 informam x para P3 e P4
– assim por diante

processadores não têm permissão de acesso simultâneo ⇒ gravam x


em lugares distintos: Mi é um dos P locais de memória global
Um vetor M auxiliar é utilizado
Solução do broadcasting (leitura)

P1 lê x
P1 escreve x em M1
P2 lê M1
P2 escreve em M2
P3 e P4 lêem M1 e M2
P3 e P4 escrevem em M3 e M4
P5, P6, P7 e P8 lêem M1, M2, M3 e M4
P5, P6, P7 e P8 escrevem M5, M6, M7 e M8
e assim por diante

a cada passo: duas vezes o número de processadores ativos do passo


anterior podem ler e escrever ⇒ log P passos
Memória compartilhada

x x x x x

M1 M2 M3 M4 M5 M6 M7 M8

P1 P2 P3 P4 P5 P6 P7 P8
x x x x x x

PASSO 1 PASSO 2 PASSO 3 PASSO 4


A Procura
o vetor V é divido em P pedaços: S1, S2, …, SN
– Pi procura por x em Si
– pior caso: n/P passos

Total: log N + n/N passos, no pior caso

Como o algoritmo poderia ser melhorado??


– Definição de uma variável Achou

Com computador mais poderoso ⇒ algoritmo mais rápido.


PRAM mais poderoso: CREW PRAM
para achar x, o algoritmo executa n/P passos
leituras concorrentes são permitidas
– todos os processadores podem acessar x em um passo
– todos os processadores podem consultar Achou em um passo
– mas ao encontrar, o processador tem que atualizar Achou

Quantos passos nas seguintes situações?


– somente um dos elementos tem valor x
– x pode ser um valor repetido em V
• mais de um processador pode atualizar Achou
simultaneamente.
Relações entre Modelos

EREW PRAM ⇒ mais fraco

CREW PRAM ⇒ pode executar EREW na mesma quantidade de tempo


– simplesmente leituras concorrentes não são feitas

CRCW PRAM ⇒ pode executar EREW na mesma quantidade de tempo


– simplesmente leituras concorrentes não são feitas
Simulando Múltiplos Acessos em EREW

um só processador pode acessar a um local de memória a em um


determinado instante

o modelo é bastante flexível


– pode ser executado em qualquer outra PRAM
– permite a simulação de múltiplos acessos mesmo que o espaço de
armazenamento aumente ou o tempo de execução aumente
Simulando Múltiplos Acessos em EREW

Por que a simulação? O simulação pode ser necessária caso uma das
razões aconteça:
– se os computadores paralelos disponíveis são do tipo EREW -
então executar algoritmos tipo: CREW e CRCW através de
simulação

– para computadores paralelos com um grande número de


processadores:
• o número de processadores que podem acessar a um mesmo
local de memória simultaneamente é limitado
Simulando CW comum em um EREW
N acessos simultâneos por um EREW PRAM por N processos no mesmo
local
– leituras simultâneas: valor difundido, conforme já descrito: log N
passos
– escritas simultâneas: procedimento simétrico à difusão

CW comum:
todos processadores podem escrever no mesmo local de memória
global se o valor for o mesmo.
Suponha que Pi queira escrever o valor ai (1≤ i ≤ N)
variável auxiliar para cada processador Pi : bi
Simulando CW comum em um EREW
Dividindo a série ai em dois grupos
compare ai com ai+(N/2)
se forem iguais, Pi seta bi para verdadeiro (1)

Dividindo a série ai em quatro grupos


compare ai com ai+(N/4) e bi com bi i+(N/4)
se forem iguais, Pi seta bi para verdadeiro (1)
Memória compartilhada
a1 a2 a3 a4 a5 a6 a7 a8 .......... b1 b2 b3 b4

5 5 5 5 5 5 5 5 1 1 1 1

P1 P2 P3 P4
=1 ? =1 ? 1= ? 1= ?

= ? = ?
&
1 1
&
= == 1 ? = == 1 ?

= ?
&1
= == 1 ?
Modelos Fortes e Fracos
O que quer dizer mais forte? Se um algoritmo é simulado em um
modelo mais fraco, o número de passos pode aumentar

CR ⇒ N leituras podem ser feitas concorrentemente


ER ⇒ uma leitura é feita por mais de uma passo

[EcKstein,1979][Vishkin,1983] p processadores CRCW com prioridade, é


simulado por um EREW PRAM com complexidade de tempo aumentado
por um fator Θ (log p).

em um CRCW com prioridade, os acessos simultâneos seria imediatos,


mas não no EREW
Algoritmos PRAM

para um problema: se um algoritmo PRAM tem complexidade de


tempo menor que a do algoritmo seqüencial ótimo, então o
paralelismo pode ser usado

Como iniciar um algoritmo PRAM: ativar os P processadores que farão


parte da computação
– os processadores serem ativados um a um
– através do algoritmo de difusão: log P passos

depois da ativação, o algoritmo paralelo pode ser executado


Identificando Paralelismo
paradigmas de computação paralela

algoritmos aqui falados consideram o modelo PRAM. Exemplos:

Árvore Binária: o fluxo de dados (e controle) se dá da raiz até as folhas


– Difusão: a partir de um processador, o fluxo (controle ou dados)
passa para dois processadores e assim, dobrando a cada iteração.
– Divisão e Conquista: um problema é subdividido em subproblemas
cada vez menores

ou contrário, das folhas até a raíz:


– Redução: dado n valores, a operação X é uma binária associativa
Redução: Soma
soma de n elementos: A = < 4, 3, 8, 2, 9, 1, 0, 5, 6, 3, 10, 2, 4, 7, 11, 3>

Soma_PRAM_Pi (){
Para ( 1≤ h ≤ log n  ) faça
se ( i ≤ n/2h  ) faça
A[i] := A[2i] + A[2i -1];
}

A[i] := A[2i] + A[2i -1]; ⇒ leitura: A[2i] e A[2i -1];


computa: A[2i] + A[2i -1];
escreve: A[i]
Memória compartilhada

32
17
4
7 3 10
8 2 10
15
9 1 0
5 5 46
21
6
9 3 10
12 2 25
11
4 7 14
11 3

P1 P2 P3 P4 P5 P6 P7 P8
7 10 10 5 9 12 11 14

17 15 21 25

32 46

78
Redução: Soma

primeiro loop: não há necessidade de mais do que n/2  processadores

processadores acessam dois locais de memória simultaneamente, mas


distintos

processadores escrevem em um local de memória (cada) simultaneamente,


mas distintos

para somar, log n iterações são necessárias, cada uma tem tempo
constante

Complexidade do Algoritmo: O ( log n) com O ( n/2  ) processadores


Noções de Complexidade
Existem algoritmos PRAM cuja complexidade de tempo é menor do que
o algoritmo correspondente seqüencial ótimo, mas podem
desempenhar mais operações do que o seqüencial

Complexidade de tempo do pior caso em função do tamanho da


entrada. Cada passo corresponde:
– uma fase de computação
– uma fase de comunicação

é importante especificar
– o número máximo de processadores usados, como função da
entrada
– o modelo arquitetural sendo usado
Noções de Complexidade
Paralelismo Limitado
algoritmo p-paralelo se implementado em um modelo com p
processadores, fixo
T(n) e P(n): o tempo de execução e a quantidade de processadores do
algoritmo paralelo
se o número de passos é T(n) considerando p processadores, então
esse algoritmo é p computável neste tempo
se T(n) é polinomial e p é limitado superiormente por polinômio, então
o número de processadores é limitado polinomialmente, senão,
ilimitado
Algumas Definições
A - algoritmo paralelo
n - o tamanho da entrada

Custo do Algoritmo Paralelo


produto tempo-processador T(n) × P(n)
– ignora ociosidade de processador

Algoritmo paralelo de custo ótimo: Ts = T(n) × P(n)


– Ts o tempo de execução do melhor algoritmo seqüencial

p < P(n) processadores: cada processador executa sequencialmente o


que P(n)/ p processadores executam
– T(n) × P(n)/p unidades de tempo
Algumas Definições
Speedup , dado o número de processadores p

Se o S(A(n),p) é linear então todos os processadores são


efetivamente utilizados
– difícil de ser alcançado devido a natureza dos algoritmos e do
ambiente computacional paralelo

– difícil decompor o algoritmo em tarefas completamente


independentes, onde cada tarefa leva Ts /p unidades de tempo
para ser executada
Algumas Definições
Eficiência do algoritmo paralelo

razão entre S(A(n),p) e o número de processadores p


E(A(n),p) = S(A(n),p)/p
– mostra como os processadores são efetivamente utilizados: quanto
maior, melhor a utilização de cada processador

se E(A(n),p) = 1 o algoritmo paralelo é de custo ótimo


Algumas Definições

Trabalho de um Algoritmo Paralelo

um algoritmo é descrito como uma seqüência de unidades de tempo, onde


em cada unidade um conjunto de instruções concorrentes

trabalho de um algoritmo paralelo é o número total de operações


executadas, não incluindo os tempos ociosos de certos processadores

são somadas, a cada unidade de tempo, o número de operações


concorrentes podem estar sendo executadas
Exemplo: soma de n elementos
T(n) e P(n): n/2 processadores executam em O(log n) unidades de
tempo
Custo de O(n log n) em O(log n) unidades de tempo

Usando p < P(n) processadores: O(n log n/p)

– 1a unidade de tempo - n/2 operações (somas em paralelo)


– 2a unidade de tempo - n/4 operações (somas em paralelo)
– 3a unidade de tempo - n/8 operações (somas em paralelo)
..............

– j-ésima unidade de tempo - n/2j operações

Total de operações: ∑ O(log n) n/2j = O(n)


Reduzindo o número de processadores

Princípio de Brent

“ Qualquer algoritmo paralelo com complexidade de tempo T(n) usando


um número suficientemente grande de processadores e que ainda
consistindo de O(e) operações elementares, pode ser implementado
em p processadores com complexidade de tempo
O(  e/p + T(n))

P ro v a ??
Total de Operações: e Total de Processadores: p
Número
Unidade de
de Operações
Tempo P1 P2 Pk Pp
1 e1

2 e2

3 . . . . . .
e3

4
. e4
. .
.
.
.
T(n)
eT
 ei    ei    ei 
Tempo Total ≤ ∑ i
 p
  ≤ ∑
i
   + 1
 p
  
 ≤  p  + T (n)
 
Outra vez: soma de n elementos
seja um modelo PRAM com p = 2q ≤ n = 2k processadores: P1,..., Pp
seja l = n/p = 2 k - q
Ps é responsável por A[l(s - 1) + 1] , …. , A[ls]
cada elemento é um vértice de uma árvore binária
o número de computações concorrentes corresponde ao número de
vértices em cada nível dividido pelos processadores disponíveis

Análise do Algoritmo
Seja o algoritmo em que n elementos são somados usando p
processadores (Obs.: O algoritmo só considera o trabalho de um dado
processador Ps:
primeiro passo: O(n/p) unidades de tempo
segundo passo?
Soma de n Elementos (JáJá)
Soma_Paralela_Ps ( A, p ){
for j =1 to l do /* l = n/p */
B(l(s - 1) + j): =A(l(s - 1) + j);

for h = 1 to log n do
if (k - h - q ≥ 0) then
for j = 2k-h-q(s - 1) + 1 to 2k-h-q s do
B(j): = B(2j - 1) + B(2j);
else if (s ≤ 2k-h) then
B(s): = B(2s - 1) + B(2s);
if (s = l) then S: = B(1);
}
Memória compartilhada
32
17
4
7 46
10
15
3 10
21
8 25
2
5 9 12
1 11
0 14
5 6 3 10 2 4 7 11 3

P1 P2 P3 P4
7 10 9 11

10 5 12 14

17 15 21 25

32 46

78
Perguntas:

qual o número de operações?


qual o trabalho?
qual o custo
complexidade?
tipo do PRAM?
teria alguma outra versão com um menor de trabalho ou custo?
qual o número de comunicações?

1 Responda as perguntas considerando as duas versões discutidas na sala de


aula
2 especificar o pseudo-algoritmo da segunda versão discutida.
Pointer Jumping
T= (V,E) : árvore direcionada
odg(v) e idg(v): graus de saída e entrada do vértice v ∈ V
∃ um vértice r tal que
∀ v ∈ V-{r}, odg(v) = 1, odg(r)=0
∀ v ∈ V-{r}, ∃ um caminho de v a r
O vértice r é dita raíz de T

P o in t e r J u m p in g é uma técnica usada para processamento de dados


armazenados em forma de um conjunto de árvores direcionadas
enraizadas
Pointer Jumping - árvore direcionada
Problema
F: floresta de árvores direcionadas enraizadas
especificação: através de um vetor F de n elementos onde
– F(i) = j se (i,j) ∈ E (é um arco) ⇒ j é o pai ou predecessor imediato de i
– F(i) = i se i é a raíz

Problema: determinar a raíz S(j) da árvore contendo o vértice j


Solução Sequencial - resolve o problema em tempo linear:
identificação das raízes: achar todos os vértics v tal que F(v) = v em
O(n)
reversão dos arcos: pois estamos considerando que se (i,j) ∈ E então j
é pai de i em O(n)
execução de uma busca em profundidade ou largura: nesta busca,
podemos saber a partir da raíz r quem são seus descendentes
Solução Paralela
Um algoritmo eficiente foi proposto, sendo um esquema totalmente
diferente do esquema sequencial

inicialmente: ∀ i ∈ V, S(i) é o pai de i

a técnica p o inte r jum p ing consiste em atualizar o sucessor de cada


vértice pelo sucessor do sucessor
– percorrendo desta maneira, corresponde a cada iteração chegar mais e
mais próximo doa raíz da árvore
– a cada iteração, a distância entre o vértice i e seu sucessor S(i) dobra
– o ponto de parada: quando S(i) é a raíz procurada;
Pointer Jumping

início : S[1] = 2 S[2] = 3 S[3] = 4 S[4] = 5 S[5] = 5


1o passo: S[1] = 3 S[2] = 4 S[3] = 5 S[4] = 5 S[5] = 5
2o passo: S[1] = 4 S[2] = 5 S[3] = 5 S[4] = 5 S[5] = 5
3o passo: S[1] = 5 S[2] = 5 S[3] = 5 S[4] = 5 S[5] = 5
……..
k-ésimo passo: S[1] = raíz ⇒ d(1, S[1]) = 2k (término do Algoritmo)
Pointer Jumping

1 2 3 4 5 R
Complexidade e Corretude do Algoritmo
como podemos provar que o algoritmo está correto?
simplesmente definimos um algoritmo e o executamos?
– seja h a altura máxima de uma árvore qualquer na floresta de árvores
enraizadas direcionadas
– por indução em h

Temos que analisar tal algoritmo, considerando a altura da maior


ávore dentre as da floresta: domina o tempo de execução do
algoritmo
– cada passo j, definição de distância entre i e S[i]
dj(i,S[j]) = 2dj-1(i,S[i])
– por indução em k: supondo verdade que dk-1(i,S[i]) = 2k-1
Complexidade e Corretude do Algoritmo

– assim, no passo k
dk(i,S[j]) = 2 dk -1(i,S[i]) = 2 * 2k-1

– logo, por definição da distância máxima, tem-se h = 2k e assim, o número


máximo de iterações é k = O(log h)

em cada iteração, o tempo paralelo é O(1). Temos um total de O(log


h) iterações. Ainda, o número de processadores é n

o algoritmo paralelo não tem custo ótimo. Por que?

é um algoritmo CREW PRAM


Soma de Prefixos
si = x1 ⊗ x2 ⊗ …. ⊗ xi

saída: n elementos : s1, s2, …., sn


Aplicações: concatenação de strings, soma, multiplicação, etc

Algoritmo Sequencial
Soma_Prefixos_Sequencial( x){
s1 := x1 ;
Para i = 2, …., n
si = si-1 ⊗ xi ;
}

Complexidade: O(n)
Soma de Prefixos

Definição

Seja X = { x1, x2, …., xn } e uma operação ⊗ binária e


a operação ⊗ é fechada sobre X ou seja, se xi e xj são elementos de X
então xi ⊗ xj também é
a operação ⊗ é associativa
Soma de Prefixos - pointer jumping

representação dos elementos é feita através de uma árvore


direcionada enraizada
– cada vértice i da árvore tem um peso associado xi
– pointer jumping é usado, armazenando as somas intermediárias a
cada iteração
– como várias árvores podem ser percorridas ao mesmo tempo,
várias seqüências podem ser resolvidas ao mesmo tempo

algoritmo para soma de prefixos: temos a informação de quem é pai


do vértice i, ou seja, F[i]
– em seqüência de n elementos F[i] = i+ 1, i = 1,…., n-1
– a árvore seria uma lista linear
Soma de Prefixo – outro paradigma

si = x1 ⊗ x2 ⊗ …. ⊗ xi

saída: n elementos : s1, s2, …., sn

Paradigma: ávores binárias

A[i] = xi
B[h,j] e C[h,j] onde 1 ≤ j ≤ n/2h (1 ≤ h ≤ log n especifica o nível)
ao final: sj = C[0,j]
Algoritmo Paralelo não Recursivo
Soma_Prefixos_Paralela_nRecursivo( A ){
1. Para 1 ≤ i ≤ n faça em // : B[0,j] := A[j];
2. Para 1 ≤ h ≤ log n faça
2.1 Para 1 ≤ j ≤ n/2h faça em //
2.1.1 B[h,j] := B[h - 1, 2j-1] * B[h - 1, 2j];

3. Para h = log n … 0 faça


3.1 Para 1 ≤ j ≤ n/2h faça em //
3.1.1 se j é par, então C[h,j] := C[h + 1,j/2];
3.1.2 se j == 1, então C[h,1] := B[h,1];
3.1.3 se j é ímpar, então C[h,j] := C[h + 1,(j-1)/2] * B[h,j];
}
P1 P2 P3 P4 P5 P6 P7 P8
B[0,1]=X1 B[0,2]=X2 B[0,3]=X3 B[0,4]=X4 B[0,5]=X5 B[0,6]=X6 B[0,7]=X7 B[0,8]=X8

B[1,1]=B[0,1]+B[0,2 B[1,2]=B[0,3]+B[0,4 B[1,3]=B[0,5]+B[0,6 B[1,4]=B[0,7]+B[0,8


] ] ] ]
X1+X2 X3+X4 X5+X6 X7+X8

B[2,1]=B[1,1]+B[1,2 B[2,2]=B[1,3]+B[1,4
] ]

X1+X2+x3+x4 X5+X6+x7+x8

B[3,1]=B[2,1]+B[2,2
]
X1+X2+...+x7+x8

C[3,1]=B[3,1]
X1+X2+...+x7+x8

C[2,1]=B[2,1] C[2,2]=B[3,1]
X1+X2+x3+x4 X1+X2+...+x7+x8

C[1,1]=B[1,1] C[1,2]=C[2,1] C[1,3]=C[2,1]+B[1,3 C[1,4]=C[2,2]


]
X1+X2 X1+X2+X3+X4 X1+X2+...+X7+X8
X1+X2+...+X5+X6

C[0,1]=B[0,1] C[0,2]=C[1,1] C[0,3]=C[1,1]+B[0,3 C[0,4]=C[1,2] C[0,5]=C[1,2]+B[0,5 C[0,6]=C[1,3] C[0,7]=C[1,3]+B[0,7 C[0,8]=C[1,4]


] ] ]
X1 X1+X2 X1+X2+X3+X4 X1+X2...X5+X6 X1+X2...X7+X8
X1+X2+X3 X1+X2+X3+X4+X5 X1+X2+...+X6+X7

Soma de Prefixos Não Recursivo


Princípio de Brent
tempo de execução do algoritmo // A para P(n) processadores: T(n)
w operações do algoritmo A
tempo paralelo do algoritmo considerando p processadores de acordo
com o princípio de Brent:
Tp(n) = w/p + T

Aplicação do Princípio
necessário saber quantas operações são executadas a cada passo
algoritmo de soma de prefixos com n = 2k - número de passos:
2log n + 2 ⇒ 2k+2
– qual o número de operações?
– qual o custo?
Princípio de Brent
w1,1 : número de operações no passo 1considerando o único loop
w2,m : número de operações executadas no passo 2 na m-ésima iteração
w3,m : número de operações executadas no passo 3 na m-ésima iteração
Então:
w1,1 = n
w2,m = n/2m = 2k /2m para 1 ≤ m ≤ k
w3,m = 2m para 0 ≤ m ≤ k
Assim:
w = w1,1 + ∑ w2,m + ∑ w3,m
w = n + ∑ n/2m + ∑ 2m
w = n + n(1-1/n) + 2n-1 = 4n-2
w = O(n)
Divisão e Conquista
usada quando identificamos problemas que podem ser particionados
em subproblemas menores, que são mais simples de serem resolvidos

– divisão da entrada em partições menores de mesmo tamanho (ou quase)

– resolução recursiva de cada subproblema definido por cada partição

– combinação das soluções de cada subproblema, produzindo a solução do


problema como um todo

tende a ser eficiente se a decisão e resolução podem ser feitas


recursivamente
eficiente no mundo seqüencial
natural na exploração de paralelismo
Divisão e Conquista

Problema da Envoltória Convexa


Seja S = { p1 , p2 , …. , pn } um conjunto de n pontos em um plano, cada um
representado por suas coordenadas (xi, yi). A envoltória convexa planar de S é o
menor polígono convexo que contém todos os pontos de S

observação: um polígono é convexo quando, para qualquer dois pontos,


(xi, yi) ≤ (xj, yj), a reta [(xi, yi),(xj, yj)] está dentro do polígono.

o problema: determinar a lista ordenada de pontos S que formam o


polígono convexo. Essa lista será denotada por CH(S) (convex hull).
Divisão e Conquista
Problema da Envoltória Convexa
é um problema muito importante em geometria planar:
– estatística
– processamento de imagem
– reconhecimento de padrões
– computação gráfica
– problemas geométricos

resolvido sequencialmente através de divisão e conquista O(n log n)


– o algoritmo de ordenação de pontos resolve esse problema mais
eficientemente
Divisão e Conquista Paralelo
Dado um conjunto S de n pontos, sejam:
p ∈ S o ponto com o menor xi
q ∈ S o ponto com o maior xj

ordenação paralela: em O(log n) em uma PRAM EREW com O(n log n )


operações

particionam CH(S) em:


envoltório superior UH(S) = < todos os pontos de p a q pertencentes a
CH(S) seguindo o sentido horário >

envoltório inferior LH(S) = < todos os pontos de q a p pertencentes a


CH(S) seguindo o sentido horário >
O Problema da Envoltória Convexa

CH(S)

envoltória superior
UH(S)

S
p
q

envoltória inferior
LH(S)
Divisão e Conquista Paralelo

p
q
Merging de duas envoltórias superiores

p
q
Merging de duas envoltórias superiores
Tangente Comum Superior
• sequencial – O(log n)
• busca binária

p
q
Algoritmo
Entrada: n pontos ordenados tais que x(p1) < x(p2) < .... <
x(pn)
Saída: UH(S)

4) Se n <= 4 então ache UH(S) por força bruta e retorne


5) S1 = (p1, ..., pn/2) e S2 = (pn/2+1, pn). Compute UH(S1) e
UH(S2) em paralelo, recursivamente
6) Ache a tangente comum superior entre UH(S1) e UH(S2) e
defina UH(S)

8) O(1)
9) T(n/2)
10) TCS(UH(S1), UH(S2)) em O (log n)
Combinar as duas curvas dada a tangente produzindo S
– em O(1) com n processadores  leituras concorrentes
serão realizadas.
Algoritmo

Explicar:
– ComplexidadeT(n) = O(log2 n) com O(n) processadores

Qual o número de operações?


Desempenho de computação paralela

o paralelismo existente na aplicação


decomposição do problema em subproblemas menores
a alocação destes subproblemas aos processadores
o modo de acesso aos dados:
– a existência de uma memória global
– ou distribuída  comunicação por trocas de mensagens

a estrutura de interconexão entre os processadores


a velocidade dos processadores, memórias e rede de interconexão.

Em ambientes distribuídos, a meta é de explorar e tirar proveito ao máximo do potencial


computacional, sendo assim questões relacionadas ao gerenciamento de recursos do
sistema muito importante
Escalonamento de Aplicações

Um algoritmo que estabelece como executar um algoritmo paralelo em um


determinado sistema de computadores
Para implementar um escalonador, o problema tem que ser especificado:
– As características da aplicação devem ser especificadas
– As características cruciais do sistema de processadores em questão
devem ser estabelecidas
Modelagem

Representação do problema

Representação do ambiente de solução

Simplificação, sem omitir as características que afetam o desempenho em


geral
Qual o objetivo?

Escalonar aplicações em um sistema paralelo e distribuído tal que o tempo


de execução seja minimizado

Escalonar aplicações em um número limitado de processadores tal que o


tempo de execução seja minimizado, considerando custo de comunicação

Escalonar aplicações em um número limitado de processadores tal que o


tempo de execução seja minimizado considerando tempos limites (deadline),
considerando custo de comunicação


o objetivo deve estar especificado
Uma Classe de Problemas

Escalonamento de Aplicações em um Sistema de Computadores Distribuídos

classe de aplicações
– especificada pelo modelo da aplicação

a arquitetura enfocada constitui em um sistema de processadores de


memória distribuída que se comunicam por trocas de mensagens
– modelo arquitetural apresenta as características importantes a serem
consideradas
Escalonamento de Aplicações

Alguns conceitos
Tarefa – uma unidade de computação

job = conj. de tarefas com objetivo comum

aplicação paralela  tarefas que seguem


uma ordem parcial
Escalonamento de Aplicações

Escalonamento local X global


global  tarefas são associadas a processadores
 mapeamento, task placement, matching
local  várias tarefas em um processador

No escalonamento global:
- migração – custoso e nem sempre usado (sala contexto, trasfere contexto
para o novo processador, reinicializa tarefa)
- geralmente se refere a balanceamento de carga
Balanceamento de Carga

sender-initiated receiver-initiated

P1 P2 P1 P2
Mais conceitos

preempção  tarefas/jobs em execução podem ser transferidas (migradas)


– mais custos

não preempção  geralmente em relação às tarefas


– tarefas não são interrompidas
– migração somente para tarefas ainda por executar
Escalonamento de Aplicações

Estático
conhecimento de características associadas às aplicações antes da execução
desta (estimativas)
relação de precedência entre os componentes da aplicação

Dinâmico
estimativas são conhecidas antes da execução e não as características
reais.
a especificação do escalonamento é feita ao longo da execução da
aplicação
– balanceamento de carga, por exemplo
Escalonamento Estático de Aplicações

O Problema de Escalonamento de Tarefas em um conjunto de


processadores é, em sua forma geral, NP-completo.

Uma variedade de heurísticas de escalonamento foram propostas,


principalmente para um conjunto de processadores homogêneos,
considerando ou não custo de comunicação associado à troca de
mensagens

Alguns heurísticas que consideram um conjunto de processadores


homogêneos foram analiticamente avaliadas, e foi concluído que
escalonamento produzidos estão dentro de um fator do ótimo.

Algumas instâncias do problema possuem solução ótima


Modelo da Aplicação

uma aplicação da classe de aplicações abordadas é constituída por um


conjunto de tarefas, ordenadas parcialmente por uma relação de
precedência

a classe de aplicações aqui discutida pode ser representada por um grafo


acíclico direcionado (GAD) G = (V,E, ε, ω), onde
– as tarefas da aplicação são representadas pelo conjunto de n vértices
V = { v1 , v2 , ... , vn }
– as relações de precedência que correspondem a dependência de dados
 são representadas pelo conjunto de dados
E = { (vi , vj ) }
Modelo da Aplicação

o peso de computação ε(vi) pode estar associado a cada tarefa de G


(estimativa) correspondendo ao número de unidades de tempo
necessários para executar a tarefa vi
o peso de comunicação ω(vi, vj ) pode estar associado a cada arco (vi, vj )
de G (estimativa) correspondendo à quantidade de dados a serem
enviados da tarefa vi para a vj
seja pred (vi) o conjunto de predecessores imediatos da tarefa vi , ou
seja,
pred (vi) = {vj / (vj ,vi ) ∈ E }
seja succ (vi) o conjunto de sucessores imediatos da tarefa vi , ou seja,
succ (vi) = {vj / (vi , vj ) ∈ E }
Modelo da Arquitetura

definição de características que afetam o desempenho


processadores homogêneos e heterogêneos
número limitado ou não de processadores
memória distribuída ou compartilhada
topologia da rede de interconexão
modelo de comunicação
– latência de comunicação
– sobrecargas de envio e recebimento
– etc
Heurísticas de Escalonamento

Heurísticas de Escalonamento Estático


heurísticas de construção: um algoritmo polinomial no tamanho da
entrada que a cada iteração, especifica para uma tarefa o seu
escalonamento
– tupla (tarefa, processador, tempo de início)
– ao final, uma só solução foi formada
– escalonamento deve ser válido (respeitar as relações de precedência
e as características do modelo arquitetural)
difícil classificação
– técnica empregada
– modelo considerado
Heurísticas de Escalonamento

Heurísticas de Escalonamento Estático


List Scheduling
Algomeração de Tarefas
Minimização de Caminho Crítico
Replicação de Tarefas
List Scheduling

Estratégia (extremamente) gulosa


tradicional e bastante conhecida (utilizada) devido a sua simplicidade
(baixa complexidade)
– escalonamento de instruções em unidades funcionais
– escalonamento em processadores heterogêneos (em número
limitado)
Alguns conceitos importantes
tarefa livre - todos os seus predecessores imediatos já foram
escalonados;
processador ocioso - em um determinado instante tk , não existe
nenhuma tarefa sendo executada neste instante;
List Scheduling Framework

Definição da prioridade das tarefas vi ∈ V;


Enquanto ( existir vi não escalonado ) faça {
vi = a tarefa livre de maior prioridade;
pj = o processador ocioso onde vi começa mais cedo;
escalone vi no processador pj ;
determine as novas tarefas livres;
}
1

pesos de execução em
pesos dos arcos em

0 4

2 8
4
5 2 3 7
1 2
1 3
2
4 6

5 6
5
4

6 3
Um Exemplo de GAD
Custos Unitários

2
1

3 9

4
5

10
6 8
7

11
Programação Distribuída
Introdução e Conceitos Básicos
Conceitos Básicos

Sistema Distribuído:
– Não há compartilhamento de memória
– Troca de informação através de troca de mensagens

Programa Distribuído: Conjunto de processos que trabalham em conjunto


para solucionar um problema

Programas Distribuídos são executados em Sistemas Distribuídos


Conceitos Básicos

Áreas de demanda:
– Otimização combinatória
– Mineração de dados
– Simulações
– Meteorologia
– Bioinformática
– Computação gráfica

Computação intensiva !!
Conceitos Básicos

Seqüencial ≠ Distribuído - Determinação de estado não é trivial

Seqüencial Distribuído

if (x=1) then if (x proc a = 1) and


(y proc b = 2) then

Não é trivial !!!


Conceitos Básicos

Seqüencial ≠ Distribuído - Ausência de uma base de tempo global

Inst 1 Inst 1
Inst 2 Inst 2
... Inst 3

tempo
Inst x msg ...
tempo

... ...
... Inst z
... ...
... ...
Inst y Inst w
Conceitos Básicos

Distribuído ≠ Centralizado - Não determinístico

P(i) P(i)

P(j) P(j)

P(k) P(k)
Modelos de Computação

Assíncrono:
– Sem coordenação global
– Atraso na transmissão das mensagens é finito mas não determinado
– Desenvolvimento mais complexo
– Realístico

Síncrono:
– Com coordenação global
– Comunicação em “pulsos”
– Desenvolvimento mais simples
– Menos realístico
Modelo utilizado

Um programa paralelo distribuído é representado por um grafo não


orientado G = (N,E) onde:
– N : conjunto de nós que representam processos
– n = |N|
– E : conjunto de arestas que representam canais de comunicação
– e = |E|

Para i = 1, 2, 3, ..., n, pi ∈ N é um processo que pode se comunicar


exclusivamente por troca de mensagens com os processos pj tal que (pi, pj)
∈E

Canais de comunicação bidirecionais de capacidade infinita


Métricas para Avaliação

CORRETUDE

Número/Tamanho das mensagens enviadas

Tempo de convergência:
– Síncrono: Nr. de pulsos
– Assíncrono: Maior cadeia de mensagens com relação de “causalidade”
Introdução ao MPI
Introdução

O MPI é um padrão para desenvolvimento de aplicações distribuídas

Disponível na forma de bibliotecas para linguagens C, C++ e Fortran

SPMD – Single Program Multiple Data – todos os processos executam o


MESMO programa

Síntese de diversos sistemas anteriores


Histórico Breve

Desenvolvido por um fórum aberto internacional composto por


representantes da indústria, universidade e entidades governamentais

Influenciado por outras plataformas e comunidades: Zipcode, Chimp, PVM,


Chamaleon e PICL

Padronização iniciada em abril de 1992


Objetivos

Eficiência na comunicação

Ambientes para desenvolvimento e execução heterogêneos

Fácil aprendizado para atuais programadores de aplicações distribuídas


(interface parecida com a do PVM)
Características

Supõe que a subcamada de comunicação é confiável

Garante ordem de entrega das mensagens

Trabalha com o conceito de COMUNICADORES, que definem o universo de


processos envolvidos em uma operação de comunicação

Cada processo ganha uma identificação numérica (rank)


Comandos de incialização e finalização

MPI_Init : Inicializa o ambiente de execução

MPI_Comm_rank: Determina o rank (identificação) do processo no


comunicador

MPI_Comm_size: Determina o número de processos no comunicador

MPI_Finalize: Termina o ambiente de execução


Comunicação Ponto a Ponto

O que acontece se um processo tentar receber uma mensagem que ainda


não foi enviada?

– Função BLOQUEANTE de recebimento (MPI_Recv) : bloqueia a aplicação


até que o buffer de recepção contenha a mensagem

– Função NÃO BLOQUEANTE de recebimento (MPI_Irecv) : retorna um


handle request, que pode ser testado ou usado para ficar em espera
pela chegada da mensagem
MPI_Send

int MPI_Send(void* message, int count, MPI_Datatype


datatype, int dest, int tag, MPI_Comm comm)

message: endereço inicial da informação a ser enviada


count: número de elementos do tipo especificado a enviar
datatype: MPI_CHAR, MPI_INT, MPI_FLOAT, MPI_BYTE,
MPI_LONG, MPI_UNSIGNED_CHAR, etc
dest: rank do processo destino
tag: identificador do tipo da mensagem
comm: especifica o contexto da comunicação e os processos
participantes do grupo. O padrão é MPI_COMM_WORLD
MPI_Recv

int MPI_Recv(void* message, int count,


MPI_Datatype datatype, int source, int tag,
MPI_Comm comm, MPI_Status* status)

message: Endereço inicial do buffer de recepção


count: Número máximo de elementos a serem recebidos
datatype: MPI_CHAR, MPI_INT, MPI_FLOAT, MPI_BYTE,
MPI_LONG, MPI_UNSIGNED_CHAR, etc.
source: rank do processo origem ( * = MPI_ANY_SOURCE)
tag: tipo de mensagem a receber ( * = MPI_ANY_TAG)
comm: comunicador
status: Estrutura com três campos: MPI_SOURCE_TAG, MPI_TAG,
MPI_ERROR
MPI_Irecv

Menos utilizada

Parâmetros iguais ao bloqueante, acrescido de uma estrutura (request) que


armazena informações que possibilitam o bloqueio posterior do processo
usando a função MPI_Wait(&request, &status)
Comunicação Coletiva

mais restritivas que as comunicações ponto a ponto:


– quantidade de dados enviados deve casar exatamente com a
quantidade de dados especificada pelo receptor
– Apenas a versão bloqueante das funções está disponível
– O argumento tag não existe

Todos os processos participantes da comunicação coletiva


chamam a mesma função com argumentos compatíveis
Algumas Funções para Comunicação Coletiva

MPI_Barrier: Bloqueia o processo até que todos os processos


associados ao comunicador chamem essa função

Inst 1 Inst 1 Inst 1


Inst 2 Inst 2 Inst 2
Inst 3 Inst 3 Inst 3
... ... ...
... ... ...
MPI_Barrier(comm) MPI_Barrier(comm) MPI_Barrier(comm)
... ... ...
... ... ...
Algumas Funções para Comunicação Coletiva

MPI_Bcast: Faz a difusão de uma mensagem do processo raiz para


todos os processos associados ao comunicador
P(i) P(j) P(k)

Inst 1
Inst 2
Inst 3
...
...
msg
MPI_Bcast(....)
...
...
Algumas Funções para Comunicação Coletiva
MPI_Reduce: Combina todos os elementos presentes no buffer de cada
processo do grupo usando a operação definida como parâmetro e coloca o
valor resultante no buffer do processo especificado. O exemplo abaixo soma
todas as variáveis “x” armazenando o total na variável “tot” do processo 2.

Processo 0 Processo 1 Processo 2


Inst 1 Inst 1 Inst 1
Inst 2 Inst 2 Inst 2
... ... ...
x=1; x=10; x=3;
MPI_Reduce(&x, &tot, MPI_Reduce(&x, &tot, MPI_Reduce(&x, &tot,
MPI_INT, MPI_INT, MPI_INT,
MPI_SUM, MPI_SUM, MPI_SUM,
2, 2, 2,
comm) comm) comm)
... ... ...
... ... ...
Programa Exemplo (1/3)

#include <stdio.h>
#include <string.h>
#include “mpi.h”

main(int argc, char** argv)


{
int meu_rank, np, origem, destino, tag=0;
char msg[100];
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &meu_rank);
MPI_Comm_size(MPI_COMM_WORLD,&np);
Programa Exemplo (2/3)

if (my_rank != 0) {
sprintf(msg, “Processo %d está vivo!”, meu_rank);
destino = 0;
MPI_Send(msg,
strlen(msg)+1,
MPI_CHAR,
destino,
tag,
MPI_COMM_WORLD);
}
Programa Exemplo (3/3)
else { // if (my_rank == 0)
for (origem=1; origem<np; origem++) {
MPI_Recv(msg,
100,
MPI_CHAR,
origem,
tag,
MPI_COMM_WORLD,
&status);
printf(“%s\n”,msg);
}
}
MPI_Finalize( );
}
Alguns Algoritmos Distribuídos

•Propagação de Informação
•Propagação com Realimentação
•Integral Definida
•Conectividade em Grafos
•Distância Mínima
Grafo para testes

0
int matrizVizinhanca[6][6] = {
{0,1,1,0,0,0},
{1,0,1,1,1,0},
1 2 {1,1,0,0,0,1},
{0,1,0,0,0,0},
{0,1,0,0,0,0},
{0,0,1,0,0,0}
3 4 5 };
Propagação de Informações
Propagação de Informações

0
Nó 0 gera uma informação que
tem de ser encaminhada a todos
os demais.
1 2

3 4 5
Propagação de Informações - Algoritmo

Variáveis:
alcançado = falso

Ação para fonte inicial da informação (inf):


alcançado := verdadeiro;
envie inf a todos os vizinhos;

Ao receber inf
se alcançado = falso;
alcançado := verdadeiro;
envie inf a todos os vizinhos;
Propagação de Informações - Execução

0
Nó 0 gera uma informação e a
encaminha a todos os seus
vizinhos
1 2

3 4 5
Propagação de Informações - Execução

0
Nós 1 e 2 recebem a informação
e a encaminham a todos os seus
vizinhos
1 2

3 4 5
Propagação de Informações - Execução

0
Nós 3, 4 e 5 recebem a
informação e a encaminham a
todos os seus vizinhos
1 2

3 4 5
Propagação de Informações

Será possível aumentar a eficiência do algoritmo?

Será necessária a propagação para TODOS os vizinhos?

Inclusive o remetente da mensagem?

Considere outra possível execução, representada nas próximas telas…


Propagação de Informações - Execução

0
Nó 0 gera uma informação e a
encaminha a todos os seus
vizinhos
1 2

3 4 5
Propagação de Informações - Execução

0
Nós 1 recebe a informação e a
encaminha a todos os seus
vizinhos mas canal de
1 2 comunicação 0 - 2 está muito
lento, e 2 recebe a informação de
1 antes de 0

3 4 5
Propagação de Informação - Complexidades

Mensagens: 2e => O(e)

Tempo: n-1 => O(n)

1 2 3 n
Propagação de Informação - Implementação

#include <stdio.h>
#include <mpi.h>

/*Definir o grafo da aplicação antes de executar*/


int numeroDeTarefas = 6;
int matrizVizinhanca[6][6] = { {0,1,1,0,0,0},
{1,0,1,1,1,0},
{1,1,0,0,0,1},
{0,1,0,0,0,0},
{0,1,0,0,0,0},
{0,0,1,0,0,0}
};
Propagação de Informação - Implementação

/*retorna o número de vizinhos da tarefa myRank*/


int contaNumeroDeVizinhos(int myRank)
{
int i;
int contador = 0;

for (i = 0; i < numeroDeTarefas; i++)


if (matrizVizinhanca[myRank][i] == 1)
contador++;

return contador;
}
Propagação de Informação - Implementação
/*programa principal*/
int main(int argc, char** argv)
{
int i;
int numeroDeVizinhos;
int myRank;
int source;
int tag = 50;
char message[100] = "Oi!";
MPI_Status status;

//inicialização do MPI
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myRank);

numeroDeVizinhos = contaNumeroDeVizinhos(myRank);
Propagação de Informação - Implementação
if (myRank == 0) {
/*enviando para todos os vizinhos de myRank*/
for (i = 0; i < numeroDeTarefas; i++)
if (matrizVizinhanca[myRank][i] == 1)
{
printf("Enviando mensagem para %d\n", i);
MPI_Send(message, strlen(message)+1, MPI_CHAR, i,
tag,
MPI_COMM_WORLD);
}

/*recebendo de todos os vizinhos de myRank*/


for(i = 0; i < numeroDeVizinhos; i++)
{
MPI_Recv(message, 100, MPI_CHAR, MPI_ANY_SOURCE, tag,
MPI_COMM_WORLD, &status);
printf("Recebendo msg de %d\n", status.MPI_SOURCE);
}
}
Propagação de Informação - Implementação
else
{
/*recebendo msg de uma tarefa vizinha qualquer*/
MPI_Recv(message, 100, MPI_CHAR, MPI_ANY_SOURCE, tag,
MPI_COMM_WORLD, &status);
/*enviando para todos os vizinhos de myRank*/
for (i = 0; i < numeroDeTarefas; i++)
if (matrizVizinhanca[myRank][i] == 1)
MPI_Send(message, strlen(message)+1, MPI_CHAR, i, tag,
MPI_COMM_WORLD);
/*recebendo de todos os vizinhos de myRank menos 1*/
for(i = 0; i < (numeroDeVizinhos - 1); i++)
MPI_Recv(message, 100, MPI_CHAR, MPI_ANY_SOURCE, tag,
MPI_COMM_WORLD, &status);
}

// Finalização do MPI
MPI_Finalize();
}
Propagação de Informações com Realimentação
Propagação de Informações com Realimentação

0
Nó 0 gera uma informação que
tem de ser encaminhada a todos
os demais e recebe uma
1 2 confirmação quando a informação
já estiver completamente
disseminada.

3 4 5
Propagação de Informações com Realimentação - Algoritmo
Variáveis
pai := nil;
count := 0;
alcançado := falso;

Ação para fonte inicial da informação (inf):


alcançado := verdadeiro;
envie inf a todos os vizinhos;

Ao receber inf:
count := count +1;
se alcançado = falso
alcançado := verdadeiro;
pai := origem(inf);
envie inf a todos os vizinhos exceto pai;
se count := |vizinhos| e pai ≠ nil
envie inf para pai;
Propagação de Informações com Realimentação - Execução

0
Nó 0 gera uma informação e a
encaminha a todos os seus
vizinhos
1 2

3 4 5
Propagação de Informações com Realimentação - Execução

0
Nós 1 e 2 recebem a informação
e a encaminham a todos os seus
vizinhos, exceto pai
1 2

3 4 5
Propagação de Informações com Realimentação - Execução

0
Nós 3, 4 e 5 recebem a
informação e como são folhas, a
retornam para seus pais.
1 2

3 4 5
Propagação de Informações com Realimentação - Execução

0
Nós 1 e 2 receberam o retorno de
seus filhos e podem informar ao
seus pais
1 2

3 4 5
Propagação de Informações com Realimentação - Execução

0
Nó 0 recebeu o retorno de todos
os seus filhos e pode concluir
que todos os nós do sistema já
1 2 receberam a informação.

3 4 5
Propagação de Informações com Realimentação - Complexidade

Mensagens: 2e => O(e)

Tempo: 2(n-1) => O(n)

1 2 3 n
Propagação de Informações com Realimentação - Implementação

#include <stdio.h>
#include <mpi.h>

/*Definir o grafo da aplicação antes de executar*/


int numeroDeTarefas = 6;
int matrizVizinhanca[6][6] = { {0,1,1,0,0,0},
{1,0,1,1,1,0},
{1,1,0,0,0,1},
{0,1,0,0,0,0},
{0,1,0,0,0,0},
{0,0,1,0,0,0}
};
Propagação de Informações com Realimentação - Implementação

/*retorna o número de vizinhos da tarefa myRank*/


int contaNumeroDeVizinhos(int myRank)
{
int i;
int contador = 0;

for (i = 0; i < numeroDeTarefas; i++)


if (matrizVizinhanca[myRank][i] == 1)
contador++;

return contador;
}
Propagação de Informações com Realimentação - Implementação

/*programa principal*/
int main(int argc, char** argv)
{
int i;
int numeroDeVizinhos;
int myRank;
int source;
int tag = 50;
int pai;
char message[100] = "Oi!";
MPI_Status status;

//inicialização do MPI
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myRank);

numeroDeVizinhos = contaNumeroDeVizinhos(myRank);
Propagação de Informações com Realimentação - Implementação
if (myRank == 0)
{
/*enviando para todos os vizinhos de myRank*/
for (i = 0; i < numeroDeTarefas; i++)
if (matrizVizinhanca[myRank][i] == 1)
{
printf("Enviando mensagem para %d\n", i);
MPI_Send(message, strlen(message)+1, MPI_CHAR, i,
tag,
MPI_COMM_WORLD);
}

/*recebendo de todos os vizinhos de myRank*/


for(i = 0; i < numeroDeVizinhos; i++)
{
MPI_Recv(message, 100, MPI_CHAR, MPI_ANY_SOURCE, tag,
MPI_COMM_WORLD, &status);
printf(“Confirmação do filho %d\n", status.MPI_SOURCE);
}
}
Propagação de Informações com Realimentação - Implementação
else
{
/*recebendo msg de uma tarefa vizinha qualquer*/
MPI_Recv(message, 100, MPI_CHAR, MPI_ANY_SOURCE, tag,
MPI_COMM_WORLD, &status);
pai = status.MPI_SOURCE;

/*enviando para todos os vizinhos de myRank menos seu pai*/


for (i = 0; i < numeroDeTarefas; i++)
if ( (matrizVizinhanca[myRank][i] == 1) && (i != pai) )
MPI_Send(message, strlen(message)+1, MPI_CHAR, i, tag,
MPI_COMM_WORLD);
/*recebendo de todos os vizinhos de myRank menos 1*/
for(i = 0; i < (numeroDeVizinhos - 1); i++)
MPI_Recv(message, 100, MPI_CHAR, MPI_ANY_SOURCE, tag,
MPI_COMM_WORLD, &status);
MPI_Send(message, strlen(message)+1, MPI_CHAR, pai, tag,
MPI_COMM_WORLD);
}
Propagação de Informações com Realimentação - Implementação

//Finalização do MPI
MPI_Finalize();
}
Integral Definida

Método do Trapezóide
Integral Definida

f(x)

∫ f(x) dx
b
a

a b x
Integral Definida – Método do Trapezóide

y
b1=f( x i-1 )
Área do trapézio = h (b1+b2) / 2
b2=f(x i )
f(x) h=(b-a)/n (constante)

Área do i-ésimo trapésio:


(h/2)[ f(xi-1) + f(xi) ]

a xi-1 xi b x

∫ f(x) dx =
b
a

(h/2)[f(x0)+f(x1) ] + (h/2)[f(x1)+f(x2) ] + (h/2)[f(x2)+f(x3)] + ... + (h/2)[f(xn-1)+f(xn)] =


(h/2)[ f(x0) + 2 f(x1) + 2 f(x2) + 2 f(x3) + ... + 2 f(xn-1) + f(xn) ] =
h [ f(x0)/2 + f(xn)/2 + f(x1) + f(x2) + f(x3) + ... + f(xn-1) ]
supondo:
Integral Definida - Algoritmo nrProcessos=3
y nrTrapézios=6
Variáveis:
=>local_n=2
h := (b-a)/nrTrapézios;
local_n := nrTrapézios / nrProcessos;
h f(x)
local_a := a + my_rank * local_n * h;
local_b := local_a + local_n * h;

x
Ação inicial para todos os nós:
x := local_a;

local_b(2)
local_b(0)=local_a(1)

local_b(1)=local_a(2)
local_a(0)
integral :=(f(local_a)+f(local_b))/2.0;
para i variando de 1 até local_n-1 faça
x := x + h;
integral := integral + f(x);
integral := integral * h;
Integral Definida - Algoritmo

se my_rank = 0
total := integral;
para i variando de 1 até nrProcessos – 1
receba valor na variável integral;
total = total + integral;
imprima “Valor calculado: “ + total;
senão
envie integral para nó 0;
Integral Definida - Implementação
#include <stdio.h>
#include <mpi.h>
main(int argc, char** argv) {
int my_rank;
int p; // número de processos
float a=0.0, b=1.0; // intervalo a calcular
int n=1024; // número de trapezóides
float h; // base do trapezóide
float local_a, local_b; // intervalo local
int local_n; // número de trapezóides local
float integral; // integral no meu intervalo
float total; // integral total
int source; // remetente da integral
int dest=0; // destino das integrais (nó 0)
int tag=200; // tipo de mensagem (único)
MPI_Status status;
Integral Definida - Implementação

float calcula(float local_a, float local_b,


int local_n, float h);

MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
MPI_Comm_size(MPI_COMM_WORLD, &p);

h = (b-a) / n;
local_n = n / p;
local_a = a + my_rank * local_n * h;
local_b = local_a + local_n * h;

integral = calcula(local_a, local_b, local_n, h);


Integral Definida - Implementação

if(my_rank == 0) {
total = integral;
for(source=1; source<p; source++) {
MPI_Recv(&integral, 1, MPI_FLOAT, source, tag,
MPI_COMM_WORLD, &status);
total +=integral;
}
} else
MPI_Send(&integral, 1, MPI_FLOAT, dest,
tag, MPI_COMM_WORLD);

if(my_rank == 0) printf(“Resultado: %f\n”, total);


MPI_Finalize();
}
Integral Definida - Implementação
float calcula(float local_a, float local_b,
int local_n, float h) {
float integral;
float x, i;

float f(float x); // função a integrar

integral = ( f(local_a) + f(local_b) ) /2.0;

x = local_a;
for( i=1; i<=local_n-1; i++) {
x += h;
integral += f(x);
}
integral *= h;
return integral;
}
Integral Definida – Implementação

float f(float x) {

float fx; // valor de retorno

// esta é a função a integrar


// exemplo: função quadrática
fx = x * x;

return fx;
}
Conectividade em Grafos
Conectividade em Grafos

1,2,3,4,5
0
O problema trata da desco-
0,2,3,4,5 berta, por cada nó, das
identificações de todos os
1 2 outros nós aos quais está
conectado.
0,1,3,4,5

3 4 5

0,1,2,4,5 0,1,2,3,5 0,1,2,3,4


Conectividade em Grafos - Algoritmo

Variáveis:
initiate = false;
para todos os nós k de N;
parent(k) := nil;
count(k) := 0;
reached(k) := false;

Ação se n ∈ N(0):
initiate := true;
reached(id) := true;
envie id para todos os vizinhos;
Conectividade em Grafos - Algoritmo

Input: id(k) recebido do nó j


se initiated = false
initiated := true;
reached(id) := true;
envie id para todos os vizinhos;
Conectividade em Grafos - Algoritmo

count(k) := count(k) + 1;
se reached(k) = false
reached(k) := true;
parent(k) := j;
para todos os vizinhos exceto parent(k)
envie id(k);

se count(k) = nr de vizinhos de i
se parent(k) ≠ nil
envie id(k) para parent(k)
nó pare cou reach
nt nt ed

Conectividade em Grafos - Execução 0 nil 0 False


1 nil 0 False
2 nil 0 False
3 nil 0 False
4 nil 0 False
nó pare cou reach 0
nt nt ed 5 nil 0 False
0 nil 0 False
nó pare cou reach
1 nil 0 False nt nt ed
2 nil 0 False 0 nil 0 False
3 nil 0 False 1 nil 0 False
4 nil 0 False 2 nil 0 False
5 nil 0 False 1 2
3 nil 0 False
4 nil 0 False
5 nil 0 False

nó pare cou reach nó pare cou reach nó pare cou reach


nt nt ed nt nt ed nt nt ed
0 nil 0 False 0 nil 0 False 0 nil 0 False
1 nil 0 False 1 nil 0 False 1 nil 0 False
3 2 nil 0 False 4 2 nil 0 False 5 2 nil 0 False
3 nil 0 False 3 nil 0 False 3 nil 0 False
4 nil 0 False 4 nil 0 False 4 nil 0 False
5 nil 0 False 5 nil 0 False 5 nil 0 False
nó pare cou reach
nt nt ed

Conectividade em Grafos - Execução 0 nil 0 True


1 nil 0 False
2 nil 0 False
3 nil 0 False
4 nil 0 False
nó pare cou reach 0
nt nt ed 5 nil 0 False
0 nil 0 False
0 0 nó pare cou reach
1 nil 0 True nt nt ed
2 nil 0 False 0 nil 0 False
1 2
3 nil 0 False 1 nil 0 False
1 2
4 nil 0 False 2 nil 0 True
5 nil 0 False 1 2
3 nil 0 False
1
1 4 nil 0 False
2
5 nil 0 False

nó pare cou reach nó pare cou reach nó pare cou reach


nt nt ed nt nt ed nt nt ed
3 4
0 nil 0 False 0 nil 0 False 0 nil 0 False
5
1 nil 0 False 1 nil 0 False 1 nil 0 False
3 2 nil 0 False 4 2 nil 0 False 5 2 nil 0 False
3 nil 0 True 3 nil 0 False 3 nil 0 False
4 nil 0 False 4 nil 0 True 4 nil 0 False
5 nil 0 False 5 nil 0 False 5 nil 0 True
nó pare cou reach
nt nt ed

Conectividade em Grafos - Execução 0 nil 0 True


1 1 1 True
2 2 1 True
3 nil 0 False
4 nil 0 False
nó pare cou reach 0
nt nt ed 5 nil 0 False
0 0 1 True
2 1 nó pare cou reach
1 nil 0 True nt nt ed
2 2 1 True 0 0 1 True
3 3 1 True 2,3,4 1,5 1 1 1 True
0,5
4 4 1 True 0,3,4 2 nil 0 True
5 nil 0 False 1 2
3 nil 0 False
0,2,3 4 nil 0 False
0,2,4
0,1
5 5 1 True

nó pare cou reach nó pare cou reach nó pare cou reach


nt nt ed nt nt ed nt nt ed
1 1
0 nil 0 False 0 nil 0 False 0 nil 0 False
2
1 1 1 True 1 1 1 True 1 nil 0 False
3 2 nil 0 False 4 2 nil 0 False 5 2 2 1 True
3 nil 0 True 3 nil 0 False 3 nil 0 False
4 nil 0 False 4 nil 0 True 4 nil 0 False
5 nil 0 False 5 nil 0 False 5 nil 0 True
nó pare cou reach
nt nt ed

Conectividade em Grafos - Execução 0 nil 0 True


1 1 2 True
2 2 2 True
3 1 1 True
4 1 1 True
nó pare cou reach 0
nt nt ed 5 2 1 True
0 0 2 True
nó pare cou reach
1 nil 2 True 1,5 2,3,4 nt nt ed
2 2 2 True 0 0 2 True
5
3 3 1 True 3,4 1 1 2 True
4 4 1 True 2 nil 1 True
5 2 1 True 1 2
3 1 1 True
5 5 4 1 1 True
3,4
5 5 1 True

nó pare cou reach nó pare cou reach nó pare cou reach


nt nt ed nt nt ed nt nt ed
0,2,4 0,2,3
0 1 1 True 0 1 1 True 0,1 0 2 1 True
1 1 1 True 1 1 1 True 1 2 1 True
3 2 1 1 True 4 2 1 1 True 5 2 2 1 True
3 nil 0 True 3 1 1 True 3 nil 0 False
4 1 1 True 4 nil 0 True 4 nil 0 False
5 nil 0 False 5 nil 0 False 5 nil 0 True
nó pare cou reach
nt nt ed

Conectividade em Grafos - Execução 0 nil 0 True


1 1 2 True
2 2 2 True
3 1 2 True
4 1 2 True
nó pare cou reach 0
nt nt ed 5 2 2 True
0 0 4 True
3,4 5 nó pare cou reach
1 nil 3 True nt nt ed
2 2 4 True 0 0 0 3 True
3 3 2 True 0 1 1 3 True
2 1
4 4 2 True 2 nil 2 True
5 2 2 True 1 2
3 1 2 True
4 1 2 True
5 5 1 True

nó pare cou reach nó pare cou reach nó pare cou reach


nt nt ed nt nt ed nt nt ed
5 5
0 1 1 True 0 1 1 True 3,4 0 2 1 True
1 1 1 True 1 1 1 True 1 2 1 True
3 2 1 1 True 4 2 1 1 True 5 2 2 1 True
3 nil 0 True 3 1 1 True 3 2 1 True
4 1 1 True 4 nil 0 True 4 2 1 True
5 1 1 True 5 1 1 True 5 nil 0 True
nó pare cou reach
nt nt ed

Conectividade em Grafos - Execução 0 nil 2 True


1 1 2 True
2 2 2 True
3 1 2 True
4 1 2 True
nó pare cou reach 0
nt nt ed 5 2 2 True
0 0 4 True
nó pare cou reach
1 nil 4 True nt nt ed
2 2 4 True 0 0 3 True
3 3 3 True 1 1 3 True
5 3,4
4 4 3 True 2 nil 3 True
5 2 4 True 1 2
3 1 3 True
4 1 3 True
5 5 2 True

nó pare cou reach nó pare cou reach nó pare cou reach


nt nt ed nt nt ed nt nt ed
0 1 1 True 0 1 1 True 0 2 1 True
1 1 1 True 1 1 1 True 1 2 1 True
3 2 1 1 True 4 2 1 1 True 5 2 2 1 True
3 nil 0 True 3 1 1 True 3 2 1 True
4 1 1 True 4 nil 0 True 4 2 1 True
5 1 1 True 5 1 1 True 5 nil 0 True
nó pare cou reach
nt nt ed

Conectividade em Grafos - Execução 0 nil 2 True


1 1 2 True
2 2 2 True
3 1 2 True
4 1 2 True
nó pare cou reach 0
nt nt ed 5 2 2 True
0 0 4 True
nó pare cou reach
1 nil 4 True nt nt ed
2 2 4 True 0 0 3 True
3 3 4 True 1 1 3 True
4 4 4 True 2 nil 3 True
5 2 4 True 1 2
3 1 3 True
3 4 4 1 3 True
5
5 5 3 True

nó pare cou reach nó pare cou reach nó pare cou reach


nt nt ed nt nt ed nt nt ed
0 1 1 True 0 1 1 True 0 2 1 True
1 1 1 True 1 1 1 True 1 2 1 True
3 2 1 1 True 4 2 1 1 True 5 2 2 1 True
3 nil 0 True 3 1 1 True 3 2 1 True
4 1 1 True 4 nil 0 True 4 2 1 True
5 1 1 True 5 1 1 True 5 nil 0 True
nó pare cou reach
nt nt ed

Conectividade em Grafos - Execução 0 nil 2 True


1 1 2 True
2 2 2 True
3 1 2 True
4 1 2 True
nó pare cou reach 0
nt nt ed 5 2 2 True
0 0 4 True
nó pare cou reach
1 nil 4 True nt nt ed
2 2 4 True 0 0 3 True
3 3 4 True 1 1 3 True
4 4 4 True 2 nil 3 True
5 2 4 True 1 2
3 1 3 True
4 1 3 True
5 5 3 True

nó pare cou reach nó pare cou reach nó pare cou reach


nt nt ed nt nt ed nt nt ed
0 1 1 True 0 1 1 True 0 2 1 True
1 1 1 True 1 1 1 True 1 2 1 True
3 2 1 1 True 4 2 1 1 True 5 2 2 1 True
3 nil 1 True 3 1 1 True 3 2 1 True
4 1 1 True 4 nil 1 True 4 2 1 True
5 1 1 True 5 1 1 True 5 nil 1 True
Conectividade em Grafos – Implementação

#include <stdio.h>
#include <mpi.h>

int numeroDeTarefas = 8;
int matrizVizinhanca[8][8] = { {0,1,1,0,0,0,0,0},
{1,0,1,1,1,0,0,0},
{1,1,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,1,1},
{0,0,0,0,0,1,0,1},
{0,0,0,0,0,1,1,0},
};
Conectividade em Grafos – Implementação

/*retorna o número de vizinhos da tarefa myRank*/


int contaNumeroDeVizinhos(int myRank)
{
int i;
int contador = 0;

for (i = 0; i < numeroDeTarefas; i++)


if (matrizVizinhanca[myRank][i] == 1)
contador++;

return contador;
}
Conectividade em Grafos – Implementação

int finaliza(int contador[], int numeroDeVizinhos,


int myRank) {
int i, todosiguaisa0=1;

if(contador[myRank]!=numeroDeVizinhos) return 0;

else return 1;
}
Conectividade em Grafos – Implementação
/*programa principal*/
int main(int argc, char** argv)
{
int i, j;
int numeroDeVizinhos;
int myRank;
int source;
int tag = 50;
int pai[numeroDeTarefas];
int contador[numeroDeTarefas];
int reached[numeroDeTarefas];
int id;
int origem;
MPI_Status status;
Conectividade em Grafos – Implementação
//inicialização do MPI
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myRank);

numeroDeVizinhos = contaNumeroDeVizinhos(myRank);

//inicializando variáveis
for (i=0; i<numeroDeTarefas; i++)
{
pai[i]=-1;
contador[i]=0;
reached[i]=0;
}
reached[myRank]=1;
Conectividade em Grafos – Implementação

//enviando minha id para os vizinhos


for (i=0; i<numeroDeTarefas; i++)
{
if (matrizVizinhanca[myRank][i]==1)
MPI_Send(&myRank, 1, MPI_INT, i, tag,
MPI_COMM_WORLD);
}
Conectividade em Grafos – Implementação
while (!finaliza(contador, numeroDeVizinhos, myRank))
{
MPI_Recv(&id, 1, MPI_INT, MPI_ANY_SOURCE, tag,
MPI_COMM_WORLD, &status);
origem=status.MPI_SOURCE;
contador[id]++;
if (reached[id]==0) {
reached[id]=1;
pai[id]=origem;
for (j=0; j<numeroDeTarefas; j++)
if (matrizVizinhanca[myRank][j]==1 && j!=pai[id])
MPI_Send(&id, 1, MPI_INT, j, tag,
MPI_COMM_WORLD);
}
Conectividade em Grafos – Implementação

if (contador[id]==numeroDeVizinhos)
if (pai[id]!=-1)
MPI_Send(&id, 1, MPI_INT, pai[id], tag,
MPI_COMM_WORLD);
}
//imprimindo resultado
printf("processo %d: Elementos conectados: ", myRank);
for (i=0; i<numeroDeTarefas; i++)
if (contador[i]!=0) printf("%d ", i);
printf("\n");
fflush(stdout);
//Finalização do MPI
MPI_Finalize();
}
Distância Mínima

Algoritmo Síncrono
Algoritmo Assíncrono
Distância Mínima
nó dist. first
nó dist. first 1 1 1
0 1 0 2 1 2
2 1 2
Ao final do algoritmo, cada
3 2 1
nó deve conhecer a
3 1 3 0 4 2 1 distância mais curta para
4 1 4 5 2 2 cada um dos demais, e
5 2 2 através de qual vizinho se
atinge outro nó com esta
1 2
distância.

nó dist. first
3 4 5 0 2 2
1 2 2
Por exemplo, estas são
2 3 2 as informações dos nós
3 3 2 0, 1 e 5.
4 3 2
Distância Mínima – Algoritmo Síncrono

Variáveis:
dist(i) := 0;
dist(k) := n para todo nó k ≠ i;
first(k) = nil para todo nó k ≠ i;
set = {id};

Inicio: s=0, msg(0)={}


se n ∈ N(0):
envie set para todos os vizinhos
Distância Mínima – Algoritmo Síncrono

Entrada:
0<s≤n-1,
msg(s) com set`s recebidos de nós n(j);

set := {};
para cada set recebido em msg
para cada id(k) em set
se dist(k)>s
dist(k) := s;
first(k) := n(j);
set := set U {id(k)}
envie set para todos os vizinhos
Distância Mínima – Algoritmo Assíncrono

Variáveis:
dist(i) := 0;
dist(k) := n para todo nó k ≠ i;
first(k) := nil para todo nó k ≠ i;
set := {id};
level(j) := 0 para todos os vizinhos de i;
state := 0;
initiate := false;

Ação se n ∈ N(0):
initiate := true;
envie set para todos os vizinhos;
Distância Mínima – Algoritmo Assíncrono

Input: set(j);
Se initiate = false
initiate := true;
envie set para todos os vizinhos;
se state < n-1
level(j) := state + 1;
para cada id(k) em set
se dist(k) > level(j)
dist(k) := level(j);
first(k) := n(j);
Distância Mínima – Algoritmo Assíncrono

se state < level(j) para todo j vizinho


state := state + 1;
set := {id(k) | dist(k) = state};
envie set para todos os vizinhos;
State=0

nó First Dist Level


0 nil 6 ---
Distância Mínima – Execução 1 nil 6 -1
2 nil 6 -1
3 nil 6 ---
State=0
4 nil 6 ---
nó First Dist Level 0 5 nil 6 --- State=0
0 nil 6 -1
nó First Dist Level
1 nil 6 --- 0 0
2 nil 6 -1 0 nil 6 -1

3 nil 6 -1 1 nil 6 -1
1 2
4 nil 6 -1 2 nil 6 ---
1 2
3 nil 6 ---
5 nil 6 ---
1 2
4 nil 6 ---
1
1 5 nil 6 -1
2

State=0
State=0 State=0
nó First Dist Level
nó First Dist Level nó First Dist Level
3 0 nil 6 --- 4
0 nil 6 --- 0 nil 6 ---
5
1 nil 6 -1
1 nil 6 -1 1 nil 6 ---
3 2 nil 6 --- 4 5
2 nil 6 --- 2 nil 6 -1
3 nil 6 ---
3 nil 6 --- 3 nil 6 ---
4 nil 6 ---
4 nil 6 --- 4 nil 6 ---
5 nil 6 ---
5 nil 6 --- 5 nil 6 ---
State=1

nó First Dist Level


0 nil 6 ---
Distância Mínima – Execução 1 1 1 0
2 2 1 0
3 nil 6 ---
State=1
4 nil 6 ---
nó First Dist Level 0 5 nil 6 --- State=1
0 0 1 0
nó First Dist Level
1 Nil 6 --- 2 1
2 2 1 0 0 0 1 0

3 3 1 0 1 1 1 0
2,3,4 1,5
4 4 1 0 2 nil 6 ---
0,5
0,3,4 3 nil 6 ---
5 nil 6 ---
1 2
4 nil 6 ---
0,2,3 5 5 1 0
0,2,4
0,1

State=1
State=1 State=1
nó First Dist Level
nó First Dist Level nó First Dist Level
1 0 nil 6 --- 1
0 nil 6 --- 0 nil 6 ---
2
1 1 1 0
1 1 1 0 1 nil 6 ---
3 2 nil 6 --- 4 5
2 nil 6 --- 2 2 1 0
3 nil 6 ---
3 nil 6 --- 3 nil 6 ---
4 nil 6 ---
4 nil 6 --- 4 nil 6 ---
5 nil 6 ---
5 nil 6 --- 5 nil 6 ---
State=2

nó First Dist Level


0 nil 6 ---
Conectividade em Grafos - Execução 1 1 1 1
2 2 1 1
3 1 2 ---
State=2
4 1 2 ---
nó First Dist Level 0 5 2 2 --- State=2
0 0 1 1
nó First Dist Level
1 Nil 6 ---
1,5 2,3,4 0 0 6 1
2 2 1 1
3 3 1 1 1 1 6 1
5
3,4
4 4 1 1 2 2 6 ---
3 1 2 ---
5 2 2 ---
1 2
4 1 2 ---
5 5 5 5 6 1
3,4

State=2
State=2 State=2
nó First Dist Level
nó First Dist Level nó First Dist Level
0,2,4 0 1 2 --- 0,2,3
0 1 2 --- 0,1 0 2 2 ---
1 1 2 1
1 1 1 1 1 2 2 ---
3 2 1 6 --- 4 5
2 1 2 --- 2 2 1 1
3 nil 6 ---
3 1 2 --- 3 nil 6 ---
4 1 2 ---
4 nil 6 --- 4 nil 6 ---
5 nil 6 ---
5 nil 6 --- 5 nil 6 ---
State=3

nó First Dist Level


0 nil 6 ---
Conectividade em Grafos - Execução 1 1 1 2
2 2 1 2
3 1 3 ---
State=3
4 1 3 ---
nó First Dist Level 0 5 2 3 --- State=3
0 0 1 2
nó First Dist Level
1 Nil 6 ---
2 2 1 2 Algoritmo prossegue trocando 0 0 6 2

3 3 1 2 mensagens vazias enquanto 1 1 6 2


2 2 6 ---
4 4 1 2
state<n-1 3 1 2 ---
5 2 2 ---
1 2
4 1 2 ---
5 5 6 2

State=3
State=3 State=3
nó First Dist Level
nó First Dist Level nó First Dist Level
0 1 2 ---
0 1 2 --- 0 2 2 ---
1 1 2 2
1 1 1 2 1 2 2 ---
3 2 1 6 --- 4 5
2 1 2 --- 2 2 1 2
3 nil 6 ---
3 1 2 --- 3 nil 6 ---
4 1 2 ---
4 nil 6 --- 4 nil 6 ---
5 nil 6 ---
5 nil 6 --- 5 nil 6 ---
Distância Mínima – Implementação

#include <stdio.h>
#include <mpi.h>

/*Definir o grafo da aplicação antes de executar*/


int numeroDeTarefas = 6;
int matrizVizinhanca[6][6] = { {0,1,1,0,0,0},
{1,0,1,1,1,0},
{1,1,0,0,0,1},
{0,1,0,0,0,0},
{0,1,0,0,0,0},
{0,0,1,0,0,0}
};
Distância Mínima – Implementação

/*retorna o número de vizinhos da tarefa myRank*/


int contaNumeroDeVizinhos(int myRank)
{
int i;
int contador = 0;

for (i = 0; i < numeroDeTarefas; i++)


if (matrizVizinhanca[myRank][i] == 1)
contador++;

return contador;
}
Distância Mínima – Implementação
/*programa principal*/
int main(int argc, char* argv[])
{
int i, j, contador;
int numeroDeVizinhos;
int myRank;
int source;
int tag=50;
int pai;
MPI_Status status;
int origem;
int state;//marca o pulso atual deste processo
int dist[numeroDeTarefas];//distância
int first[numeroDeTarefas];//primeiro nó no caminho
int set[numeroDeTarefas];
int level[numeroDeTarefas];//pulso dos meus vizinhos
Distância Mínima – Implementação
//inicialização do MPI
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myRank);

numeroDeVizinhos = contaNumeroDeVizinhos(myRank);
printf("iniciando...\n");
fflush(stdout);
//inicializando vetor dist e first
for (i=0; i<numeroDeTarefas; i++)
{ dist[i]=numeroDeTarefas;//considerado como infinito
first[i]=-1;
set[i]=0;
level[i]=0;
}
dist[myRank]=0;
set[myRank]=1;
state=0;
Distância Mínima – Implementação

//enviando meu conjunto para os meus vizinhos


for (i = 0; i < numeroDeTarefas; i++)
if (matrizVizinhanca[myRank][i] == 1)
{
//pede aos vizinhos para enviar
MPI_Send(set, numeroDeTarefas, MPI_INT,
i, tag, MPI_COMM_WORLD);
}
Distância Mínima – Implementação
while (state < numeroDeTarefas-1)
{
MPI_Recv(set, numeroDeTarefas, MPI_INT,
MPI_ANY_SOURCE, tag, MPI_COMM_WORLD,
&status);
origem=status.MPI_SOURCE;
level[origem]++;
for (i=0; i<numeroDeTarefas; i++)
{
if (set[i]==1)
{
if (dist[i]>level[origem])
{
dist[i]=level[origem];
first[i]=origem;
}
Distância Mínima – Implementação

//testando se meu state é menor ou igual ao


//level de todos os meus vizinhos
int continua=1;
for (j=0; j<numeroDeTarefas; j++)
if (matrizVizinhanca[myRank][j]==1 &&
state>=level[j])
{
continua=0;
break;
}
Distância Mínima – Implementação
if (continua){
state++;
//se minha distancia até j for igual a state,
//então o adiciono a set
for (j=0; j<numeroDeTarefas; j++){
if (dist[j]==state)
set[j]=1;
}
for (j=0; j<numeroDeTarefas; j++)
if (matrizVizinhanca[myRank][j]==1)
MPI_Send(set, numeroDeTarefas, MPI_INT, j,
tag, MPI_COMM_WORLD);
}
}
}
printf("processo %d: state=%d\n", myRank, state);
fflush(stdout);
}
Distância Mínima – Implementação

//imprimindo as distâncias
printf("processo %d: ", myRank);
for (i=0; i<numeroDeTarefas; i++)
printf("dist(%d)=%d, ", i, dist[i]);
printf("\n");
printf("processo %d: ", myRank);
for (i=0; i<numeroDeTarefas; i++)
printf("first(%d)=%d, ", i, first[i]);
printf("\n");
fflush(stdout);
//Finalização do MPI
MPI_Finalize();
}
Exercício

Desenvolva uma aplicação paralela em MPI para


multiplicar duas matrizes.
Referências
[1] N. MacDonald et alli, Writing Message Passing Programs with MPI,
Edinburgh Parallel Computer Centre Englewood Cliffs, NJ, Prentice--Hall, 1988
[2] P. S. Pacheco, Parallel Programming with MPI, Morgan Kaufman Pub,
1997
[3] Message Passing Interface Forum, MPI: A Message Passing Interface
Standard, International Journal of Supercomputer Applications, vol. 8, n. 3/4,
1994
[4] Valmir C. Barbosa, An Introduction to Distributed Algorithms, MIT Press,
1996
[5] Nancy A. Lynch, Distributed Algorithms, Morgan Kaufman Pub, 1997
[6] J. Jájá, Introduction to Parallel Algorithms, Addison-Wesley, 1992.
[7] M. J. Quinn, Parallel Computing Theory and Practice, 2nd edition,
McGraw-Hill, 1994.
[8] Ian Foster, Designing and Building Parallel Programs, Addison-Wesley,
1995.
[9] S. Akl, Parallel Computation – Models and Methods, Prentice-Hall, 1997.

Você também pode gostar