Escolar Documentos
Profissional Documentos
Cultura Documentos
Tutorialparelelismo
Tutorialparelelismo
1
Computação de Alto Desempenho
Abstract
The following text consists of the class material of the Laboratory of High Perfor-
mance Computing, shown in XI ERBASE 2011. The text is divided into topics: The
main innovation of parallel computing; The art of parallel programming via exam-
ples; and Current trends in research and technology in Parallel, Distributed and Grid
computing.
Resumo
O seguinte texto consiste no material didático das sessões das aulas de Laboratório de
Computação de Alto Desempenho, apresentadas no XI ERBASE 2011. Este docu-
mento esta dividido em tópicos que abordam: Os principais avanços em computação
paralela; A arte de programar em paralelo através de exemplos; e As tendências
atuais em pesquisa e tecnologia de Computação Paralela, Distribuı́das e de Grids.
1.1. Introdução
Podemos afirmar que o processamento paralelo era utilizado desde o surgi-
mento dos primeiros computadores na década de 50 [1]. Em 1955, o IBM 704 inclui
um hardware para processamento de números de ponto flutuante (co-processador).
Em 1956, a IBM lança o projeto 7030 (conhecido como STRETCH) para produzir
um “supercomputador” para o Los Alamos National Laboratory (LANL). O objeti-
vo, na época era construir uma máquina com capacidade computacional 100 vezes
1 Colegiado de Engenharia da Computação (CECOMP), Universidade Federal do Vale do Sao
Franscisco, Juazeiro, Bahia, Brasil, murilo.boratto@univasf.edu.br
2 Departamento de Informática y Sistemas (DIS), Universidad de Murcia, Murcia, Espanha,
domingo@um.es
3 Núcleo de Arquitetura de Computadores e Sistemas Operacionais (ACSO), Universidade do
Memória Compartilhada
O modelo de Memória Compartilhada (Shared Memory Model) será iden-
tificado quando existir uma porção de memória que possa ser acessada dire-
tamente por todos os elementos de um conjunto de processos. Esta memória
será utilizada para transferência de informação entre os mesmos. Este tipo de
modelo corresponde a sistemas que possuem um conjunto de memória com-
partilhada com todos os processadores envolvidos, onde a memória estaria
distribuı́da no sistema, entre os distintos processadores. Existem ferramentas
especı́ficas de programação em memória compartilhada. As mais conhecidas
são pthreads [5], Threading Building Blocks [6], OpenMP [7, 3, 8]. Esta última
pode ser considerada, na atualidade, o padrão para este tipo de programação
e será alvo de estudo deste curso.
Memória Distribuı́da
No modelo de Memória Distribuı́da (Distributed Memory Model) cada pro-
cessador tem associado um bloco de memória próprio. Assim, cada elemento
pode acessar indiretamente um dos blocos de memórias associados a outros
processadores. Desta forma, para conseguir a troca de dados é necessário que
cada processador realize explicitamente a solicitação de dados aos processado-
res disponı́veis, que serão os responsáveis pelo envio dos dados (i.e., resposta).
Este modelo se baseia na técnica de Passagem de Mensagem. Existem vá-
rios ambientes de programação para esse modelo (e.g., PVM [9], BSP [10]) e
o estándar atual chama-se MPI [4, 11], o qual será abordado neste seminário.
1.3.2. Necessidade da Computação Paralela
A necessidade da Computação Paralela é provocada pelas limitações de ex-
pansão dos computadores seqüenciais (i.e.,limites fı́sicos de hardware) e possibilida-
de de expansão da capaciadade de processemanto mediante agrupação de diferentes
computadores: integrando múltiplos processadores pode-se executar vários processos
simultaneamente para solucionar problemas que exigem mais memória ou um maior
poder de computação.
Outro fator que justifica a necessidade da computação paralela é a relação
custo/benefı́cio proporcionada. Há razões econômicas para que o preço dos compu-
tadores seqüenciais não seja proporcional à sua capacidade de computação. Para
adquirir uma máquina com o dobro de capacidade computacional, normalmente é
necessário o investimento de mais que o dobro do valor da mesma. Já na computação
paralela, a conexão de múltiplos processadores através de uma rede de interconexão
permite a obtenção do aumento no desempenho de forma proporcional ao número
de processadores envolvidos, com um custo mı́nimo adicional.
Estas caracterı́sticas, aliadas as novas e crescentes demandas das aplicações
emergentes (e.g., Vı́deo sob Demanda, Processamentos Bio-fı́sicos, Simulações de
tempo real,...), que demandam quantidades de recursos computacionais elevadas
dificultam a sobrevida de sistemas sequenciais.
A programação paralela é uma solução para resolver estes problemas, mas
apresenta outras dificuldades. Alguns desafios são fı́sicos, tais como: dificuldade em
integração de componentes; dissipação de calor associados; e aumento da comple-
xidade no acesso aos dados. Estas questões podem se tornar pontos de estrangu-
lamento, o que irá tornar difı́cil a obtenção de bons desempenhos, porém, se bem
administrados, podem aumentar a capacidade computacional do sistema.
Há também problemas lógicos, tais como: maior dificuldade no desenvolvi-
mento de compiladores; e existência de um ambiente eficiente de programação para
sistemas paralelos. Estes problemas são mais complexos de serem resolvidos em sis-
temas paralelos que em sistemas seqüencias: programar em paralelo é muito mais
complexo e difı́cil do que programar seqüencialmente. Não obstante, um programa
em paralelo é utilizado para reduzir a resolução temporal de problemas computacio-
nais, para resolver problemas de grande escala que não podem ser resolvidos por um
processo seqüencial. Para isto, faz-se necessário a utilização da computação de alto
desempenho através de algoritmos paralelos que utilizem os sistema de forma eficaz
e eficiente.
Os problemas que são tratados em paralelo são, em geral, de um tipo especı́fi-
co: problemas de alto custo computacional (i.e., problemas que demandam utilização
de grandes quantidades de recursos computacionais como memória, processamento
e armazenamento); e, problemas que envolvem determindado prazo máximo de exe-
cução (i.e., problemas de tempo real). Assim, a comunidade cientı́fica utiliza compu-
tação paralela para resolver estes problemas que, sem a computação paralela, seriam
inviáveis de serem solucionados.
Como exemplo de algumas áreas do conhecimento que podem ser beneficiadas
com a utilização da programação paralela, dentre outros, podemos citar: estudos
meteorológicos, através das previsões e estudos da climatologia; o estudo do genoma
humano, a modelagem da biosfera; as predições sı́smicas; e a simulação de moléculas.
para permitir que a utilização da mesma por mais de uma instrução (paralelismo de instrução) em
paralelo, imitando a linha de montagem das industrias
um conjunto de processadores que operam de forma paralela e sı́ncrona, exe-
cutando normalmente a mesma função. Os processadores de uma máquina
vetorial são chamados de Unidades de Processamento (EP) e trabalham sob a
supervisão de uma única Unidade de Controle (UC).
É também usual encontrar co-processadores de entrada/saı́da. Estes compo-
nentes hardware permitem operações de E/S simultánemente com as operações
de processamento (i.e., computação).
Esta lista, ainda que, não muito extensa, dá idéia da importância da noção
de paralelismo e da sua utilização no desenho de arquiteturas seqüenciais para ace-
lerar o processamento. O estudo detalhado da arquitetura dos computadores e da
utilização do paralelismo tanto em sistemas seqüenciais tanto em paralelos não se-
rá abordada neste curso, mas há inúmeros livros que abordam este tema de uma
maneira exaustiva e podem ser consultados em [12, 13, 14].
Por outro lado, a lei de Moore ([15, 16]) diz que o a capacidade de processa-
mento dos processadores integrados dobra a cada 18 meses. Isto produz um incre-
mento na velocidade de execução de programas, mas se observarmos,comprovamos
que este aumento é conseguido, na atualidade, apenas pelos processadores Dual Core
da Intel, que incluem dois núcleos e que necessitam portanto da programação para-
lela de forma explı́cita para poder obter os máximos desempenhos que estes sistemas
podem oferecer. Esse tipo de processadores são usados como componentes básicos
nos computadores que são comercializados na atualidade, o que nos permite afir-
mar que a programação básica nos processadores atuais necessita da programação
paralela como base para explorar toda a potencialidade oferecida pelo hardware.
O modelo que utilizamos neste curso é o MIMD, que é o que segue os mul-
ticomputadores atuais, não importando o paradigma de programação: por memó-
ria compartilhada ou por envio de mensagens. Também, consideraremos o modelo
SPMD (Single Program Multiple Data), modelo em que todos os threads ou pro-
cessos executam o mesmo programa más sem sincronizar a execução das instruções:
cada elemento do processo executa as instruções no seu próprio ritmo, mesmo que
em alguns pontos pode haver sincronização dos processos. Nas sessões seguintes
analisaremos a programação paralela com OpenMP e MPI para sistemas homogê-
neos, apesar que esse tipo de programação também usa como base outros sistemas
já anteriormente mencionados, e analisaremos alguns exemplos de combinações de
OpenMP e MPI para a Programação Hı́brida.
1.4. Programação OpenMP
OpenMP é um padrão atual para a programação utilizando memoria compar-
tilhada, que incluem os sistemas multicores e computadores de altas prestações com
memoria virtual compartilhada. Nesta sessão analisaremos as caracterı́sticas básicas
do OpenMP utilizando os exemplos que se encontram em http://www.paraninfo.es/.
Z 1
1 π
2
dx =
0 1+x 4
Uma das soluções adotadas seria aproximar a área da integral a áreas de
retângulos de uma certa base. Quanto menor for a base, mais retângulos teremos,
logo haverá uma melhor aproximação ao valor final da área. O código código3-
1.c é um programa seqüencial para este problema, que contém um loop for que se
acumulam as áreas dos retângulo com os que se aproximam da integral.
Uma versão paralela do mesmo problema para OpenMP pode-se encontrar no
código código3-16.c. Se consegue incluir ao código o paralelismo, apenas indicando
a forma com que se deve distribuir o trabalho dos loop as diferentes threads. Aqui
aparecem algumas interfaces C de OpenMP:
Cada um dos threads que trabalha na região paralela possui seu identifi-
cador de thread (que esta entre 0 y OMP_NUM_THREADS-1) usando a função
omp_get_thread_num, e o guarda em sua copia local de tid.
Por outro lado, todos obtém o número de threads que existe na região cha-
mando a omp_get_num_threads, que devolve o número de threads que se esta
executando dentro de uma região paralela. Caso se chama-se estaúltima função
desde uma região seqüencial o resultado seria 1, pois somente se está execu-
tando o thread mestre. Como todos escrevem o mesmo valor, a ordem em que
os threads se atualiza nthreads não importa; mas o acesso a variável compar-
tilhada supondo um tempo de execução adicional pela gestão do sistema de
acesso as variáveis compartilhadas.
Executam-se duas regiões paralelas, e todo o código que esta fora delas se
executa em seqüencial pela thread mestre.
if (expressao-escalar)
private (lista-de-variaveis)
shared (lista-de-variaveis)
firstprivate (lista-de-variaveis)
copyin (lista-de-variaveis)
#include <omp.h>
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("Numero de threads = %d\n", nthreads);
}
}
omp_set_num_threads(3);
nthreads = omp_get_num_threads();
printf("Numero de threads en execuçao = %d", nthreads);
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("Numero de threads = %d", nthreads);
}
}
}
private (lista-de-variaveis)
firstprivate (lista-de-variaveis)
lastprivate (lista-de-variaveis)
nowait
#include <omp.h>
} //sections
} //parallel section
}
A execução do código anterior produz a seguinte saı́da, sendo que cada thread foi
atribuı́do a uma das sessões paralelas, com um número total de threads igual a 4.
6 é
uma coleção de máquinas que se utilizam das redes de computadores comerciais, locais e/ou
remotas para paralelizar suas transações
paralelas utilizando passagem de mensagem.
Inicialmente o MPI não continha algumas facilidades implementadas no PVM,
como: os módulos de tolerância a falhas e a orientação a clusters, mas com o tempo
foram feitas algumas evoluções e hoje já existe MPI orientado a tolerância a falhas
(FT-MPI [19]) e a sistemas heterogéneos (HeteroMPI [20], MPICH-Madeleine [21]).
Nesta sessão analisaremos as caracterı́sticas gerais da especificação padrão
do MPI, publicada em 1994. Esta especificação foi desenvolvida pelo MPI Forum,
formado por um conjunto de universidades e empresas que especificaram as funções
que deveriam estar contidas na biblioteca de passagem por mensagem. A partir dessa
especificação os fabricantes de multicomputadores incluı́ram implementações espe-
cificas para seus equipamentos, aparecendo varias implementações livres, as versões
mais difundidas são: MPICH [22] e LAM-MPI [23]. O MPI vem evoluindo e também
podemos encontrar versões MPI2 [24] e OpenMPI [25], que é uma distribuição de
código aberto do MPI2.
Assim, o MPI possibilitou:
Caso seja iniciada a execução (i.e., execução do comando mpirun) a partir do host 01
os 4 processos seriam atribuı́dos as máquinas seguindo a ordem do arquivo de má-
quinas e sua configuração. Neste caso 3 processos no host 01 e 1 processo no host 02.
A forma de atribuição desses processos segue uma ordem cı́clica padrão do MPI.
Também é possı́vel lançar a execução especificando os processos por linha de
comando sendo argumentos do mpirun
mpirun n0,1,2,3 AloMundo
onde está sendo lançado 4 processos que são atribuı́dos aos hosts 0, 1, 2 e 3. Esta
forma de trabalhar é tı́pica do LAMMPI. Outra diferença no lançamento dos pro-
cessos MPI utilizando a versão LAMMPI, é que antes de inicializarmos os processos
temos que formar uma rede de execução através do comando lamboot e somente
depois executamos o mpirun.
buf contém o inicio da zona de memória do dado a ser enviado ou onde vai ser
armazenado em sua recepção. count contém o número de dados a ser enviado
ou o espaço disponı́vel para receber.
Para mandar blocos de dados diferentes aos diferentes processos deve-se utilizar
int MPI_Alltoall(void *sendbuf, int sendcount,
MPI_Datatype sendtype, void *recvbuf, int recvcount,
MPI_Datatype recvtype, MPI_Comm comm). Para cada processo i, o
bloco j será enviado ao processo j, que o armazena como bloco i em recvbuf.
[2] Francisco Almeida, Domingo Giménez, José Miguel Mantas, and Antonio M.
Vidal. Introducción a la programación paralela. Paraninfo Cengage Learning,
2008.
[4] Message Passing Interface Forum. MPI: A Message Passing Interface Standard.
Univ. of Tennessee, Knoxville, Tennessee, 1995.
[5] Bradford Nichols, Dick Buttlar, and Jacqueline Proulx Farrel. Pthreads pro-
gramming: A Posix Standard for Better Multiprocessing. O’Reilly, 1996.
[7] Rohit Chandra, Ramesh Menon, Leo Dagum, David Kohr, Dror Maydan, and
Jeff McDonald. Parallel Programming in OpenMP. Morgan Kauffman, 2001.
[8] Rohit Chandra, Ramesh Menon, Leo Dagum, David Kohr, Dror Maydan,
and Jeff McDonald. OpenMP C and C++ Application Program Interfa-
ce. OpenMP Architecture Review Board. http://www.openmp.org/drupal/mp-
documents/cspec20.pdf, 2002.
[11] Marc Snir and William Gropp. MPI. The Complete Reference. 2nd edition.
The MIT Press, 1998.
[15] Gordon Moore. Cramming more components onto integrated circuits. Electro-
nics Magazine, 1965.
[16] Robert R. Schaller. Moore’s law: past, present, and future. IEEE Spectrum,
34:52–59, 1997.
[17] M. J. Flynn. Some computer organizations and their effectivness. IEEE Tran-
sactions on Computers, 21:948–960, 1972.
[27] Barry Wilkinson and Michael Allen. Parallel Programming: Techniques and
Applications Using Networked Workstations and Parallel Computers. Prentice-
Hall, second edition, 2005.
[28] Michael J. Quinn. Parallel Programming in C with MPI and OpenMP. McGraw
Hill, 2004.
[29] Peter Pacheco. Parallel Programming with MPI. Morgan Kaufmann Publishers,
1997.