• Nenhum resultado encontrado

COMPUTAÇÃO PARALELA NO LNEC

N/A
N/A
Protected

Academic year: 2021

Share "COMPUTAÇÃO PARALELA NO LNEC"

Copied!
44
0
0

Texto

(1)

Lisboa • julho de 2013 CONSELHO DIRETIVO

Núcleo de Tecnologias da Informação em Engenharia Civil

Proc. 1302/044

I&D CONSELHO DIRETIVO

COMPUTAÇÃO PARALELA NO LNEC

Guia introdutório e estudo de caso

(2)

(3)

Resumo

Este trabalho introduz os princ´ıpios da computac¸ ˜ao paralela tendo em vista a sua aplicac¸ ˜ao como ferramenta de computac¸ ˜ao cient´ıfica, em particular para acelerar a execuc¸ ˜ao e alargar o escopo de aplicac¸ ˜ao de programas de c ´alculo cient´ıfico. S ˜ao introduzidos os principais conceitos relativos `a computac¸ ˜ao paralela, as diferentes arquitecturas de m ´aquinas paralelas e os diferentes modelos de paralelizac¸ ˜ao que se podem adoptar, com destaque para o modelo de message-passing e a ferramenta MPI. S ˜ao ainda descritos os m ´etodos de operac¸ ˜ao com o cluster do LNEC, o Medusa, e um estudo de caso que ilustra a aplicac¸ ˜ao de MPI e de uma estrat ´egia de paralelizac¸ ˜ao `a resoluc¸ ˜ao de um problema real.

Abstract

In this work we introduce the fundamentals of parallel computing as a tool for scientific computing,

particularly regarding performance improvement and application scope enhancement of scientific pro-

grams. We introduce the main relevant concepts and architectures in parallel computing, as well as the

distinct parallelization models which may be adopted, highlighting the message-passing approach and

the MPI tool. We also describe standard operational methods for the LNEC cluster, Medusa, along with

a case-study demonstrating the usefulness of using both MPI and a structured parallelization strategy

in solving a real problem.

(4)

´Indice

Resumo . . . . 3

Abstract . . . . 3

Lista de Tabelas . . . . 5

Lista de Figuras . . . . 5

1 Introduc¸ ˜ao 7 2 Computac¸ ˜ao Paralela 9 2.1 Definic¸ ˜ao . . . . 9

2.2 Conceitos . . . . 9

2.2.1 Fundamentos . . . . 9

2.2.2 Arquitetura e comunicac¸ ˜ao . . . . 11

2.2.3 Limites da paralelizac¸ ˜ao . . . . 13

2.2.4 Modelos de paralelizac¸ ˜ao . . . . 15

2.3 Estado da arte . . . . 17

2.4 MPI . . . . 17

2.5 Estrat ´egia de paralelizac¸ ˜ao . . . . 19

2.5.1 Abordagem inicial . . . . 19

2.5.2 Paralelizac¸ ˜ao . . . . 20

2.5.3 Dicas . . . . 22

3 Computac¸ ˜ao Paralela no LNEC 25 3.1 Medusa . . . . 25

4 Estudo de Caso: Interbath 27 4.1 Definic¸ ˜ao do problema . . . . 27

4.2 Ferramentas utilizadas . . . . 28

4.2.1 ParMETIS . . . . 28

4.2.2 K-D Tree . . . . 29

4.2.3 Timing . . . . 30

4.2.4 Debugging . . . . 30

4.2.5 An ´alise . . . . 30

4.3 Paralelizac¸ ˜ao . . . . 31

4.4 Conclus ˜oes . . . . 37

5 Trabalho Futuro 39

Bibliografia 41

(5)

Lista de Tabelas

2.1 Concorr ˆencia entre processos. . . . 10

2.2 Sincronizac¸ ˜ao de processos com uma barreira. . . . 10

2.3 Classificac¸ ˜ao de arquiteturas paralelas. . . . 12

2.4 Speedup em func¸ ˜ao de P. . . . . 13

2.5 Speedup em func¸ ˜ao de N. . . . 14

2.6 N em func¸ ˜ao de P para S = 24. . . . . 14

2.7 Preservac¸ ˜ao da reversibilidade durante a paralelizac¸ ˜ao. . . . 22

3.1 Principais comandos para utilizac¸ ˜ao do Medusa. . . . 26

4.1 Malhas de batimetria utilizadas. . . . 28

4.2 Tempo de execuc¸ ˜ao em func¸ ˜ao da malha de background. . . . 28

4.3 Vers ˜oes paralelas do Interbath. . . . 32

4.4 Paralelismo na vers ˜ao paralela v1. . . . 32

Lista de Figuras 2.1 Mem ´oria partilhada. . . . 12

2.2 Mem ´oria distribu´ıda. . . . 12

4.1 Tempos de execuc¸ ˜ao (v1). . . . 33

4.2 Tempos de execuc¸ ˜ao para a malha BGround2 (v3). . . . . 35

4.3 Tempos de execuc¸ ˜ao para as malhas Batim e BGround2 (v3.2). . . . . 36

4.4 Tempos de execuc¸ ˜ao para as malhas Batim e BGround2 (v3.3 e v3.3.2). . . . . 37

(6)
(7)

Cap´ıtulo 1

Introduc¸ ˜ao

N ˜ao ´e nova a noc¸ ˜ao de que a evoluc¸ ˜ao registada desde a d ´ecada de 70 em mat ´eria de hardware sequencial, que tem dado origem a uma duplicac¸ ˜ao da capacidade de processamento neste tipo de m ´aquinas sensivelmente a cada 18 meses, est ´a a atingir o seu limite. Esta constatac¸ ˜ao, juntamente com fatores pr ´aticos e econ ´omicos, tem estado na origem da tend ˆencia crescente de associac¸ ˜ao de m ´aquinas sequenciais em aglomerados como forma de obter maior capacidade de processamento, e na utilizac¸ ˜ao de computac¸ ˜ao paralela para explorar o potencial destas estruturas. O LNEC deu resposta a essa tend ˆencia com a aquisic¸ ˜ao de um cluster para computac¸ ˜ao paralela, o Medusa, um recurso que n ˜ao ´e ainda aproveitado ao m ´aximo pelos investigadores do laborat ´orio.

Este relat ´orio foi elaborado no contexto da execuc¸ ˜ao de uma bolsa de iniciac¸ ˜ao `a investigac¸ ˜ao no LNEC, na ´area da computac¸ ˜ao paralela, com dois objetivos: em primeiro lugar, pretende reunir os conhecimentos adquiridos durante a execuc¸ ˜ao desta bolsa sob a forma de um guia de iniciac¸ ˜ao `a programac¸ ˜ao paralela no ambiente do LNEC, de forma a permitir que futuros investigadores interessa- dos em paralelizar os seus programas tenham acesso a um recurso introdut ´orio relativamente simples e adaptado ao seu contexto. Este objetivo ´e particularmente importante por duas raz ˜oes: por um lado, o cluster do LNEC para computac¸ ˜ao paralela, o Medusa, ´e relativamente recente e subaproveitado;

por outro lado, a tend ˆencia de crescimento do paradigma paralelo torna expect ´avel que o n ´umero de investigadores com interesse por esta mat ´eria, particularmente em iniciar-se nela, venha a aumentar.

O segundo objetivo consiste em documentar o trabalho realizado at ´e ao momento no ˆambito desta bolsa; em particular, pretende-se descrever com algum detalhe a abordagem a um problema espec´ıfico, o Interbath, tendo em vista a sua paralelizac¸ ˜ao. Os dois prop ´ositos s ˜ao em larga medida coincidentes, sendo a paralelizac¸ ˜ao do Interbath usada como estudo de caso no cap´ıtulo 4.

A estrutura do relat ´orio divide-se essencialmente em tr ˆes secc¸ ˜oes:

• Computac¸ ˜ao Paralela - esta secc¸ ˜ao pretende introduzir os fundamentos b ´asicos do paralelismo, descrever sucintamente o estado da arte e apresentar uma estrat ´egia geral para a paralelizac¸ ˜ao, com base na experi ˆencia do trabalho desenvolvido durante a bolsa.

• Computac¸ ˜ao Paralela no LNEC - aqui pretende-se descrever os recursos de computac¸ ˜ao paralela no LNEC, nomeadamente o Medusa, e introduzir os fundamentos para a sua utilizac¸ ˜ao.

• Estudo de Caso - Interbath - esta parte visa descrever em detalhe o trabalho realizado no ˆambito

da paralelizac¸ ˜ao do programa Interbath, incluindo as ferramentas utilizadas, a abordagem adop-

tada e a an ´alise dos resultados obtidos.

(8)
(9)

Cap´ıtulo 2

Computac¸ ˜ao Paralela

2.1 Definic¸ ˜ao

Define-se computac¸ ˜ao paralela como a utilizac¸ ˜ao simult ˆanea de mais do que um processador (core) para resolver um problema computacional, por oposic¸ ˜ao `a tradicional computac¸ ˜ao em s ´erie em que apenas um processador ´e utilizado em cada momento. A sua aplicabilidade assenta no princ´ıpio de que

´e poss´ıvel subdividir um problema de grandes dimens ˜oes em v ´arios subproblemas de menor dimens ˜ao que podem ser resolvidos concorrentemente. A motivac¸ ˜ao para recorrer a este tipo de computac¸ ˜ao prende-se com duas necessidades fundamentais:

1. Minimizar o tempo de execuc¸ ˜ao

2. Resolver problemas de maiores dimens ˜oes

Estes dois fatores s ˜ao usualmente apontados como os objetivos principais do recurso `a paralelizac¸ ˜ao, como resposta a dois problemas frequentes da computac¸ ˜ao s ´erie: por um lado, a sua excessiva lentid ˜ao no tratamento de problemas de complexidade elevada; por outro, as restric¸ ˜oes que a utilizac¸ ˜ao de uma

