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 Life Sciences CAD/CAM Aerospace Digital Biology Internet & Ecommerce 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 propósito de ser um MPP montados pelo próprio usuário, com MPPs Multicomputadores Escalonador CPU CPU Mem. Mem. ... CPU Mem. requisições 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 CPU CPU Mem. Mem. ... requisições CPU 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 Escalonador de Aplicação Escalonador de Recursos MPP usuário Escalonador de Aplicação Escalonador de Recursos SMP Escalonador de Recursos Cluster SMP Grades Computacionais Cluster Workstation MPP Computador convencional Workstation Internet SMP Servidor SMP MPP Resumo Plataformas de Execução Paralela Características Conectividade Heterogeneidade Compartilhamento Imagem do Sistema Escalabilidade SMPs excelente nula não única 10 MPPs muito boa baixa não comum 1.000 NOWs boa média sim comum 1.000 Grids média/ruim alta sim múltipla 100.000 Top500 Supercomputer (atualizada) Site Computer Procs Year Rmax Rpeak 1 DOE/NNSA/LLNL United States BlueGene/L - eServer Blue Gene Solution IBM 131072 2005 280600 367000 2 NNSA/Sandia National Laboratories United States Red Storm - Sandia/ Cray Red Storm, Opteron 2.4 GHz dual Cray Inc. 26544 2006 101400 127411 3 IBM Thomas J. Watson Research Center United States BGW - eServer Blue Gene Solution IBM 40960 2005 91290 114688 4 DOE/NNSA/LLNL United States ASC Purple - eServer pSeries p5 575 1.9 GHz IBM 12208 2006 75760 92781 5 Barcelona Supercomputing Center Spain MareNostrum - BladeCenter JS21 Cluster, PPC 970, 2.3 GHz, Myrinet IBM 10240 2006 62630 94208 6 NNSA/Sandia National Laboratories United States Thunderbird - PowerEdge 1850, 3.6 GHz, Infiniband Dell 9024 2006 53000 64972.8 7 Commissariat a l'Energie Atomique (CEA) France Tera-10 - NovaScale 5160, Itanium2 1.6 GHz, Quadrics Bull SA 9968 2006 52840 63795.2 8 NASA/Ames Research Center/NAS United States Columbia - SGI Altix 1.5 GHz, Voltaire Infiniband SGI 10160 2004 51870 60960 9 GSIC Center, Tokyo Institute of Technology Japan STSUBAME Grid Cluster - Sun Fire x4600 Cluster, Opteron 2.4/2.6 GHz and ClearSpeed Accelerator, Infiniband NEC/Sun 11088 2006 47380 82124.8 Oak Ridge National Laboratory United States Jaguar - Cray XT3, 2.4 GHz Cray Inc. 10424 2006 43480 54204.8 10 Rmax Maximal LINPACK performance achieved GFlpos Rpeak Theoretical peak performance Top500 Supercomputer (Máquinas Brasileiras) Site Computer Procs Year Rmax Rpeak 273 Petroleum Company (C) Brazil xSeries Cluster Xeon 3.06 GHz – Gig-E IBM 1024 2004 3755 6266.88 275 PETROBRAS Brazil Rbwr1 Cluster platform 3000 DL140G3 Xeon 3.06 GHz GigEthernet Hewlett-Packard 1300 2004 3739 7956 363 University of San Paulo Brazil BladeCenter JS21 Cluster, PPC970, 2.5 GHz, Myrinet IBM 448 2006 3182.38 4480 418 PETROBRAS Brazil bw7 – Cluster platform 3000 DL140G3 Xeon 3.06 GHz GigEthernet Hewlett-Packard 1008 2004 2992 6169 Rmax Maximal LINPACK performance achieved GFlpos Rpeak Theoretical peak performance 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 + * 329 33 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 4 info 3 info 1 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 input (A,B,C); if A>B then{ C=A-B; output (C); }else{ C = B-A; output (A,B,C) } Tarefa T2 A = 0; Tarefa T3 B=1; Tarefa T4 for i=1 to 3 { input(C); A=A+C; B=B*C; } output (A,B,C) Exemplo T2 1 T3 T1 4 T4 16 1 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 computacionalmente os Grandes Desafios são limitados 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 aritiméticas básicas com seus dados locais executam operações 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 d1 PASSO 12 P2 d2 P3 d3 Pn dn 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 P1 P2 P3 P4 P5 P6 x x x x x x PASSO 1 PASSO 2 PASSO 3 M7 M8 P7 PASSO 4 P8 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 5 5 5 5 5 5 5 P1 = 1 5 P2 =1 ? = & 1 ? = 1 & = .......... b1 b2 b3 b4 == 1 ? ? == 1 ? = 1 P3 =1 = 1 & 1 ? ? == 1 ? 1= 1 ? P4 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 32 78 15 21 46 25 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 Processadores: p Total de Operações: e Unidade de Tempo P1 Pk P2 Pp Número de Operações e1 1 2 e2 . . . . . . 3 e3 4 . e4 . . . . . T(n) Tempo Total ≤ ∑ i ei p ≤ ∑ i ei + 1 p ≤ ei p + T (n) eT 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 P1 10 21 8 25 2 5 9 12 1 P2 11 0 14 5 6 3 10 P3 2 4 7 11 P4 7 10 9 11 10 5 12 14 17 15 32 46 78 21 25 3 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 = (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 T ∀ 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 1o passo: S[1] = 3 2o passo: S[1] = 4 3o passo: S[1] = 5 …….. k-ésimo passo: S[1] S[2] = 3 S[2] = 4 S[2] = 5 S[2] = 5 S[3] = 4 S[3] = 5 S[3] = 5 S[3] = 5 S[4] = 5 S[4] = 5 S[4] = 5 S[4] = 5 S[5] = 5 S[5] = 5 S[5] = 5 S[5] = 5 = 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 B[0,1]=X1 P2 B[0,2]=X2 P3 P4 P5 B[0,3]=X3 B[0,4]=X4 B[0,5]=X5 P6 B[0,6]=X6 P7 B[0,7]=X7 P8 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 C[1,3]=C[2,1]+B[1,3 ] C[1,4]=C[2,2] 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] X1+X2 X1+X2+X3+X4 C[0,1]=B[0,1] C[0,2]=C[1,1] X1 X1+X2 X1+X2+...+X5+X6 C[0,3]=C[1,1]+B[0,3 ] X1+X2+X3 X1+X2+...+X7+X8 C[0,4]=C[1,2] X1+X2+X3+X4 C[0,5]=C[1,2]+B[0,5 ] X1+X2+X3+X4+X5 C[0,6]=C[1,3] X1+X2...X5+X6 Soma de Prefixos Não Recursivo C[0,7]=C[1,3]+B[0,7 ] X1+X2+...+X6+X7 C[0,8]=C[1,4] X1+X2...X7+X8 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) 5) 6) 8) 9) 10) Se n <= 4 então ache UH(S) por força bruta e retorne S1 = (p1, ..., pn/2) e S2 = (pn/2+1, pn). Compute UH(S1) e UH(S2) em paralelo, recursivamente Ache a tangente comum superior entre UH(S1) e UH(S2) e defina UH(S) O(1) T(n/2) 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 receiver-initiated sender-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 4 0 2 8 4 5 1 2 2 3 3 1 4 2 6 5 6 5 4 3 6 7 Um Exemplo de GAD Custos Unitários 0 2 1 3 4 9 5 6 8 7 11 10 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 Inst Inst ... Inst ... ... ... ... Inst 1 2 x y msg Inst 1 Inst 2 Inst 3 ... ... Inst z ... ... Inst w tempo tempo Seqüencial ≠ Distribuído - Ausência de uma base de tempo global 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 2 Inst 3 ... ... MPI_Barrier(comm) ... ... Inst 1 Inst 2 Inst 3 ... ... MPI_Barrier(comm) ... ... Inst 1 Inst 2 Inst 3 ... ... 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) Inst 1 Inst 2 Inst 3 ... ... MPI_Bcast(....) ... ... msg P(k) 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 Inst 1 Inst 2 ... x=1; MPI_Reduce(&x, &tot, MPI_INT, MPI_SUM, 2, comm) ... ... Processo 1 Inst 1 Inst 2 ... x=10; MPI_Reduce(&x, &tot, MPI_INT, MPI_SUM, 2, comm) ... ... Processo 2 Inst 1 Inst 2 ... x=3; MPI_Reduce(&x, &tot, MPI_INT, MPI_SUM, 2, 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 1 3 2 4 5 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 Propagação de Informações 0 1 3 Nó 0 gera uma informação que tem de ser encaminhada a todos os demais. 2 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 1 3 Nó 0 gera uma informação e a encaminha a todos os seus vizinhos 2 4 5 Propagação de Informações - Execução 0 1 3 Nós 1 e 2 recebem a informação e a encaminham a todos os seus vizinhos 2 4 5 Propagação de Informações - Execução 0 1 3 Nós 3, 4 e 5 recebem a informação e a encaminham a todos os seus vizinhos 2 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 1 3 Nó 0 gera uma informação e a encaminha a todos os seus vizinhos 2 4 5 Propagação de Informações - Execução 0 1 3 2 4 5 Nós 1 recebe a informação e a encaminha a todos os seus vizinhos mas canal de comunicação 0 - 2 está muito lento, e 2 recebe a informação de 1 antes de 0 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 1 3 2 4 5 Nó 0 gera uma informação que tem de ser encaminhada a todos os demais e recebe uma confirmação quando a informação já estiver completamente disseminada. 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 1 3 Nó 0 gera uma informação e a encaminha a todos os seus vizinhos 2 4 5 Propagação de Informações com Realimentação - Execução 0 1 3 Nós 1 e 2 recebem a informação e a encaminham a todos os seus vizinhos, exceto pai 2 4 5 Propagação de Informações com Realimentação - Execução 0 1 3 Nós 3, 4 e 5 recebem a informação e como são folhas, a retornam para seus pais. 2 4 5 Propagação de Informações com Realimentação - Execução 0 1 3 Nós 1 e 2 receberam o retorno de seus filhos e podem informar ao seus pais 2 4 5 Propagação de Informações com Realimentação - Execução 0 1 3 2 4 5 Nó 0 recebeu o retorno de todos os seus filhos e pode concluir que todos os nós do sistema já receberam a informação. 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 y f(x) ∫ f(x) dx a b 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 = a b (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: nrProcessos=3 nrTrapézios=6 =>local_n=2 Integral Definida - Algoritmo h f(x) local_b(2) local_b(1)=local_a(2) x local_b(0)=local_a(1) Ação inicial para todos os nós: x := local_a; 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; y local_a(0) Variáveis: h := (b-a)/nrTrapézios; local_n := nrTrapézios / nrProcessos; local_a := a + my_rank * local_n * h; local_b := local_a + local_n * 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) local_n = local_a = local_b = / n; n / p; a + my_rank * local_n * h; 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 0,2,3,4,5 1 2 0,1,3,4,5 3 0,1,2,4,5 4 0,1,2,3,5 5 0,1,2,3,4 O problema trata da descoberta, por cada nó, das identificações de todos os outros nós aos quais está conectado. 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) Conectividade em Grafos - Execução nó pare nt cou nt reach ed 0 nil 0 False 1 nil 0 False 2 nil 0 3 nil 4 5 3 0 nó pare nt cou nt reach ed 0 nil 0 False 1 nil 0 False 2 nil 0 False 3 nil 0 False 4 nil 0 False 5 nil 0 False nó pare nt cou nt reach ed False 0 nil 0 False 0 False 1 nil 0 False nil 0 False 2 nil 0 False nil 0 False 3 nil 0 False 4 nil 0 False 5 nil 0 False 2 1 nó pare nt cou nt reach ed nó pare nt cou nt reach ed nó pare nt cou nt reach 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 2 nil 0 False 2 nil 0 False 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 4 5 Conectividade em Grafos - Execução nó pare nt cou nt reach ed 0 nil 0 False 1 nil 0 True 2 nil 0 False 3 nil 0 False 4 nil 0 False 5 nil 0 False 0 0 3 pare nt cou nt reach ed 0 nil 0 True 1 nil 0 False 2 nil 0 False 3 nil 0 False 4 nil 0 False 5 nil 0 False 0 1 2 1 2 2 1 1 1 3 nó 2 nó pare nt cou nt reach ed 0 nil 0 False 1 nil 0 False 2 nil 0 False 3 nil 0 4 nil 5 nil nó pare nt cou nt reach ed 0 nil 0 False 1 nil 0 False 2 nil 0 True 3 nil 0 False 4 nil 0 False 5 nil 0 False nó pare nt cou nt reach ed 0 nil 0 False 1 nil 0 False 2 nil 0 False nó pare nt cou nt reach ed 0 nil 0 False 1 nil 0 False 2 nil 0 False True 3 nil 0 False 3 nil 0 False 0 False 4 nil 0 True 4 nil 0 False 0 False 5 nil 0 False 5 nil 0 True 4 4 5 5 Conectividade em Grafos - Execução nó pare nt cou nt reach ed 0 0 1 True 1 nil 0 True 2 2 1 True 3 3 1 True 4 4 1 True 5 nil 0 False 0 2 3 pare nt cou nt reach ed 0 nil 0 True 1 1 1 True 2 2 1 True 3 nil 0 False 4 nil 0 False 5 nil 0 False 1 2,3,4 1,5 0,5 0,3,4 2 1 0,2,3 0,2,4 1 nó nó pare nt cou nt reach ed 0 nil 0 False 1 1 1 True 2 nil 0 False 3 nil 0 4 nil 5 nil 0,1 nó pare nt cou nt reach ed 0 0 1 True 1 1 1 True 2 nil 0 True 3 nil 0 False 4 nil 0 False 5 5 1 True nó pare nt cou nt reach ed 0 nil 0 False 1 nil 0 False 2 2 1 True nó pare nt cou nt reach ed 0 nil 0 False 1 1 1 True 2 nil 0 False True 3 nil 0 False 3 nil 0 False 0 False 4 nil 0 True 4 nil 0 False 0 False 5 nil 0 False 5 nil 0 True 1 4 2 5 Conectividade em Grafos - Execução nó pare nt cou nt reach ed 0 0 2 True 1 nil 2 True 2 2 2 True 3 3 1 True 4 4 1 True 5 2 1 True 0 1,5 3 pare nt cou nt reach ed 0 nil 0 True 1 1 2 True 2 2 2 True 3 1 1 True 4 1 1 True 5 2 1 True 2,3,4 5 3,4 2 1 5 0,2,4 nó 5 nó pare nt cou nt reach ed 0 1 1 True 1 1 1 True 2 1 1 True 3 nil 0 4 1 5 nil 3,4 nó pare nt cou nt reach ed 0 0 2 True 1 1 2 True 2 nil 1 True 3 1 1 True 4 1 1 True 5 5 1 True nó pare nt cou nt reach ed 0 2 1 True 1 2 1 True 2 2 1 True nó pare nt cou nt reach ed 0 1 1 True 1 1 1 True 2 1 1 True True 3 1 1 True 3 nil 0 False 1 True 4 nil 0 True 4 nil 0 False 0 False 5 nil 0 False 5 nil 0 True 0,2,3 4 0,1 5 Conectividade em Grafos - Execução nó pare nt cou nt reach ed 0 0 4 True 1 nil 3 True 2 2 4 True 3 3 2 True 4 4 2 True 5 2 2 True 5 3 0 3,4 nó pare nt cou nt reach ed 0 nil 0 True 1 1 2 True 2 2 2 True 3 1 2 True 4 1 2 True 5 2 2 True 5 0 0 2 1 2 1 nó pare nt cou nt reach ed 0 1 1 True 1 1 1 True 2 1 1 True 3 nil 0 4 1 5 1 nó pare nt cou nt reach ed 0 0 3 True 1 1 3 True 2 nil 2 True 3 1 2 True 4 1 2 True 5 5 1 True nó pare nt cou nt reach ed 0 2 1 True 1 2 1 True 2 2 1 True nó pare nt cou nt reach ed 0 1 1 True 1 1 1 True 2 1 1 True True 3 1 1 True 3 2 1 True 1 True 4 nil 0 True 4 2 1 True 1 True 5 1 1 True 5 nil 0 True 5 4 3,4 5 Conectividade em Grafos - Execução nó pare nt cou nt reach ed 0 0 4 True 1 nil 4 True 2 2 4 True 3 3 3 True 4 4 3 True 5 2 4 True 3 0 nó pare nt cou nt reach ed 0 nil 2 True 1 1 2 True 2 2 2 True 3 1 2 True 4 1 2 True 5 2 2 True 5 3,4 2 1 nó pare nt cou nt reach ed 0 0 3 True 1 1 3 True 2 nil 3 True 3 1 3 True 4 1 3 True 5 5 2 True nó pare nt cou nt reach ed nó pare nt cou nt reach ed nó pare nt cou nt reach 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 2 1 1 True 2 1 1 True 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 4 5 Conectividade em Grafos - Execução nó pare nt cou nt reach ed 0 0 4 True 1 nil 4 True 2 2 4 3 3 4 5 0 pare nt cou nt reach ed 0 nil 2 True 1 1 2 True 2 2 2 True 3 1 2 True 4 1 2 True 5 2 2 True nó pare nt cou nt reach ed True 0 0 3 True 4 True 1 1 3 True 4 4 True 2 nil 3 True 2 4 True 3 1 3 True 4 1 3 True 5 5 3 True 2 1 3 3 nó 4 5 nó pare nt cou nt reach ed nó pare nt cou nt reach ed nó pare nt cou nt reach 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 2 1 1 True 2 1 1 True 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 4 5 Conectividade em Grafos - Execução nó pare nt cou nt reach ed 0 0 4 True 1 nil 4 True 2 2 4 3 3 4 5 3 0 nó pare nt cou nt reach ed 0 nil 2 True 1 1 2 True 2 2 2 True 3 1 2 True 4 1 2 True 5 2 2 True nó pare nt cou nt reach ed True 0 0 3 True 4 True 1 1 3 True 4 4 True 2 nil 3 True 2 4 True 3 1 3 True 4 1 3 True 5 5 3 True 2 1 nó pare nt cou nt reach ed nó pare nt cou nt reach ed nó pare nt cou nt reach 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 2 1 1 True 2 1 1 True 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 4 5 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 3 2 1 3 1 3 4 2 1 4 1 4 5 2 2 5 2 2 0 1 3 Ao final do algoritmo, cada nó deve conhecer a distância mais curta para cada um dos demais, e através de qual vizinho se atinge outro nó com esta distância. 2 4 5 nó dist. first 0 2 2 1 2 2 2 3 2 3 3 2 4 3 2 Por exemplo, estas são as informações dos nós 0, 1 e 5. 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 Distância Mínima – Execução State=0 nó First Dist Level 0 nil 6 -1 1 nil 6 --- 2 nil 6 -1 3 nil 6 -1 4 nil 6 -1 5 nil 6 --- 0 0 3 0 3 nil Dist Level 0 nil 6 --- 1 nil 6 -1 2 nil 6 -1 3 nil 6 --- 4 nil 6 --- 5 nil 6 --- 0 2 1 2 2 1 1 2 State=0 First First 1 1 nó nó State=0 Dist 6 Level --- 1 nil 6 -1 2 nil 6 --- 3 nil 6 --- 4 nil 6 --- 5 nil 6 --- 4 4 State=0 nó First Dist Level 0 nil 6 -1 1 nil 6 -1 2 nil 6 --- 3 nil 6 --- 4 nil 6 --- 5 nil 6 -1 State=0 nó First Dist Level nó First Dist Level 0 nil 6 --- 0 nil 6 --- 1 nil 6 -1 1 nil 6 --- 2 nil 6 --- 2 nil 6 -1 3 nil 6 --- 3 nil 6 --- 4 nil 6 --- 4 nil 6 --- 5 nil 6 --- 5 nil 6 --- 5 5 State=1 Distância Mínima – Execução State=1 nó First Dist Level 0 0 1 0 1 Nil 6 --- 2 2 1 0 3 3 1 0 4 4 1 0 5 nil 6 --- 0 2 1 3 0 nil Dist Level 0 nil 6 --- 1 1 1 0 2 2 1 0 3 nil 6 --- 4 nil 6 --- 5 nil 6 --- 1 1,5 0,5 0,3,4 2 1 0,2,3 0,1 State=1 First First 2,3,4 0,2,4 nó nó State=1 Dist 6 Level --- 1 1 1 0 2 nil 6 --- 3 nil 6 --- 4 nil 6 --- 5 nil 6 --- 1 4 State=1 nó First Dist Level 0 0 1 0 1 1 1 0 2 nil 6 --- 3 nil 6 --- 4 nil 6 --- 5 5 1 0 State=1 nó First Dist Level nó First Dist Level 0 nil 6 --- 0 nil 6 --- 1 1 1 0 1 nil 6 --- 2 nil 6 --- 2 2 1 0 3 nil 6 --- 3 nil 6 --- 4 nil 6 --- 4 nil 6 --- 5 nil 6 --- 5 nil 6 --- 2 5 State=2 Conectividade em Grafos - Execução State=2 nó First Dist Level 0 0 1 1 1 Nil 6 --- 2 2 1 1 3 3 1 1 4 4 1 1 5 2 2 --- 0 1,5 0,2,4 3 Dist Level 0 nil 6 --- 1 1 1 1 2 2 1 1 3 1 2 --- 4 1 2 --- 5 2 2 --- 2,3,4 3,4 2 1 5 3,4 State=2 First First 5 5 nó nó State=2 Dist Level 0 1 2 --- 1 1 2 1 2 1 6 --- 3 nil 6 --- 4 1 2 --- 5 nil 6 --- 0,2,3 4 State=2 nó First Dist Level 0 0 6 1 1 1 6 1 2 2 6 --- 3 1 2 --- 4 1 2 --- 5 5 6 1 State=2 nó First Dist Level nó First Dist Level 0 1 2 --- 0 2 2 --- 1 1 1 1 1 2 2 --- 2 1 2 --- 2 2 1 1 3 1 2 --- 3 nil 6 --- 4 nil 6 --- 4 nil 6 --- 5 nil 6 --- 5 nil 6 --- 0,1 5 State=3 Conectividade em Grafos - Execução State=3 nó First Dist Level 0 0 1 2 1 Nil 6 --- 2 2 1 2 3 3 1 2 4 4 1 2 5 2 2 --- 0 First Dist Level 0 nil 6 --- 1 1 1 2 2 2 1 2 3 1 3 --- 4 1 3 --- 5 2 3 --- Algoritmo prossegue trocando mensagens vazias enquanto state<n-1 2 1 State=3 3 nó State=3 nó First Dist Level 0 1 2 --- 1 1 2 2 2 1 6 --- 3 nil 6 --- 4 1 2 --- 5 nil 6 --- 4 State=3 nó First Dist Level 0 0 6 2 1 1 6 2 2 2 6 --- 3 1 2 --- 4 1 2 --- 5 5 6 2 State=3 nó First Dist Level nó First Dist Level 0 1 2 --- 0 2 2 --- 1 1 1 2 1 2 2 --- 2 1 2 --- 2 2 1 2 3 1 2 --- 3 nil 6 --- 4 nil 6 --- 4 nil 6 --- 5 nil 6 --- 5 nil 6 --- 5 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.