´unica m ´aquina coloca em termos de mem ´oria dispon´ıvel e que afetam a sua capacidade de resolver problemas de grandes dimens ˜oes. Consoante o setor de atividade, outras motivac¸ ˜oes podem surgir como relevantes, tais como a reduc¸ ˜ao de despesas (tipicamente relacionadas com energia, e portanto associadas `a reduc¸ ˜ao do tempo de computac¸ ˜ao) ou a necessidade de utilizar recursos n ˜ao locais

1

.

2.2 Conceitos

Existem v ´arios conceitos essenciais associados `a computac¸ ˜ao paralela que ´e importante introduzir.

Nesta secc¸ ˜ao s ˜ao descritos em primeiro lugar os fundamentos da paralelizac¸ ˜ao e a sua terminologia;

posteriormente, s ˜ao apresentadas as diferentes arquiteturas paralelas e os modos de comunicac¸ ˜ao en- tre elas. S ˜ao ainda expostos os limites te ´oricos da paralelizac¸ ˜ao e os diferentes modelos de paralelizac¸ ˜ao usualmente adotados.

2.2.1 Fundamentos

E importante comec¸ar por definir alguns conceitos b ´asicos relacionados com a paralelizac¸ ˜ao, n ˜ao s ´o por ´ serem utilizados repetidamente ao longo deste trabalho mas sobretudo por se tratarem de elementos importantes desta forma de computac¸ ˜ao.

Designamos por processador (ou CPU) a estrutura b ´asica de computac¸ ˜ao, que pode incluir um ou v ´arios cores; um conjunto de processadores, juntamente outras estruturas de base (mem ´oria e in- terface de rede, por exemplo), forma um n ´ o. Em cada processador ´e executado um processo. Um supercomputador ´e tipicamente formado por um conjunto de n ´os, e pode ser um cluster, se esse con- junto for aproximadamente homog ´eneo e estiver localizado no mesmo local, ou uma grid , por exemplo se n ˜ao houver homogeneidade entre os n ´os, caso estes estejam dispersos geograficamente, ou ainda se pertencerem a dom´ınios administrativos distintos.

E frequente introduzir a distinc¸ ˜ao entre dois tipos de paralelismo, ´ data parallelism e task paral- lelism, consoante o m ´etodo de paralelizac¸ ˜ao usado ([1]). No essencial, para cada processador, a

1Por exemplo, no caso de umagridde recursos geograficamente dispersos em que cada unidade disp ˜oe de informac¸ ˜ao n ˜ao acess´ıvel `as restantes.

(10)

execuc¸ ˜ao da mesma tarefa sobre diferentes conjuntos de dados constitui uma forma de data paralle- lism, ao passo que a execuc¸ ˜ao de diferentes tarefas - quer seja sobre conjuntos de dados diferentes ou n ˜ao - se classifica como task parallelism. Na pr ´atica esta distinc¸ ˜ao ´e quase exclusivamente formal, uma vez que o modelo de paralelizac¸ ˜ao adotado reside frequentemente algures num cont´ınuo entre estes dois extremos.

Em qualquer caso, a partir do momento em que mais do que um processador est ´a envolvido na resoluc¸ ˜ao de uma determinada tarefa, ´e praticamente certo que existir ´a necessidade de trocar informac¸ ˜ao entre processadores; para isso, ´e preciso criar a estrutura que permita essa troca de informac¸ ˜ao, ou comunicac¸ ˜ao, entre processadores. Os processos de comunicac¸ ˜ao tendem a aumentar o tempo de execuc¸ ˜ao do programa, pelo que ´e recomend ´avel reduzir o volume total de comunicac¸ ˜oes ao m´ınimo. Neste contexto, o conceito de granularidade, definido (qualitativamente) como r ´acio entre o tempo de CPU dispendido em atividades de computac¸ ˜ao versus de comunicac¸ ˜ao, G =

TTcomp

comm

, surge como uma razo ´avel medida do peso relativo da comunicac¸ ˜ao no programa, que em geral se classifica como de granularidade grosseira caso processe volumes elevados de computac¸ ˜ao entre eventos de comunicac¸ ˜ao (G >> 1), ou de granularidade fina caso esses volumes sejam relativamente pequenos (G ≈ 1).

A concec¸ ˜ao tradicional de um programa assenta numa l ´ogica sequencial - isto ´e, as diversas instruc¸ ˜oes s ˜ao executadas pela mesma ordem em que est ˜ao presentes no c ´odigo-fonte, de forma previs´ıvel e de- terminada. Pelo contr ´ario, num programa em paralelo, os v ´arios processos s ˜ao executados de forma concorrente, n ˜ao havendo maneira de prever qual deles executar ´a uma determinada instruc¸ ˜ao primeiro;

a l ´ogica sequencial ´e preservada apenas localmente (i.e., para cada processo). Por exemplo, o seguinte fragmento de c ´odigo produz, em tr ˆes chamadas diferentes, outros tantos outputs:

if (rank == 0) str=’isto’

if (rank == 1) str=’ ´e’

if (rank == 2) str=’uma’

if (rank == 3) str=’frase’

do i=0,3

if (i == rank) print*, str end do

(a) Input

Run 1 Run 2 Run 3

´e ´e uma

isto uma frase

frase isto ´e uma frase isto

(b) Output

Tabela 2.1: Concorr ˆencia entre processos.

Este tipo de comportamento torna-se problem ´atico quando ´e necess ´ario trocar informac¸ ˜ao entre processos, pois ´e imposs´ıvel saber a priori se essa informac¸ ˜ao estar ´a j ´a dispon´ıvel no processo emissor quando for solicitada pelo recetor. Para evitar esse problema ´e necess ´ario recorrer `a sincronizac¸ ˜ao de processos, ou seja, `a coordenac¸ ˜ao dos processos em tempo real; a soluc¸ ˜ao mais frequente para isso ´e introduzir uma barreira, i.e., uma instruc¸ ˜ao para que o processo espere at ´e que outros processos alcancem esse ponto do programa.

if (rank == 0) str=’isto’

if (rank == 1) str=’ ´e’

if (rank == 2) str=’uma’

if (rank == 3) str=’frase’

do i=0,3

if (i == rank) print*, str call MPI Barrier end do

(a) Input

Run 1 Run 2 Run 3 isto isto isto

´e ´e ´e

uma uma uma

frase frase frase

(b) Output

Tabela 2.2: Sincronizac¸ ˜ao de processos com uma barreira.

A escalabilidade de um programa paralelo define-se como a sua capacidade de, mediante um

aumento dos recursos dispon´ıveis (por exemplo, mais processadores), exibir um aumento proporcional

da efici ˆencia em termos de tempo de execuc¸ ˜ao. No caso ideal de um programa ser perfeitamente

(11)

escal ´avel, ´e de esperar que um aumento do n ´umero de processadores para o dobro reduza o tempo de execuc¸ ˜ao para metade. V ´arios fatores contribuem para a escalabilidade do programa, incluindo especificac¸ ˜oes de hardware e da rede de comunicac¸ ˜ao; de entre aqueles que s ˜ao control ´aveis pelo programador, os mais importantes para garantir m ´axima escalabilidade s ˜ao o volume de comunicac¸ ˜oes, que deve ser mantido no m´ınimo, e a distribuic¸ ˜ao de carga computacional pelos processadores, que deve ser o mais equilibrada poss´ıvel.

Em geral, o tempo de execuc¸ ˜ao de um programa s ´erie diz respeito apenas ao tempo despendido nas tarefas de computac¸ ˜ao. Ao introduzir paralelismo, ser ´a necess ´ario gastar mais tempo na gest ˜ao das tarefas exclusivamente paralelas; a este tempo extra, que n ˜ao est ´a relacionado com a execuc¸ ˜ao de trabalho ´util mas apenas com a gest ˜ao do paralelismo, ´e dado o nome de overhead paralelo. V ´arios fatores podem contribuir para o aumento deste overhead :

• Comunicac¸ ˜oes entre processos - com tend ˆencia crescente para um maior n ´umero de processos;

• Tempo de espera por sincronizac¸ ˜oes;

• Inicializac¸ ˜ao e encerramento do ambiente paralelo.

O overhead paralelo pode ser visto como um custo m´ınimo da introduc¸ ˜ao de paralelizac¸ ˜ao, que na pr ´atica ´e o que impede que a melhoria de performace mediante o aumento do n ´umero de processadores seja linear

2

.

Tem sido referida repetidamente a necessidade de minimizar o tempo de execuc¸ ˜ao, o que pres- sup ˜oe, em primeiro lugar, a sua medic¸ ˜ao, o que n ˜ao ´e trivial; de facto, existem duas medidas relevantes para a contabilizac¸ ˜ao do tempo: tempo de rel ´ogio e tempo de CPU.

O tempo de rel ´ ogio

3

corresponde ao tempo real que o programa demora a executar, a mesma medic¸ ˜ao que seria obtida usando um cron ´ometro. Esta ´e a m ´etrica mais importante e a que deve ser usada para comparar os desempenhos de vers ˜oes s ´erie e paralela do mesmo programa, uma vez que ´e a que incide sobre o objetivo da paralelizac¸ ˜ao: minimizar o tempo real de execuc¸ ˜ao. No entanto, esta medida ´e afetada por tudo o que possa estar a acontecer no sistema no momento da computac¸ ˜ao - sobrecarga do CPU com outros processos, por exemplo - pelo que n ˜ao ´e a melhor opc¸ ˜ao para comparar diferentes corridas de uma mesma vers ˜ao sujeita a diferentes condic¸ ˜oes, como por exemplo a performance dessa vers ˜ao para diferente n ´umero de processadores.

Para isso, uma melhor opc¸ ˜ao ´e o tempo de CPU, que contabiliza apenas o tempo que a m ´aquina despende a executar o c ´odigo (incluindo chamadas ao I/O) e por isso ´e menos afetado por fatores externos `a computac¸ ˜ao. Para um ´unico processador, o tempo de CPU ´e necessariamente inferior ao tempo de rel ´ogio (no limite ser ´a igual). Em computac¸ ˜ao paralela ´e frequente calcular o tempo de CPU como a soma dos tempos individuais despendidos por cada processador (tempo de CPU unit ´ario) que deve, em teoria, ser independente do n ´umero de processadores a menos do overhead de paralelizac¸ ˜ao.

Por vezes ´e tamb ´em ´util usar o tempo de CPU unit ´ario, por exemplo comparando-o entre os v ´arios processadores para confirmar se a carga computacional se encontra bem distribu´ıda.

2.2.2 Arquitetura e comunicac¸ ˜ao

Existem v ´arias classificac¸ ˜oes poss´ıveis para as m ´aquinas de computac¸ ˜ao paralela. Uma classificac¸ ˜ao b ´asica, definida em func¸ ˜ao do n´ıvel em que o hardware suporta paralelismo, compreende duas cate- gorias: sistemas multi-core, caso um ´unico CPU disponha de mais do que um core; ou sistemas como clusters ou grids, em que v ´arias m ´aquinas diferentes s ˜ao usadas para resolver uma mesma tarefa.

A distinc¸ ˜ao mais comum entre arquiteturas de m ´aquinas paralelas, usualmente designada por Ta- xonomia de Flynn, ´e definida de acordo com as caracter´ısticas da m ´aquina segundo duas dimens ˜oes independentes, o fluxo de instruc¸ ˜ oes e o fluxo de dados passados `a m ´aquina em cada ciclo de rel ´ogio; cada uma destas dimens ˜oes compreende apenas dois estados, singular e m ´ultiplo. Desta forma fica definida a matriz de classificac¸ ˜ao de arquiteturas da tabela 2.3.

2Considerando o caso ideal de um programa 100%paraleliz ´avel.

3Alternativamente,wall-clock timeouelapsed time.

(12)

SISD SIMD

fluxo de instruc¸ ˜oes singular fluxo de instruc¸ ˜oes singular fluxo de dados singular fluxo de dados m ´ultiplo

MISD MIMD

fluxo de instruc¸ ˜oes m ´ultiplo fluxo de instruc¸ ˜oes m ´ultiplo fluxo de dados singular fluxo de dados m ´ultiplo

Tabela 2.3: Classificac¸ ˜ao de arquiteturas paralelas.

Nesta classificac¸ ˜ao a terminologia ´e bastante intuitiva. As m ´aquinas SISD

4

correspondem aos com- putadores s ´erie tradicionais (um sistema n ˜ao-paralelo); por cada ciclo de rel ´ogio, recebe apenas uma instruc¸ ˜ao

5

e um conjunto de dados, que s ˜ao executados de forma determin´ıstica. As m ´aquinas SIMD s ˜ao j ´a um tipo de computador paralelo, em que todos os processadores executam o mesmo conjunto de instruc¸ ˜oes mas sobre conjuntos de dados que podem ser distintos; s ˜ao ideais para resolver pro- blemas com elevada regularidade e separabilidade, como por exemplo o tratamento de imagens com GPUs, e a execuc¸ ˜ao ´e determin´ıstica e feita de forma s´ıncrona. N ˜ao s ˜ao frequentes as m ´aquinas de tipo MISD, em que em teoria cada core processa um conjunto diferente de instruc¸ ˜oes sobre o mesmo conjunto de dados; uma poss´ıvel aplicac¸ ˜ao para este tipo de m ´aquina seria, por exemplo, ter v ´arios al- goritmos criptogr ´aficos distintos a tentar quebrar a seguranc¸a de uma ´unica mensagem. Por ´ultimo, os computadores paralelos de tipo MIMD podem executar diferentes instruc¸ ˜oes sobre diferentes conjuntos de dados, de forma s´ıncrona ou ass´ıncrona (dependendo da utilizac¸ ˜ao de locksteps); este ´e o tipo de computador paralelo mais comum, correspondendo por exemplo `a maior parte dos supercomputadores atuais e aos sistemas cluster ou grid.

Um outro ponto fundamental acerca da arquitetura de computadores paralelos prende-se com a quest ˜ao da mem ´oria. No essencial, s ˜ao admitidas duas estruturas: mem ´oria partilhada e mem ´oria distribu´ıda. Nas m ´aquinas com mem ´ oria partilhada (figura 2.1), todos os processadores podem ace- der a todo o espac¸o de mem ´oria, pelo que as alterac¸ ˜oes efetuadas por um processador s ˜ao vis´ıveis para todos os outros; cada processador opera de forma independente dos outros, mas os recursos de mem ´oria s ˜ao partilhados entre si. Esta ´e uma boa forma de partilhar informac¸ ˜ao entre processadores, que elimina a necessidade de uma rede de comunicac¸ ˜ao entre processos; a desvantagem ´e que, por um lado, ´e muito pouco escal ´avel - a introduc¸ ˜ao de mais unidades de CPU vai congestionar o tr ´afego no sistema CPU-mem ´oria, atrasando a execuc¸ ˜ao; e, por outro, ´e mais vulner ´avel a erros de alocac¸ ˜ao de mem ´oria, dado que sobrecarrega o programador com a responsabilidade de coordenar todos os processos de forma a garantir que n ˜ao existam acessos concorrentes simult ˆaneos.

Figura 2.1: Mem ´oria partilhada. Figura 2.2: Mem ´oria distribu´ıda.

Em sistemas de mem ´ oria distribu´ıda, cada processador tem a sua pr ´opria mem ´oria, pelo que opera de forma verdadeiramente independente - as alterac¸ ˜oes que faz n ˜ao s ˜ao reproduzidas nos outros pro- cessadores. Desta forma, ´e necess ´ario manter uma rede de comunicac¸ ˜ao entre processadores; sempre que um processador precisa de informac¸ ˜ao contida noutro, ´e necess ´ario usar a rede para que troquem uma mensagem entre si. ´ E da compet ˆencia do programador formular explicitamente esse pedido e garantir a sincronizac¸ ˜ao entre os dois processos (isto ´e, garantir que a informac¸ ˜ao j ´a est ´a dispon´ıvel num processador quando deve ser enviada para o outro). A principal vantagem deste modelo reside

4Do ingl ˆesSingle Instruction, Single Data; as restantes siglas seguem uma terminac¸ ˜ao an ´aloga.

5No sentido defluxo, ou seja, recebe uma ou mais instruc¸ ˜oes de apenas uma fonte.

(13)

precisamente na quest ˜ao da escalabilidade: ao introduzir mais CPUs, o espac¸o de mem ´oria aumenta proporcionalmente, pelo que ´e poss´ıvel aumentar a capacidade de processamento sem comprometer a mem ´oria; adicionalmente, neste formato ´e tamb ´em poss´ıvel ter um CPU a trabalhar na sua pr ´opria mem ´oria, sem necessidade de sincronizac¸ ˜ao permanente com os restantes, evitando assim overheads e outras interfer ˆencias. Pela negativa, o programador tem a responsabilidade de coordenar toda a rede de comunicac¸ ˜ao, e o tempo de execuc¸ ˜ao est ´a sujeito a maiores flutuac¸ ˜oes, visto que o tempo de acesso atrav ´es da rede a um determinado conjunto de dados depende da sua localizac¸ ˜ao f´ısica.

Em geral, sistemas de mem ´oria distribu´ıda adequam-se melhor a uma situac¸ ˜ao em que os processa- dores possam trabalhar independentemente e apenas necessitem de comunicar, com pouca frequ ˆencia, os seus resultados entre si; se, por outro lado, houver necessidade de permanente sincronizac¸ ˜ao entre todos os processos, ent ˜ao os sistemas de mem ´oria partilhada s ˜ao uma melhor opc¸ ˜ao. Evidentemente, pode ser encontrado um bom equil´ıbrio usando ambas as arquiteturas simultaneamente; basta con- siderar um sistema de mem ´oria distribu´ıda em que cada n ´o, em vez de ser um ´unico processador, ´e uma m ´aquina com v ´arios processadores com mem ´oria partilhada entre si. Estes sistemas h´ıbridos permitem aproveitar as vantagens dos dois modelos e ao mesmo tempo aumentar a escalabilidade do sistema, com o ´obvio custo de uma programac¸ ˜ao mais complexa.

2.2.3 Limites da paralelizac¸ ˜ao

Para ser poss´ıvel avaliar o benef´ıcio da paralelizac¸ ˜ao ´e necess ´ario compreender em que aspetos ela pode ser ´util, perceber quanto se pode ganhar em cada um desses aspetos e definir m ´etricas apropri- adas para medir esses ganhos.

Na secc¸ ˜ao 4.1 estabeleceu-se a minimizac¸ ˜ao do tempo de execuc¸ ˜ao de um programa como um dos objetivos fundamentais da paralelizac¸ ˜ao. Tornam-se portanto pertinentes as quest ˜oes sobre qual o grau expect ´avel dessa minimizac¸ ˜ao e como poder ´a ela ser quantificada. ´ E conveniente introduzir o conceito de speedup (S

N

), que se define como o fator pelo qual o tempo de execuc¸ ˜ao ´e alterado mediante a introduc¸ ˜ao de paralelizac¸ ˜ao ([3]):

S

N

= t

1

t

N

(2.1) em que t

1

´e o tempo de execuc¸ ˜ao para um processador

6

e t

N

a quantidade an ´aloga para N processadores.

A lei de Amdahl ( [4]) estabelece que o speedup potencial de um programa ´e definido pela frac¸ ˜ao de c ´odigo que pode ser paralelizada (P )

7

:

S

max

= 1 1 − P

Como corol ´ario desta definic¸ ˜ao, ´e evidente que se nenhuma parte do c ´odigo for paraleliz ´avel ent ˜ao S

max

= 1, ou seja, n ˜ao h ´a speedup; e que se o c ´odigo for inteiramente paraleliz ´avel, ent ˜ao o speedup ´e, em teoria, infinito. Dela se conclui tamb ´em que ´e fundamental que o c ´odigo seja altamente paraleliz ´avel, uma vez que S

max

cresce exponencialmente com P , tal como mostra a tabela 2.4.

P .5 .8 .9 .95 .99 .999 .9999 S

max

2 5 10 20 100 1000 10000

Tabela 2.4: Speedup em func¸ ˜ao de P .

Tomando em considerac¸ ˜ao os N processadores utilizados e assumindo uma m ´aquina de dois esta- dos - isto ´e, que em cada momento ou apenas um processador est ´a a ser usado, ou ent ˜ao todos os N processadores est ˜ao - resulta que

S

max

= 1

P

N

+ (1 − P)

6A definic¸ ˜ao usual ´e quet1 corresponde aomelhortempo de execuc¸ ˜ao poss´ıvel para um processador; por conveni ˆencia,

´e comum utilizar-se o tempo de execuc¸ ˜ao da aplicac¸ ˜ao paralela correndo num ´unico processador, o que, n ˜ao sendo em rigor correto, introduz apenas um erro residual.

7Por coer ˆencia, definimos o tempo totaltcomo a soma do tempo despendido na porc¸ ˜aoPde tarefas paraleliz ´aveis,p, com o tempo dispendido na porc¸ ˜ao(1−P)de tarefas n ˜ao-paraleliz ´aveis,s;t=s+p.

(14)

E imediato observar que a primeira parte do denominador corresponde `a porc¸ ˜ao de c ´odigo que ´e ´ paraleliz ´avel e a segunda `aquela que ´e estritamente sequencial. Tal como seria de esperar, o speedup aumenta com o n ´umero de processadores utilizados, at ´e ao limite

(1−P1 )

, como mostram as tabelas 2.5:

N S

max

10 1.818 100 1.980 1000 1.998 10000 1.999

(a) P=.5

N S

max

10 5.263 100 9.174 1000 9.911 10000 9.991

(b) P=.9

N S

max

10 9.174 100 50.251 1000 90.992 10000 99.020

(c) P=.99

Tabela 2.5: Speedup em func¸ ˜ao de N .

Esta formulac¸ ˜ao ignora ainda o contributo do overhead de paralelizac¸ ˜ao, que pode ser significativo e em geral cresce com N ; introduzindo o termo σ(N ) para designar este overhead, vem finalmente a express ˜ao geral para S

max

S

max

= 1

P

N

+ (1 − P ) + σ(N ) (2.2)

que, no caso P = 1 (paralelizac¸ ˜ao total), se reduz a S

max

=

1+N σ(NN )

. Com N → ∞, S

max

1

σ(N)

, que ´e uma func¸ ˜ao decrescente; isto significa que existe um N ´otimo para o qual o speedup ´e maximizado

8

.

E importante frisar que a principal limitac¸ ˜ao ao paralelismo vem da pr ´opria estrutura do programa, ´ e n ˜ao do n ´umero de processadores utilizados. Considere-se o caso de se ter um programa com tempo de execuc¸ ˜ao de 1 dia, que se pretende reduzir para 1 hora; o speedup desejado ´e ent ˜ao S = 24. A tabela ?? mostra o n ´umero de processadores necess ´arios para obter S = 24, em func¸ ˜ao do grau de paralelizac¸ ˜ao do programa; a diferenc¸a entre paralelizar o programa a 96% ou 97%, aparentemente pouco significativa, ´e a diferenc¸a entre precisar de 576 processadores - imposs´ıvel, por exemplo, no medusa - ou 84 - um n ´umero razo ´avel na maior parte das infraestruturas.

P .96 .965 .97 .975 .98 .985 .99

N 576 145 84 59 46 37 32

Tabela 2.6: N em func¸ ˜ao de P para S = 24.

Os resultados da lei de Amdahl baseiam-se na assumpc¸ ˜ao de que o tempo gasto por um pro- cessador em tarefas paraleliz ´aveis, p, ´e independente de N. Na verdade, essa abordagem fixed-size raramente ´e aplicada fora do campo da investigac¸ ˜ao acad ´emica; na pr ´atica, o tamanho do problema escala com o n ´umero de processadores, ou seja, p depende de N (e, em geral, aumenta)

9

, uma vez que as componentes s ´erie do programa, como inicializac¸ ˜oes de vetores e I/O, n ˜ao variam com N. Esta observac¸ ˜ao limita a aplicabilidade da lei de Amdahl e est ´a na origem da chamada lei de Gustafson [5].

Definindo p e s como os tempos paralelo e sequencial dispendidos no sistema paralelo, ent ˜ao, para um processador s ´erie, t

1

= s + p × N , donde

S

s

= t

1

/t

N

= (s + p × N)/(s + p)

= s + p × N

= N + (1 − N ) × s

Este speedup escalado S

s10

tem um comportamento linear em N , que contrasta com o comporta- mento exponencial previsto pela lei de Amdahl; esta abordagem de tamanho escal ´avel justifica que, na

8Este resultado mant ´em-se obviamente v ´alido paraP <1.

9Frequentemente par ˆametros como resoluc¸ ˜ao da rede, n ´umero de etimesteps, etc, s ˜ao ajustados para que o programa corra dentro de uma janela temporal aceit ´avel; ao aumentar a capacidade de computac¸ ˜ao dispon´ıvel, tende-se a reajustar esses par ˆametros de forma a obter melhores resultados dentro da mesma janela temporal. ´E a isto que se refere a express ˜ao “o tamanho do problema escala com o n ´umero de processadores”.

10Do originalscaled speedup.

(15)

pr ´atica, conseguir um speedup elevado n ˜ao seja t ˜ao complicado como a lei de Amdahl sugere, desde que se escale o tamanho do problema para acompanhar o n ´umero de processadores

11

.

Um indicador complementar ao speedup ´e a efici ˆencia, definida como E

N

= S

N

N = T

1

N T

N

A efici ˆencia toma valores no intervalo [0, 1] e ´e frequentemente utilizada para perceber se os pro- cessadores est ˜ao a ser bem utilizados, ou seja, se est ˜ao dedicados a tarefas de computac¸ ˜ao ´uteis (E

N

≈ 1), ou se est ˜ao a ser desperdic¸ados em processos de comunicac¸ ˜ao e sincronizac¸ ˜ao (E

N

≈ 0).

No caso ideal de perfeita escalabilidade (e portanto speedup linear), E

N

= 1; em geral, a efici ˆencia decresce com N , refletindo o aumento dos overheads de comunicac¸ ˜ao. Esta medida ´e uma forma f ´acil de perceber se a paralelizac¸ ˜ao est ´a a funcionar bem ou se, pelo contr ´ario, os recursos est ˜ao a ser desperdic¸ados.

Apesar de largamente utilizados, ambos estes indicadores, speedup e efici ˆencia, apresentam algu- mas lacunas que justificam a introduc¸ ˜ao de uma nova m ´etrica:

e =

1 SN

N1

1 −

N1

(2.3)

Esta m ´etrica de Karp-Flatt

12

([6]) representa a frac¸ ˜ao s ´erie do programa, que em condic¸ ˜oes ideais se anula (para speedup linear, S

N

= N). Como e deve manter-se constante para N crescente, ´e conveniente utiliz ´a-la para detetar e compreender pequenas flutuac¸ ˜oes nos outros dois indicadores, nomeadamente relacionadas com:

• Distribuic¸ ˜ao da carga computacional - assume-se uma distribuic¸ ˜ao equilibrada para todos os n ´os, o que n ˜ao ´e necessariamente verdade; uma distribuic¸ ˜ao menos equilibrada traduz-se num au- mento do valor de e.

• Overheads - um aumento dos overheads resulta na reduc¸ ˜ao do speedup , logo tamb ´em num aumento de e com N ; um crescimento regular de e ´e um poss´ıvel indicador de que a granularidade da parelelizac¸ ˜ao ´e demasiado fina.

Resumindo, existem v ´arios indicadores para aferir o desempenho de um programa paralelo, cada qual com as suas vantagens e inconvenientes. O speedup introduzido na equac¸ ˜ao 2.1 ´e uma me- dida aceit ´avel e largamente utilizada do grau de paralelizac¸ ˜ao do programa, que deve ser calculado e comparado com S

max

da equac¸ ˜ao 2.2 para compreender se a paralelizac¸ ˜ao est ´a a ser feita da melhor forma, caso estejamos a tratar um problema de tamanho fixo; caso contr ´ario, devemos recorrer a S

s

. A efici ˆencia E

N

´e ´util para perceber se o hardware est ´a a ser utilizado convenientemente, ao passo que a m ´etrica de Karp-Flatt, e, pode ser usada para um diagn ´ostico mais detalhado do programa, nomeada- mente para identificar problemas de distribuic¸ ˜ao de carga computacional ou de excessivos overheads.

2.2.4 Modelos de paralelizac¸ ˜ao

V ´arios modelos diferentes permitem concretizar a paralelizac¸ ˜ao do programa, que no essencial diferem entre si no que diz respeito `a utilizac¸ ˜ao da mem ´oria (partilhada ou distribu´ıda), ao modo de comunicac¸ ˜ao entre processos e ao tratamento do input de dados. ´ E conveniente introduzir alguns desses modelos

13

e especificar as suas caracter´ısticas, ainda que de forma simplificada, de modo a facilitar a escolha do modelo mais adequado em cada situac¸ ˜ao. Note-se que o modelo de paralelizac¸ ˜ao ´e independente da arquitetura da m ´aquina - por exemplo, pode ser aplicado um modelo de mem ´oria partilhada a uma arquitetura de mem ´oria distribu´ıda ([7]).

11O tamanho do problema n ˜ao ´e necessariamente constante. Tipicamente esta grandeza ´e controlada por par ˆametros ajust ´aveis pelo utilizador - a resoluc¸ ˜ao da malha, por exemplo, ou o n ´umero de iterac¸ ˜oes, na resoluc¸ ˜ao num ´erica de equac¸ ˜oes.

Perante uma maior capacidade de processamento, estes par ˆametros s ˜ao em geral ajustados para obter melhores resultados, de tal forma que em geral o que se pretende constante ´e o tempo de execuc¸ ˜ao - dentro de algum limite razo ´avel - e n ˜ao a dimens ˜ao do problema. Ver [5].

12Descrita aqui para problemas de tamanho fixo. No caso de um problema de tamanho escal ´avel, nos moldes considerados na lei de Gustafson, pode ser generalizada introduzindo um fator de escala adicional

13Utiliza-se uma combinac¸ ˜ao livre do idioma original e do portugu ˆes nos nomes dos modelos de forma a minimizar traduc¸ ˜oes forc¸adas e perdas de signific ˆancia.

(16)

• Mem ´oria partilhada (sem Threads)

Este ´e provavelmente o modelo mais simples de paralelizac¸ ˜ao, assente no mesmo paradigma que a arquitetura de mem ´oria partilhada: os processadores partilham um mesmo espac¸o de mem ´oria, no/do qual escrevem/leem assincronamente. Tem a vantagem de n ˜ao precisar de comunicac¸ ˜oes, logo per- mitir uma escrita simples do programa, mas implica a coordenac¸ ˜ao dos v ´arios processos para impedir escritas concorrentes e conflitos de dados. Em geral ´e indesej ´avel para programas com grande volume de computac¸ ˜ao, devido `a necessidade de sincronizac¸ ˜ao entre os processos.

• Threads

No modelo de threads um processo principal ´e subdividido em n sub-processos (as threads) con- correntes entre si. Estas threads operam independentemente umas das outras e partilham os recur- sos do processo original, pelo que comunicam entre si pela atualizac¸ ˜ao da mem ´oria, sem recurso `a troca de mensagens, tal como acontece no modelo de mem ´oria partilhada. Da mesma forma, exigem sincronizac¸ ˜ao para evitar a escrita simult ˆanea num mesmo enderec¸o de mem ´oria. Este modelo pode ser visto como uma vers ˜ao localizada do modelo de mem ´oria partilhada, confinado ao escopo do pro- cesso principal, sendo principalmente utilizado para evitar replicar a informac¸ ˜ao do processo original e para introduzir paralelismo localmente, sem necessidade de paralelizar todo o programa.

• Message Passing

Neste modelo cada processo executa as suas tarefas na sua pr ´opria mem ´oria local, n ˜ao partilhando espac¸o de mem ´oria com outros processos (mesmo que os processos partilhem a mesma m ´aquina f´ısica, cada um tem o seu espac¸o de mem ´oria). A comunicac¸ ˜ao ´e feita por troca de mensagens entre os processos, atrav ´es de uma rede de comunicac¸ ˜oes, e geralmente envolve uma ac¸ ˜ao cooperativa, isto ´e, a uma mensagem de send tem de corresponder uma de receive do processo adequado. Esta troca de mensagens ´e feita usualmente recorrendo a uma biblioteca de sub-rotinas, que pode variar dependendo da implementac¸ ˜ao usada.

• Data Parallel

Este modelo pressup ˜oe um tratamento global do espac¸o de mem ´oria. Cada processo executa um mesmo conjunto de operac¸ ˜oes sobre diferentes porc¸ ˜oes de uma mesma estrutura de dados - por exemplo, num array de 100 unidades, usando 4 processos, o processo 1 trabalha sobre as unidades 1 a 25, o processo 2 sobre as unidades 26 a 50, o processo 3 sobre as unidades 51 a 75 e o processo 4 sobre as restantes. A sua implementac¸ ˜ao pode depender da arquitetura: se for de mem ´oria partilhada todos os processos t ˆem automaticamente acesso `a sua porc¸ ˜ao de dados, mas se for de mem ´oria distribu´ıda ´e necess ´ario definir a priori a subdivis ˜ao e atribuic¸ ˜ao de cada bloco de dados ao processo respetivo.

• Modelos H´ıbridos

E rara a utilizac¸ ˜ao estrita de apenas um destes modelos, sendo mais comuns soluc¸ ˜oes h´ıbridas ´ em que v ´arios modelos se combinam. Um exemplo de modelo h´ıbrido comum reside na combinac¸ ˜ao dos modelos de message passing e threads: a computac¸ ˜ao intensiva ´e desenvolvida pelas threads localmente, em cada n ´o, e a comunicac¸ ˜ao entre processos diferentes ´e feita atrav ´es da rede de comunicac¸ ˜oes quando ´e preciso trocar dados entre processos. Esta forma de paralelismo adequa-se especialmente `as arquiteturas de cluster.

• Modelos de alto n´ıvel

Em geral a programac¸ ˜ao paralela ´e feita recorrendo a um modelo de alto n´ıvel constru´ıdo com base numa combinac¸ ˜ao dos modelos-base apresentados. Distingue-se habitualmente entre modelos SPMD e MPMD

14

, sendo o modelo SPMD o mais comum. Este modelo consiste em ter v ´arios processos a executar um mesmo programa, sobre conjuntos que podem ser distintos; este programa ´e composto por uma combinac¸ ˜ao de threads, message passing, data parallel ou h´ıbridos, e em geral n ˜ao exige a execuc¸ ˜ao da totalidade do programa por todos os processadores - admitindo, por exemplo, distribuic¸ ˜oes ramificadas ou condicionais da carga computacional. O modelo MPMD apenas difere do SPMD na medida em que cada processo pode executar um programa distinto, o que, n ˜ao sendo frequentemente necess ´ario, pode por vezes constituir uma soluc¸ ˜ao relevante.

14Do originalSingleeMultiple Program Multiple Data.

(17)

2.3 Estado da arte

Em 1967 j ´a G. Amdahl afirmava que “H ´a mais de uma d ´ecada que existem profetas a afirmar que a organizac¸ ˜ao de um computador singular atingiu o limite e que apenas a ligac¸ ˜ao de uma multiplicidade de computadores de maneira tal que permita uma soluc¸ ˜ao cooperativa pode dar origem a avanc¸os significativos” ([4, p.1]).

Apesar de existir interesse em computac¸ ˜ao paralela desde meados da d ´ecada de 1950, apenas nos anos 60 e 70 esse interesse se materializou sob a forma de supercomputadores, assentes num modelo de mem ´oria partilhada. Esse interesse fomentou uma evoluc¸ ˜ao cont´ınua, que se refletiu no aparecimento de Massively Parallel Processors (MPPs) em meados de 80, nos clusters de m ´aquinas independentes desde o in´ıcio dos anos 90 e, mais recentemente, na banalizac¸ ˜ao de m ´aquinas multi- core que incluem v ´arios processadores.

Actualmente, a profecia a que Amdahl fazia refer ˆencia parece estar a concretizar-se, alimentada pela progressiva constatac¸ ˜ao de que o aumento da capacidade computacional pela maximizac¸ ˜ao do clock speed est ´a a atingir o seu limite ([8]) e que se torna mais eficiente, em termos econ ´omicos e energ ´eticos, promover esse aumento atrav ´es da agregac¸ ˜ao de v ´arios cores ([9]); paralelamente, a cont´ınua verificac¸ ˜ao da lei de Moore tem significado um crescimento exponencial da quantidade de hardware dispon´ıvel, permitindo construir plataformas multi-core a um custo cada vez mais reduzido.

Estes fatores t ˆem contribu´ıdo para acelerar a transic¸ ˜ao da computac¸ ˜ao sequencial para a paralela.

Neste momento a computac¸ ˜ao paralela ´e largamente usada em v ´arios setores - o da ind ´ustria ´e o mais dominante, usando 50% dos recursos existentes, seguido da investigac¸ ˜ao cient´ıfica - e num conjunto variado de aplicac¸ ˜oes t ˜ao distintas como financ¸a, medicina ou gest ˜ao log´ıstica ([7]). Apesar deste crescimento, a paralelizac¸ ˜ao ´e ainda um processo pouco automatizado, exigindo uma abordagem muito manual e espec´ıfica para cada problema e consequentemente aumentando a responsabilidade do programador.

As ferramentas dispon´ıveis para paralelizac¸ ˜ao s ˜ao sobretudo Interfaces de Programac¸ ˜ao de Aplica- tivos (APIs) ou bibliotecas que trabalham sobre linguagens de programac¸ ˜ao de baixo n´ıvel (tipicamente C/C++ e Fortran). As linguagens de programac¸ ˜ao exclusivamente paralelas existentes s ˜ao em geral bastante espec´ıficas e de baixo n´ıvel; a inexist ˆencia de uma linguagem paralela mais global e de alto n´ıvel ´e vista frequentemente como uma das causas para a lenta transic¸ ˜ao para o paralelismo, e constitui um importante problema em aberto neste campo ([10]). Existem m ´ultiplas ferramentas para desenvol- vimento de programas em paralelo, das quais ´e conveniente destacar, pela sua elevada utilizac¸ ˜ao, o MPI, para o modelo de message-passing, e o OpenMP, usado com o modelo de threads; ambos s ˜ao utilizados em conjunto com uma linguagem usual, como C++ ou F ortran.

Ainda que tenha vindo a verificar uma utilizac¸ ˜ao crescente, a computac¸ ˜ao paralela ´e ainda vista largamente como uma tend ˆencia e n ˜ao ainda como a abordagem padr ˜ao. Num estudo recente enco- mendado pela Intel sobre h ´abitos de programadores e gestores de software, apenas 26% classificam a utilizac¸ ˜ao de paralelismo no seu trabalho como cr´ıtica - 56% veem-na como importante mas n ˜ao essen- cial, e os restantes como irrelevante ([11]). O mesmo estudo mostra que as ferramentas auxiliares de detec¸ ˜ao de defeitos de threading ou de mem ´oria s ˜ao ainda pouco utilizadas, ilustrando a imaturidade do desenvolvimento de software paralelo e a necessidade de melhores ferramentas de paralelizac¸ ˜ao.

Em resumo, tem sido verificado um aumento crescente da utilizac¸ ˜ao de computac¸ ˜ao paralela nos anos recentes, potenciado pela noc¸ ˜ao de que o hardware est ´a a atingir o limite do seu desenvolvimento;

este crescimento n ˜ao se restringe aos tradicionais setores acad ´emico e de investigac¸ ˜ao, antes ´e trans- versal a todos os setores de atividade e com especial impacto na ind ´ustria. Sendo um desenvolvimento recente, ´e ainda uma ´area imatura e n ˜ao considerada essencial, existindo v ´arias ferramentas de de- senvolvimento - em geral n ˜ao-standard e pouco user-friendly - mas poucas complementares, como debuggers. O paradigma atualmente dominante ´e o de message-passing, sendo o MPI a plataforma mais utilizada.

2.4 MPI

O desenvolvimento da paralelizac¸ ˜ao descrita no cap´ıtulo 4 foi feito com base no modelo de message- passing, usando a implementac¸ ˜ao Open MPI do MPI. Esta secc¸ ˜ao pretende apenas introduzir o MPI e os seus conceitos essenciais; para detalhes sobre a sua utilizac¸ ˜ao, em particular sobre a sintaxe das rotinas, ´e conveniente consultar a documentac¸ ˜ao de Open MPI, dispon´ıvel em [12]

15

.

15Para uma descric¸ ˜ao mais completa ver [13].

(18)

O MPI

16

, cuja primeira vers ˜ao data de 1992, ´e uma especificac¸ ˜ao de um conjunto de rotinas (bi- blioteca) usadas para a comunicac¸ ˜ao entre processos paralelos

17

. Tem como objetivo estabelecer um padr ˜ao para a escrita de programas paralelos no modelo de message-passing, de forma a maximizar a sua portabilidade, praticabilidade, flexibilidade e efici ˆencia. A vers ˜ao mais recente de MPI ´e o MPI-3.

Na pr ´atica, as raz ˜oes por optar por MPI prendem-se com o facto de ser a ´unica especificac¸ ˜ao que pode ser considerada standard, de praticamente n ˜ao ser necess ´ario alterar o c ´odigo aquando de uma mudanc¸a de plataforma e de estar facilmente acess´ıvel, incluindo implementac¸ ˜oes open-source como o Open MPI. Pode ser utilizado sobre as linguagens C, C++ ou F ortran, com diferenc¸as m´ınimas ao n´ıvel da escrita

18

.

A estrutura geral de um programa MPI ´e a seguinte:

• incluir o header mpif.h e/ou outras bibliotecas

• declarar vari ´aveis, prot ´otipos, etc (tal como num programa s ´erie)

• inicializar o MPI (com a func¸ ˜ao MPI Init)

• processar a computac¸ ˜ao

• finalizar o MPI (com a func¸ ˜ao MPI Finalize)

N ˜ao ´e recomend ´avel a inclus ˜ao de c ´odigo antes da inicializac¸ ˜ao de MPI (exceto declarac¸ ˜oes) nem ap ´os a sua finalizac¸ ˜ao, caso contr ´ario o comportamento ´e imprevis´ıvel.

Em MPI, a estrutura de grupo inclui um ou mais processadores que se pretende que comuniquem entre si. Cada processador est ´a associado a um ou mais grupos. Cada grupo tem associado a si um comunicador, mais especificamente um intra-comunicador, que permite a troca de mensagens entre os processos desse grupo. O grupo existente por defeito, a partir do momento em que o MPI ´e lanc¸ado,

´e universal e o seu comunicador MPI COMM WORLD engloba todos os processos. Cada comunicador tem um tamanho - o n ´umero de processos que abrange - que vulgarmente se designa por nproc. Cada processo tem um n ´umero inteiro ´unico que o identifica no contexto do seu comunicador, o rank, contido no intervalo [0, nproc − 1].

A comunicac¸ ˜ao em MPI ´e feita por troca de mensagens entre os processos, que pode ser feita ponto-a-ponto ou coletivamente. As comunicac¸ ˜oes ponto-a-ponto envolvem a troca de uma mensagem apenas entre dois processos espec´ıficos; tipicamente o processo A chama um send com a mensagem pretendida, que s ´o ´e recebido pelo processo B se este chamar um receive correspondente.

Em termos de aplicac¸ ˜ao, existem v ´arias variac¸ ˜oes de sends e receives. A distinc¸ ˜ao mais impor- tante reside no facto de serem blocking ou non-blocking : uma operac¸ ˜ao blocking deixa o processo em espera at ´e receber a mensagem correspondente, enquanto que uma non-blocking prossegue a execuc¸ ˜ao. A primeira opc¸ ˜ao ´e mais segura e f ´acil de prever, mas mais demorada e pode dar origem a deadlocks, isto ´e, situac¸ ˜oes em que dois processos esperam um pelo outro e nenhum avanc¸a, blo- queando o programa. Um exemplo t´ıpico de deadlock ´e o caso em que os processos A e B chamam simultaneamente um send para o outro; como cada processo fica `a espera do receive do outro, que n ˜ao

´e chamado por nenhum porque o send ainda n ˜ao foi conclu´ıdo, ambos os processos ficam bloqueados.

A segunda opc¸ ˜ao ´e mais r ´apida mas mais dif´ıcil de controlar, uma vez que pode originar problemas de sobreposic¸ ˜ao de mensagens ou de correspond ˆencia errada, devido `a execuc¸ ˜ao concorrente dos pro- cessos (secc¸ ˜ao 2.2.1). Este tipo de comunicac¸ ˜ao ´e ´util quando ´e apenas necess ´ario trocar informac¸ ˜ao com um ´unico processador sem envolver os restantes, por exemplo num problema do tipo “linha de montagem” em que cada processo recebe uma informac¸ ˜ao, executa uma certa operac¸ ˜ao e envia o resultado para o processo seguinte.

Mais comuns s ˜ao as comunicac¸ ˜oes coletivas, em que a troca de informac¸ ˜ao envolve todos os processos do comunicador. Estas comunicac¸ ˜oes tamb ´em podem ser blocking ou non-blocking. Relati- vamente `as comunicac¸ ˜oes ponto-a-ponto, estas s ˜ao mais f ´aceis de escrever - por exemplo para enviar uma mensagem para N processadores basta fazer uma chamada a uma func¸ ˜ao coletiva, em vez de N sends - mas exigem uma maior atenc¸ ˜ao do programador para garantir que todos os processos s ˜ao en- volvidos na comunicac¸ ˜ao (mesmo os que n ˜ao tenham necessidade disso). Distinguem-se neste campo tr ˆes tipos de operac¸ ˜oes: sincronizac¸ ˜ao, troca de dados e computac¸ ˜ao coletiva.

16Do originalMessage Passing Interface.

17N ˜ao ´e, em si, uma biblioteca - esta resulta da implementac¸ ˜ao, por exemploOpen MPI.

18Neste trabalho as refer ˆencias dizem respeito `a sintaxe deF ortrana menos que o contr ´ario seja especificado.

(19)

• Sincronizac¸ ˜ao - frequentemente ´e necess ´ario forc¸ar que todos os processos esperem por um determinado evento, isto ´e, sincronizar os processos. Isso pode ser feito atrav ´es de uma barreira coletiva (func¸ ˜ao MPI Barrier) - cada processo espera at ´e que todos tenham chamado a mesma barreira antes de prosseguir.

• Troca de dados - permite trocar informac¸ ˜ao entre mais do que dois processos. As principais operac¸ ˜oes s ˜ao de broadcast - envio de uma mensagem de um processador para todos -, de scatter - envia diferentes mensagens com origem num ´unico processador para todos os proces- sadores (“dispers ˜ao”) - e de gather - recebe informac¸ ˜ao de v ´arios processadores num ´unico, de forma inversa ao scatter (“recolha”).

• Computac¸ ˜ao coletiva - permite executar uma operac¸ ˜ao sobre dados residentes em todos os pro- cessadores. Tipicamente corresponde a uma operac¸ ˜ao de reduce, que junta a informac¸ ˜ao de todos os processadores, trata-a e devolve o resultado para um ´unico processador. Al ´em das operac¸ ˜oes mais comuns definidas pelo MPI (soma, multiplicac¸ ˜ao, m ´aximo) ´e poss´ıvel ao utiliza- dor definir a sua pr ´opria func¸ ˜ao de reduce.

Estes s ˜ao os conceitos fundamentais do funcionamento do MPI; n ˜ao se pretende fazer uma descric¸ ˜ao exaustiva das capacidades do MPI mas apenas permitir a compreens ˜ao do seu modo de aplicac¸ ˜ao gen ´erico. Para uma explorac¸ ˜ao mais detalhada das funcionalidades do MPI recomenda-se a consulta das refer ˆencias [14] e [15].

2.5 Estrat ´egia de paralelizac¸ ˜ao

E conveniente reunir os conceitos j ´a abordados numa ´unica estrat ´egia de paralelizac¸ ˜ao, t ˜ao gen ´erica ´ quanto poss´ıvel, que possa ser aplicada a uma variedade de problemas. ´ E esse o objetivo desta secc¸ ˜ao, cujo conte ´udo tem como origem a experi ˆencia de paralelizac¸ ˜ao de alguns problemas como o descrito no cap´ıtulo 4. Apesar de t ˜ao geral quanto poss´ıvel, esta estrat ´egia foi pensada no contexto do modelo de message-passing com utilizac¸ ˜ao do MPI, pelo que podem existir noc¸ ˜oes que n ˜ao se apliquem a todos os modelos ou plataformas.

Distinguimos tr ˆes momentos do processo de paralelizac¸ ˜ao: a abordagem inicial, a paralelizac¸ ˜ao propriamente dita e a an ´alise dos resultados; esta ´ultima n ˜ao ´e contemplada nesta secc¸ ˜ao, sendo apenas abordada no contexto do estudo de caso do cap´ıtulo 4 para melhor compreens ˜ao. Acrescenta- se ainda uma secc¸ ˜ao de “dicas” destinadas a chamar a atenc¸ ˜ao para alguns detalhes que podem ser

´uteis na resoluc¸ ˜ao de determinados problemas. E ignorada a hip ´otese de recorrer a paralelizac¸ ˜ao ´ autom ´atica.

2.5.1 Abordagem inicial

Normalmente pretende-se introduzir paralelizac¸ ˜ao para melhorar um programa j ´a existente. A tarefa priorit ´aria ´e compreender qual o problema que o programa trata e como faz para o resolver, para facilitar a sua posterior modificac¸ ˜ao para incluir paralelismo. Simultaneamente, a motivac¸ ˜ao para a paralelizac¸ ˜ao deve ficar bem definida - ´e importante que seja claro se o programa demora demasiado tempo a executar, e portanto o objetivo ´e reduzir esse tempo, ou se ´e incapaz de processar os dados que devia, e ent ˜ao o objetivo ´e repartir a utilizac¸ ˜ao de mem ´oria. Em cada caso devem ser estabelecidos objetivos concretos: caso o problema seja o tempo, definir o limite superior ao tempo desejado; caso seja a mem ´oria, definir a dimens ˜ao do problema que deve ser poss´ıvel processar.

O passo seguinte ´e perceber se o programa ´e paraleliz ´avel. N ˜ao ´e de forma alguma trivial que todos os programas o sejam - por exemplo, um programa que calcule os termos da sequ ˆencia de Fibonacci pelo algoritmo usual F(n) = F(n −1)+ F (n −2) n ˜ao ´e paraleliz ´avel, porque existe uma depend ˆencia en- tre os termos. Nesta fase o programador deve perceber se existem partes do programa que possam ser paralelizadas e identificar elementos inibidores de paralelismo, tais como a depend ˆencia entre dados do exemplo anterior. As porc¸ ˜oes de computac¸ ˜ao que n ˜ao sejam identificadas como n ˜ao-paraleliz ´aveis constituem os potenciais candidatos a serem paralelizados.

Conhecendo j ´a o programa a este n´ıvel, torna-se essencial maximizar o proveito da paralelizac¸ ˜ao, respondendo `a pergunta “Onde gasta o programa mais tempo?”; o programador deve identificar onde

´e feita a maior parte da computac¸ ˜ao, e marcar esses hotspots como os mais fortes candidatos a

paralelizar para obter o m ´aximo benef´ıcio. Apenas as porc¸ ˜oes de c ´odigo onde seja gasto um tempo

(20)

relevante devem ser consideradas para paralelizac¸ ˜ao, uma vez que paralelizar secc¸ ˜oes com pouco impacto no tempo total de execuc¸ ˜ao ter ´a impacto reduzido no resultado final (pode, inclusivamente, aument ´a-lo, devido aos overheads).

Para a determinac¸ ˜ao dos hotspots ´e muito ´util o recurso a ferramentas de profiling , das quais a mais acess´ıvel ´e o gprof

19

. Para analisar um programa s ´erie com esta ferramenta, o programador deve:

1. Acrescentar a flag “-pg” `a compilac¸ ˜ao de nome programa

2. Executar o programa normalmente ( ´e criado o ficheiro “gmon.out”) 3. Correr o comando “gprof nome programa>out”

4. Analisar ficheiro “out” que cont ´em a informac¸ ˜ao de profiling

Para um programa paralelo este tipo de an ´alise ´e mais complicado, dado que o gprof n ˜ao funciona de maneira regular. Existem ferramentas para profiling de aplicac¸ ˜oes paralelas, mas que n ˜ao foram abordadas neste trabalho; algumas orientac¸ ˜oes sobre esta quest ˜ao podem ser encontradas em [16].

Resumindo, a abordagem inicial deve responder a tr ˆes quest ˜oes:

1. Como funciona o programa?

2. ´ E paraleliz ´avel?

3. Onde ´e mais rent ´avel paralelizar?

onde se gasta mais tempo?

que estruturas ocupam mais mem ´oria?

2.5.2 Paralelizac¸ ˜ao

A partir do momento em que se conclui pela possibilidade da paralelizac¸ ˜ao e se identificam os hotspots relevantes, est ˜ao reunidas as condic¸ ˜oes para passar `a paralelizac¸ ˜ao propriamente dita, que corres- ponde `a fase de computac¸ ˜ao da estrutura global introduzida em 2.4. Esta fase apresenta em geral uma subestrutura semelhante `a seguinte:

1. aquisic¸ ˜ao do input;

2. distribuic¸ ˜ao da carga computacional;

3. computac¸ ˜ao independente;

4. comunicac¸ ˜ao:

com outros processos, se necess ´ario para prosseguir computac¸ ˜ao (nesse caso regressar ao ponto 3);

dos resultados (operac¸ ˜ao de reduce), caso a computac¸ ˜ao esteja finalizada.

A generalidade dos programas inclui uma parte inicial de processamento de I/O, nomeadamente para input dos dados com que vai trabalhar. Na transic¸ ˜ao para o paralelismo ´e necess ´ario decidir como tratar este processo. Distinguem-se duas opc¸ ˜oes mais comuns, ambas constituindo formas de I/O standard (ou seja, sequencial): leitura simult ˆanea em todos os processos (transversal) ou leitura no root seguida de broadcast para os restantes processos.

O primeiro caso ´e bastante simples de escrever: basta incluir as instruc¸ ˜oes de leitura tal como num programa sequencial. Cada processador executa essa instruc¸ ˜ao e l ˆe o ficheiro designado. Esta soluc¸ ˜ao tem a vantagem de ser a mais simples de usar e n ˜ao contribuir para o overhead de comunicac¸ ˜ao; no entanto, exige a alocac¸ ˜ao dos arrays globais em todos os processos - o que pode n ˜ao ser vi ´avel em termos de mem ´oria dispon´ıvel - e pode dar origem a congestionamentos no sistema, quando m ´ultiplos processos tentam aceder ao mesmo ficheiro, introduzindo assim um overhead de I/O indesejado, cuja magnitude depende da quantidade de informac¸ ˜ao que ´e necess ´ario ler ou escrever.

19O Gprof ´e um projecto GNU sob licenc¸a GNU GPL (open-source, dispon´ıvel por defeito nos sistemas Unix.

(21)

No caso leitura em root + broadcast , evita-se o congestionamento provocado por m ´ultiplas leituras simult ˆaneas e ´e f ´acil trocar o broadcast por um scatter e com isso evitar a necessidade de alocar os arrays globais, o que ´e uma vantagem em termos de mem ´oria. A desvantagem desta opc¸ ˜ao ´e que introduz um overhead de comunicac¸ ˜ao, que pode ser muito significativo, e que obriga todos os processadores non-root a esperar enquanto o root faz a leitura e at ´e lhes ser enviada a sua parcela de dados a trabalhar. Ambas as soluc¸ ˜oes t ˆem aplicac¸ ˜ao, cabendo ao programador decidir, em func¸ ˜ao do problema, qual a mais adeaquada.

Existe ainda uma terceira opc¸ ˜ao, que ´e usar I/O paralelo, j ´a inclu´ıda nas implementac¸ ˜oes de MPI-2, mas que ´e ainda algo imatura e tem algumas particularidades - por exemplo, a leitura pode ser feita apenas a partir de ficheiros bin ´arios - que complicam a sua utilizac¸ ˜ao.

Conclu´ıdo o processo de leitura ´e preciso distribuir a carga computacional pelos processadores. O primeiro passo ´e distribuir os dados pelos processadores que os v ˜ao trabalhar; a maneira mais simples de conseguir isso ´e definir N blocos de dados e distribuir um por cada processador. Existem v ´arias alternativas para efetuar esta distribuic¸ ˜ao, por exemplo usando um modelo c´ıclico ou uma distribuic¸ ˜ao funcional; o ponto essencial ´e que essa distribuic¸ ˜ao seja equilibrada e permita que a cada processador seja atribu´ıda uma carga semelhante, de forma a minimizar o tempo em que alguns processos est ˜ao `a espera que outros acabem o seu trabalho.

Para problemas com estruturas de dados uniformes este tipo de distribuic¸ ˜ao est ´atica ´e geralmente a melhor opc¸ ˜ao. Pode no entanto acontecer que essas estruturas sejam mais heterog ´eneas (por exemplo, matrizes esparsas) e que uma distribuic¸ ˜ao regular origine uma sobrecarga de alguns processos; nesse caso pode ser proveitoso adotar uma estrat ´egia de distribuic¸ ˜ao din ˆamica: o root lista todas as tarefas pendentes e recebe de cada processo um pedido de atribuic¸ ˜ao de tarefa, que quando ´e conclu´ıda d ´a origem a novo pedido, e assim sucessivamente. Desta forma assegura-se em princ´ıpio que todos os processos est ˜ao sempre ocupados, at ´e que n ˜ao haja mais tarefas; o custo ´e ao n´ıvel da programac¸ ˜ao, sendo necess ´ario definir a tarefa unit ´aria e introduzir um algoritmo de gest ˜ao das tarefas no processo root.

A partir do momento em que todos os processos t ˆem a sua carga de trabalho, comec¸am a trabalhar nela de forma independente uns dos outros, at ´e que seja necess ´ario trocar informac¸ ˜ao entre eles ou forc¸ar a sincronizac¸ ˜ao de todos os processos. Cabe ao programa decidir o perfil de comunicac¸ ˜oes - ou seja, a granularidade - do programa. Uma granularidade fina - baixo volume de computac¸ ˜ao entre comunicac¸ ˜oes - facilita o balanceamento da carga computacional, mas implica um grande overhead de comunicac¸ ˜ao. Por oposic¸ ˜ao, uma granularidade grosseira permite minimizar os overheads comunicati- vos mas dificulta a distribuic¸ ˜ao da carga. O balanc¸o ´otimo entre estes extremos depende fortemente do problema, mas em geral ´e mais indicado optar por uma granularidade mais grosseira, uma vez que os overheads de comunicac¸ ˜ao representam o maior contributo para um tempo de execuc¸ ˜ao excessivo.

No final da computac¸ ˜ao ´e necess ´ario reunir toda a informac¸ ˜ao para gerar o resultado pretendido, o que normalmente corresponde a fazer uma operac¸ ˜ao de recolha de informac¸ ˜ao sobre todos os proces- sadores. Esta operac¸ ˜ao pode ser feita para um array global ou apenas local, dependendo do objetivo e dos constrangimentos de mem ´oria. Em ambos os casos pode ser feita para um ´unico processador, por exemplo com reduce, ou para todos, com allreduce. Novamente, a decis ˜ao depende fortemente do problema e dos objetivos em quest ˜ao.

E conveniente ilustrar esta estrutura gen ´erica com um exemplo. Considere-se o exemplo trivial de ´ um programa que calcula o quadrado de cada elemento de um vetor v com n elementos. O c ´odigo s ´erie ser ´a algo como:

do i=1,n

v(i) = v(i)*v(i) end do

Vejamos como podem estas orientac¸ ˜oes ser aplicadas `a paralelizac¸ ˜ao deste exemplo

20

. Inicial- mente, a leitura de input (como exemplo usamos o modelo root + broadcast):

if(rank==root) allocate v(n)

20Para facilitar a compreens ˜ao e evitar detalhes desnecess ´arios usamos pseudo-c ´odigo baseado emF ortran.

(22)

read v end if

Note-se que apenas ´e preciso alocar o vetor global no rank onde vai ser lido. A distribuic¸ ˜ao pode ent ˜ao ser feita com um scatter em vez de broadcast, definindo a dimens ˜ao dos vetores locais n pp e alocando-os:

n_pp = n/n_proc allocate (v_pp(n_pp))

call scatter (n_pp elementos de v para v_pp)

Cada processo tem a sua carga e pode comec¸ar a computac¸ ˜ao independente:

do i=1,n_pp

v_pp(i) = v_pp(i)*v_pp(i}

end do

Neste exemplo n ˜ao ´e preciso combinar os resultados dos diferentes processos (fazendo um reduce), mas apenas agreg ´a-los. Reunindo a informac¸ ˜ao de todos os processos, voltamos a obter o vetor global:

call gather (n_pp elementos de v_pp para v no rank root)

Apesar de simples, este exemplo da paralelizac¸ ˜ao de um loop ilustra a estrutura fundamental de um programa paralelo: leitura de input, distribuic¸ ˜ao da carga computacional, execuc¸ ˜ao da computac¸ ˜ao em cada processador e reuni ˜ao final dos resultados.

2.5.3 Dicas

Em conjunto com esta estrat ´egia global, ´e importante ter presente um conjunto de apontamentos que podem facilitar o processo de paralelizac¸ ˜ao:

• preservar reversibilidade - ´e frequente cair na tentac¸ ˜ao de paralelizar uma parte demasiado ex- tensa do c ´odigo logo `a partida, sem nenhum tipo de verificac¸ ˜ao peri ´odica, e chegar a um ponto em que o c ´odigo semi-paralelo/semi-s ´erie n ˜ao corre ou n ˜ao d ´a os resultados esperados. Nesse sentido, ´e boa pr ´atica preservar a reversibilidade do programa para a vers ˜ao s ´erie durante a paralelizac¸ ˜ao, e remover essa reversibilidade apenas ap ´os testar o funcionamento da vers ˜ao paralela. Na pr ´atica, uma maneira de fazer isto ´e comec¸ar por incluir todo o programa num if(rank==root), de forma a preservar a serialidade, e ir progressivamente paralelizando porc¸ ˜oes do c ´odigo e retirando-as do if. A tabela 2.7 apresenta um exemplo gen ´erico desta soluc¸ ˜ao. Note- se que ela implica que a porc¸ ˜ao paralelizada (no exemplo, work mesh pp) devolva os mesmos par ˆametros que o original n ˜ao paralelizado (work mesh, o que implica usar os arrays globais e portanto usar mais espac¸o de mem ´oria do que o necess ´ario; por ´em, essa situac¸ ˜ao pode ser corrigida `a posteriori removendo a operac¸ ˜ao de reduce do work mesh pp, pelo que esta ´e uma estrat ´egia ´util para a parte inicial da paralelizac¸ ˜ao e que n ˜ao compromete o resultado final.

call create mesh call work mesh call print mesh

(a) S ´erie

if(rank==root) call create mesh call work mesh call print mesh end if

(b) Paralelo I

if(rank==root) call create mesh end if

call work mesh pp if(rank==root)

call print mesh end if

(c) Paralelo II

Tabela 2.7: Preservac¸ ˜ao da reversibilidade durante a paralelizac¸ ˜ao.

(23)

• minimizar comunicac¸ ˜ao - independentemente do modelo de programac¸ ˜ao, ´e sempre conveniente reduzir ao m´ınimo a comunicac¸ ˜ao entre processos de forma a evitar overheads excessivos que podem comprometer a viabilidade da paralelizac¸ ˜ao.

• usar comunicac¸ ˜oes non-blocking - caso a topologia do programa exija um elevado volume de comunicac¸ ˜oes, deve ser considerada a hip ´otese de usar vers ˜oes non-blocking das rotinas de comunicac¸ ˜ao, de forma a que os processos possam prosseguir em tarefas de computac¸ ˜ao em vez de ficar `a espera da conclus ˜ao da comunicac¸ ˜ao. Esta soluc¸ ˜ao acarreta um esforc¸o acrescido de sincronizac¸ ˜ao dos processos para evitar o overwrite de dados.

• minimizar I/O - tipicamente processos de I/O s ˜ao uma fonte de demora no programa, podendo criar bottlenecks caso muitos processos tentem aceder ao mesmo conjunto de dados ao mesmo tempo, pelo que, tal como as comunicac¸ ˜oes, devem ser minimizados de forma a evitar overheads.

Adicionalmente, sempre que poss´ıvel devem ser escritos largos volumes de dados poucas vezes, em vez de pequenos volumes muitas vezes, para maximizar a efici ˆencia.

• considerar outros algoritmos - ´e natural a tend ˆencia para manter o algoritmo s ´erie original e intro- duzir apenas as alterac¸ ˜oes necess ´arias para a execuc¸ ˜ao em paralelo, mas esta pode n ˜ao ser a melhor soluc¸ ˜ao em termos de tempo. Se poss´ıvel, devem ser considerados algoritmos alternati- vos que se adequem melhor a um ambiente paralelo (idealmente essa investigac¸ ˜ao deve ser feita antes de se comec¸ar a paralelizac¸ ˜ao, para evitar esforc¸o desnecess ´ario).

• usar bibliotecas - em geral existem v ´arias bibliotecas com rotinas que se adequam ao problema espec´ıfico a ser tratado. Este software deve ser pesquisado e utilizado por forma a acelerar o programa e minimizar trabalho desnecess ´ario. Dois exemplos s ˜ao as bibliotecas ParMETIS, para partic¸ ˜ao de malhas n ˜ao-estruturadas em paralelo, e o ScaLAPACK, uma biblioteca de ´algebra linear.

• distribuic¸ ˜oes de arrays n ˜ao uniformes - ´e frequente que, ao dividir um array global por v ´arios pro- cessadores, os subarrays fiquem com dimens ˜oes diferentes (basta a dimens ˜ao do array n ˜ao ser m ´ultipla do n ´umero de processadores). S ˜ao tamb ´em frequentes operac¸ ˜oes de gather ou scatter entre esses arrays, e um erro comum ´e tentar utilizar as func¸ ˜oes MPI Gather ou MPI Scatter para isso, o que resulta num erro porque essas func¸ ˜oes apenas podem ser usadas para arrays de dimens ˜oes iguais. As func¸ ˜oes adequadas a este caso s ˜ao MPI GatherV e MPI ScatterV.

• manter nomenclatura inteligente - ´e relativamente f ´acil, particularmente numa fase inicial, que a paralelizac¸ ˜ao d ˆe origem a tantos novos vetores e operac¸ ˜oes que se torne confuso para o progra- mador saber o que ´e o qu ˆe. Para minimizar confus ˜oes ´e conveniente adotar uma nomenclatura inteligente que permita ao programador perceber imediatamente se determinado vetor ´e local ou global, se tem algum equivalente local/global, etc. Uma sugest ˜ao para isso ´e acrescentar o sufixo pp aos elementos paralelizados, e manter a nomenclatura original para os s ´erie. Uma outra forma

´e apresentada na secc¸ ˜ao 4.3 no ˆambito do caso de estudo; v ´arias formas s ˜ao poss´ıveis, desde

que cumpram o objetivo priorit ´ario de facilitar a leitura e compreens ˜ao do c ´odigo.

(24)

Referências

Documentos relacionados

Como já destacado anteriormente, o campus Viamão (campus da última fase de expansão da instituição), possui o mesmo número de grupos de pesquisa que alguns dos campi

Equipamentos de emergência imediatamente acessíveis, com instruções de utilização. Assegurar-se que os lava- olhos e os chuveiros de segurança estejam próximos ao local de

•   O  material  a  seguir  consiste  de  adaptações  e  extensões  dos  originais  gentilmente  cedidos  pelo 

A Tabela 3 apresenta os resultados de resistência ao impacto Izod e as caracterizações térmicas apresentadas em função dos ensaios de HDT, temperatura Vicat e a taxa de queima do

Este trabalho buscou, através de pesquisa de campo, estudar o efeito de diferentes alternativas de adubações de cobertura, quanto ao tipo de adubo e época de

A prova do ENADE/2011, aplicada aos estudantes da Área de Tecnologia em Redes de Computadores, com duração total de 4 horas, apresentou questões discursivas e de múltipla

17 CORTE IDH. Caso Castañeda Gutman vs.. restrição ao lançamento de uma candidatura a cargo político pode demandar o enfrentamento de temas de ordem histórica, social e política

O enfermeiro, como integrante da equipe multidisciplinar em saúde, possui respaldo ético legal e técnico cientifico para atuar junto ao paciente portador de feridas, da avaliação