Buscar

Plataformas de Computação Paralela

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

2
PLATAFORMAS PARA COMPUTAÇÃO
PARALELA
As limitações de desempenho de um sistema computacional estão relacionadas prin-
cipalmente à velocidade do processador e à capacidade de acesso e armazenamento em
memória. Baseado nestes elementos, as plataformas computacionais foram classificadas.
Este capítulo visa apresentar as principais plataformas de computação paralela e distribuída
existentes e quais as limitações dos recursos computacionais para se obter alto desem-
penho. As informações apresentadas estão baseadas em [Quinn (2004), Grama (2003),
Jaja (1992), Pitanga (2002)].
2.1 PARALELISMO IMPLÍCITO
A velocidade de clock dos microprocessadores tem apresentado um aumento expres-
sivo - duas ou três ordens de magnitude - em relação a 20 anos atrás. Entretanto, este
aumento na velocidade do clock é diluído pelas limitações da tecnologia de memória. Ao
mesmo tempo, níveis mais altos de integração de dispositivos têm também resultado em
bom desempenho. Conseqüentemente, técnicas que permitem a execução de múltiplas
instruções em um único ciclo de clock têm se tornado populares.
Processadores podem explorar o paralelismo em nível de instrução ou ILP (Instruction-
level parallelism) para tentar melhorar o desempenho do processador por ter múltiplos
componentes de processamento ou unidades funcionais executando instruções simulta-
neamente. Há duas técnicas principais de ILP: pipeline, onde unidades funcionais são
arranjadas em estágios e múltiplas execuções, onde múltiplas instruções podem ser simul-
taneamente iniciadas.
Na técnica de pipeline, a sobreposição de vários estágios de execução de instruções
(busca, escalonamento, decodificação, busca de operando, execução, armazenamento, en-
tre outros), em pipelines, permite uma execução mais rápida. Fazendo analogia com uma
linha de produção de um carro, se a produção de um carro levar 100 unidades de tempo,
esta produção pode ser quebrada em 10 estágios de pipeline com 10 unidades cada. As-
sim, uma única linha de produção pode produzir um carro a cada 10 unidades de tempo.
18 DCC/UFLA - Programação Paralela e Distribuída
Isto representa uma aceleração (speedup) de 10 vezes na produção de um carro totalmente
seqüencial, um após o outro. Desta forma, o aumento do desempenho para um único pi-
peline é obtido através da divisão das tarefas em unidades menores (estágios), formando
o pipeline e aumentando a sobreposição na execução da tarefa. Considere a Figura 2.1
que apresenta um pipeline de 5 estágios, onde cada instrução é representada como tarefa.
Pode-se observar que cada tarefa foi dividida em busca, decodificação, execução, acesso
a memória e resultado. No tempo 1, a Tarefa 1 executa a busca da instrução. No tempo 2
começa sua decodificação, enquanto a Tarefa 2 inicia a busca da instrução. No tempo 3, a
Tarefa 1 realiza a execução, a Tarefa 2 a decodificação e a Tarefa 3 a busca da instrução.
Estes passos são realizados até se obter o resultado. Se cada tarefa fosse executada de
forma sequencial, ou seja, se fossem realizados todos os estágios da Tarefa 1 e somente
depois da Tarefa 2 e assim por diante, seriam necessárias 20 unidades de tempo para exe-
cutar as 4 tarefas. Utilizando o pipeline são necessários apenas 8 unidades de tempo, pois
há estágios das tarefas que são realizados de forma concorrente.
Figura 2.1: Pipeline de 5 estágios
No contexto de processadores isto permite taxas de clock mais rápidas já que as tare-
fas são agora menores. Por exemplo, a arquitetura Core tem um pipeline de 14 estágios. A
velocidade de um único pipeline é limitado pela maior tarefa atômica no pipeline. Entretanto,
em instruções típicas, toda quinta ou sexta instrução é uma instrução de desvio. Pipelines
de instruções longas precisam de técnicas eficientes para prever destino de desvios tal que
pipelines possam ser preenchidos especulativamente. A penalidade de uma previsão incor-
reta aumenta quando os pipelines tornam-se mais profundos já que um grande número de
instruções precisam ser desfeitas. Estes fatores colocam limitações na profundidade de um
pipeline de processador.
Uma forma de melhorar a taxa de execução de instrução além deste nível é usar
múltiplos pipelines. Durante cada ciclo de clock, múltiplas instruções são colocadas em um
pipe no processador em paralelo. Estas instruções são executadas em múltiplas unidades
funcionais.
Plataformas para Computação Paralela 19
Por exemplo, se há dois somadores de ponto flutuante, a execução gastará aproxima-
damente a metade do tempo para executar o loop
for (1 = 0; i < 1000; i++) z[i] = x[i] + y[i];
Enquanto o primeiro somador está computando z[0], o segundo pode computar z[1].
Enquanto o primeiro está computando z[2], o segundo pode computar z[3] e assim por di-
ante.
Considere, por exemplo, um processador com 2 pipelines e a habilidade de simultane-
amente executar duas instruções. Este tipo de processador recebe o nome de processador
super-pipelined. A habilidade de um processador tratar múltiplas instruções no mesmo ci-
clo é referida como execução superescalar. Em um programa as instruções podem estar
relacionadas. Então, os resultados de uma instrução podem ser requeridos por instruções
subseqüentes (dependência de dados verdadeira). Outro tipo de dependência ocorre, por
exemplo, quando duas instruções utilizam a mesma unidade de ponto de flutuante (depen-
dência de recursos). Na execução de um desvio condicional, o destino do desvio somente
é conhecido no momento da execução e o escalonamento de instruções, a priori, através
de desvios pode gerar erros (dependência de desvio ou dependência procedural).
A habilidade de um processador detectar e escalonar instruções concorrentes é crí-
tica para o desempenho superescalar. Para isso é necessário que as instruções sejam
escalonadas fora de ordem. Neste caso, o paralelismo é explorado em nível de instrução.
O desempenho de arquiteturas superescalares é limitado pela disponibilidade de pa-
ralelismo em nível de instrução.
O paralelismo em nível de thread ou TLP (Thread-level parallelism) tenta prover para-
lelismo através de execuções simultâneas de diferentes threads. As unidades do programa
que estão sendo executadas simultaneamente (threads) são maiores do que as instruções
individuais.
2.2 LIMITAÇÕES DE DESEMPENHO DO SISTEMA DE MEMÓRIA
O desempenho de um programa em um computador depende da velocidade do pro-
cessador e também da habilidade do sistema de memória para fornecer dados ao proces-
sador. Em nível lógico, um sistema de memória, possivelmente consistindo de múltiplos
níveis de cache, realiza uma requisição por uma palavra de memória e retorna um bloco de
dados de tamanho b contendo a palavra requisitada após l nanossegundos, onde l é a la-
tência da memória. A taxa em que o dado pode ser copiado da memória para o processador
determina a banda do sistema de memória.
Considere um processador operando a 1GHz (1ns de clock) conectado a uma DRAM
com uma latência de 100ns (sem cache). Assuma que o processador tenha 2 unidades
multiplicação-adição e seja capaz de executar quatro instruções em cada ciclo de 1ns. O
20 DCC/UFLA - Programação Paralela e Distribuída
processamento máximo é de 4 GFLOPS (Giga FLoating point Operations per Second). Já
que a latência de memória é igual a 100 ciclos e o tamanho de bloco é de uma palavra, toda
vez que uma requisição de memória é realizada, o processador deve esperar 100 ciclos
antes que ele possa processar o dado. Considere o problema de computar o produto de dois
vetores em tal plataforma. Uma computação de produto realiza uma multiplicação-adição
de um único par de elementos do vetor, isto é, cada operação de ponto flutuante requer
uma busca de dado. É fácil verificar que a velocidade de pico destacomputação é limitada
a uma operação de ponto flutuante a todo 100ns, ou à velocidade de 10MFLOPS, uma fração
muito pequena da taxa máxima do processador. Este exemplo destaca a necessidade de
desempenho do sistema de memória em alcançar altas taxas de computação.
A presença de caches pode melhorar o desempenho baseado na hipótese de que há
repetidas referências a um mesmo item de dados. Esta noção de referência repetida a um
item de dados em uma janela de tempo pequena é chamada de localidade temporal. A
reutilização de dados é crítica para o desempenho de caches porque se cada item de dado
é usado somente uma vez, ele teria de ser buscado uma vez por uso para a DRAM e a
latência da DRAM ocorreria para cada operação.
A banda de memória refere-se à taxa em que os dados podem ser movidos entre o
processador e a memória. É determinada pela largura de banda do barramento da memória
bem como pela unidade de memória. Uma técnica muito usada para melhorar a largura
de banda da memória é aumentar o tamanho dos blocos de memória. Para exemplificar,
assuma que uma única requisição de memória retorna um bloco contíguo de 4 palavras. A
única unidade de 4 palavras neste caso é referida como uma linha de cache. Computadores
convencionais tipicamente buscam dois a oito palavras juntas na cache.
Considere um sistema de memória com um único ciclo de cache e 100 ciclos de
latência DRAM com o processador operando a 1 GHz. Se o tamanho de bloco é de uma
palavra, o processador leva 100 ciclos para buscar cada palavra. Para cada par de palavras,
o produto de vetores realiza uma multiplicação-adição, isto é, 2 FLOPs. Além disso, o
algoritmo realiza um FLOP a todo 100 ciclos para uma velocidade de pico de 10 MFLOPS.
Considere que o tamanho do bloco é aumentado para 4 palavras, isto é, se o proces-
sador puder buscar uma linha de cache de 4 palavras a cada 100 ciclos. Assuma que os
vetores são colocados de forma linear em memória, 8 FLOPs (4 operações multiplicação-
adição) podem ser realizadas em 200 ciclos. Isto é porque um único acesso de memória
busca 4 palavras consecutivas no vetor. Além disso, 2 acessos podem buscar 4 elementos
de cada um dos vetores. Isto corresponde a um FLOP a cada 25 ns, para uma velocidade
de pico de 40 MFLOPS. Note que o aumento no tamanho do bloco de um para 4 palavras
não mudou a latência do sistema de memória. Entretanto, aumenta a banda de memória
em 4 vezes. Neste caso, o aumento da banda do sistema de memória permite-nos acelerar
o algoritmo do produto de vetores que não tem dados para serem reutilizados.
Plataformas para Computação Paralela 21
O cenário ilustrado anteriormente corresponde a um barramento de dados largo (4
palavras ou 128 bits) conectado a múltiplos bancos de memória. Na prática, tal largura de
barramento é cara para se construir. Em um sistema mais prático, palavras consecutivas
são enviadas ao barramento de memória em ciclos de barramento subseqüentes após a
primeira palavra ser recuperada. Por exemplo, com um barramento de dados de 32 bits,
a primeira palavra é colocada no barramento depois de 100ns (latência associada) e uma
palavra é coloca em cada ciclo de barramento subseqüente.
Quando os dados consecutivos em memória são usados por instruções sucessivas
há uma localidade espacial de acesso à memória. Se a computação não tem localidade
espacial, então a banda pode ser muito menor do que a banda máxima.
Um exemplo de tal padrão de acesso é ler uma matriz densa por coluna quando a ma-
triz seria armazenada em memória por linha. Compiladores podem ser programados para
fazer um bom trabalho reestruturando a computação para aproveitar a localidade espacial.
Considere o fragmento de código:
1 for (i = 0; i < 1000; i++){
2 column_sum[i] = 0.0;
3 for (j = 0; j < 1000; j++)
4 column_sum[i] += b[j][i];
}
O fragmento de código soma as colunas da matriz b em um vetor column_sum. Duas
observações importantes podem ser feitas: (i) o vetor column_sum é pequeno e pode
facilmente caber na cache e (ii) a matriz b é acessada na ordem das colunas. Para uma
matriz de tamanho 1000 x 1000, armazenada na ordem das colunas, isto corresponde a
acessar todas as 1000 entradas, como ilustra a Figura 2.2(a).
Pode ocorrer que somente uma palavra de cada linha de cache buscada na memória
seja usada. Conseqüentemente, o fragmento de código produzirá um desempenho ruim.
A falta de localidade espacial em computação causa um desempenho ruim do sistema de
memória. Mas freqüentemente é possível reestruturar a computação para remover acesso
freqüente. Desta forma, o fragmento de código pode ser reescrito para que a matriz seja
varrida por linhas e não por colunas. O código reestruturado fica como segue:
1 for (i=0; i < 1000; i++)
2 column_sum[i] = 0.0;
3 for (j=0; j < 1000; j++){
4 for (i=0; i<1000; i++)
5 column_sum[i] += b[j][i];
}
22 DCC/UFLA - Programação Paralela e Distribuída
Figura 2.2: Multiplicação de uma matriz por um vetor: (a) multiplicação de coluna por coluna; (b)
multiplicação de linha por coluna
Neste caso, a matriz é varrida na ordem das linhas, como ilustrado na Figura 2.2(b).
Pode-se notar que o vetor column_sum pode ser mantido em cache durante a execução
do loop.
Este exemplo mostrou que os seguintes conceitos são importantes:
• Explorar localidade espacial e temporal em aplicações é crítico para amortizar a
latência de memória e aumentar o uso efetivo de banda de memória.
• Certas aplicações têm localidade temporal maior do que outras e têm maior tole-
rância à baixa banda de memória. A taxa entre o número de operações e o número
de acessos à memória é um bom indicador de tolerância antecipada para banda de
memória.
• A organização da memória e a organização da computação de forma apropriada
podem gerar um impacto significativo na localidade espacial e temporal.
2.2.1 Estratégias alternativas para esconder latência de memória
Suponha que um usuário esteja sentado em frente seu computador navegando pela
web durante um período de alto tráfego de rede. O desenvolvedor de um navegador pode
implementá-lo de forma que o tempo de resposta seja reduzido. Para isso, existem 3 estra-
tégias simples:
1. prefetching: antecipar quais páginas serão acessadas informando quais as requi-
sições que serão realizadas;
Plataformas para Computação Paralela 23
2. multithreading: abrir múltiplos navegadores e acessar diferentes páginas em cada
navegador. Enquanto espera-se que uma página seja carregada pode-se ler outras
páginas ou
3. localidade espacial: acessar um conjunto de páginas de uma vez só - amortizando
a latência de vários acessos.
Multithreading para esconder latência: Uma thread é um fluxo de execução de um
programa. Considere o exemplo de multiplicação de matriz por vetor para ilustrar threads.
Considere o seguinte código para multiplicar uma matriz a, n x n, por um vetor b, obtendo
um vetor c.
1 for (i=0; i < n; i++)
2 c[i] = dot_product(get_row(a,i),b);
Este código computa cada elemento de c como produto escalar da linha correspon-
dente de a pelo vetor b. Note que cada produto escalar é independente do outro e repre-
senta uma unidade de execução concorrente. Este segmento de código pode ser reescrito
como segue:
1 for (i=0; i < n; i++)
2 c[i] = create_thread(dot_product, get_row(a,i),b);
A única diferença entre os dois segmentos de código é que temos explicitamente es-
pecificada cada instância da computação do produto escalar como sendo uma thread. Con-
sidere a execução de cada instância da função dot_product. A primeira instância desta
função acessa um par de elementos do vetor e espera por eles. Enquanto isso, a segunda
instância desta função pode acessar dois outros elementos do vetor no próximo ciclo e as-
sim por diante. Apósl unidades de tempo, onde l é a latência do sistema de memória, a
primeira instância da função obtém o dado requisitado da memória e pode realizar a com-
putação requisitada. No próximo ciclo, os itens de dado para a próxima instância da função
são retornados e assim por diante. Desta forma, em todo ciclo de clock, pode-se realizar
uma computação.
A maneira como é realizada a execução deste exemplo pode ser analisada sob dois
aspectos: o sistema de memória é capaz de servir múltiplas requisições pendentes e o
processador é capaz de escalonar uma thread diferente a cada ciclo. Além disso, ele tam-
bém requer que o programa tenha uma especificação explícita de concorrência na forma de
threads.
Prefetching para reduzir latência: Em programa, um dado é carregado e usado por
um processador em uma pequena janela de tempo. Se o carregamento (load) do dado
resultar em um cache miss (dado não está em memória), então a execução é interrom-
pida para buscar o dado em disco. Uma solução simples para este problema é antecipar
a operação de carregamento (load) do dado tal que mesmo que haja um cache miss, o
dado chegue normalmente no tempo de ser usado. Entretanto, se o dado já tiver sido so-
24 DCC/UFLA - Programação Paralela e Distribuída
brescrito entre o carregamento e a utilização, uma atualização torna-se necessária. Note
que isso não é pior do que a situação em que o carregamento não tenha sido antecipado.
Uma análise cuidadosa desta técnica revela que prefetching funciona pela mesma razão
de multithreading. Antecipando o carregamento, estamos tentando identificar threads inde-
pendentes de execução que não tenham dependência de recursos entre si (isto é, usam os
mesmos registradores).
Considere o problema de adicionar dois vetores a e b usando um único loop for.
Na primeira iteração do loop, o processador requisita a[0] e b[0]. Como estes valores
não estão em cache, o processador deve esperar, havendo latência de memória. Enquanto
estas requisições estão sendo atendidas, o processador também requisita a[1] e b[1].
Assumindo que cada requisição é gerada em um ciclo de (1 ns) e as requisições de me-
mória são satisfeitas em 100 ns, após 100 requisições o primeiro conjunto de dados é
retornado pelo sistema de memória. Subseqüentemente, um par de componentes do vetor
será retornado a cada ciclo. Desta forma, em cada ciclo subseqüente, uma adição pode ser
realizada e ciclos de processador não são desperdiçados.
Uma desvantagem da utilização de prefetching e multithreading é que há necessidade
de mais memória. As requisições de memória em um sistema multithreaded podem aumen-
tar significativamente porque a cache residente de cada thread é pequena. Por exemplo,
considere que uma máquina tenha 32KB de memória cache e há a execução multithreaded
com 32 threads. Então, cada thread terá uma cache residente de 1KB, pois cada thread
terá maior necessidade de buscar dados em memória.
Em computadores paralelos a memória pode estar distribuída em diferentes computa-
dores, como é o caso dos clusters, ou compartilhada pelos processadores, em uma mesma
máquina. No caso da memória distribuída, somente o processador do computador que
possui o dispositivo de memória terá acesso aos dados. Os demais processadores locali-
zados em outros computadores deverão solicitar os dados através da troca de mensagens.
No caso da memória compartilhada, todos os processadores terão acesso a esta memó-
ria para leitura e escrita. Para não haver inconsistência de dados deverão ser adotadas
políticas de consistência de memória.
2.3 CLASSIFICAÇÃO DAS PLATAFORMAS PARALELAS
As plataformas paralelas podem ser classificadas de acordo com as características de
hardware ou com o fluxo de instruções e dados.
Considerando a perspectiva de memória, tem-se os modelos de memória distribuída
(distributed), memória compartilha (shared) e memória compartilhada-distribuída (distributed-
shared).
Plataformas para Computação Paralela 25
2.3.1 Modelo de memória distribuída
No modelo de memória distribuída, encontram-se os cluster de computadores pes-
soais ou estações de trabalho (workstations) que são também conhecidos como NOW
(Network of Workstations) ou cluster Beowulf.
Um cluster Beowulf é uma coleção de computadores e switches, localizados no mesmo
recinto, dedicados para execução de jobs paralelos. Os computadores tipicamente não pos-
suem teclado nem monitores, sendo acessados somente via rede. Todos os computadores
executam a mesma versão de sistema operacional e possuem imagens de disco local idên-
ticas. O cluster inteiro é administrado como uma entidade. A rede pode ser fast Ethernet
(100Mbit/sec), gigabit Ethernet (1000Mbit/sec) e Myrinet (1920Mbit/sec).
A Figura apresenta um exemplo de um cluster com 8 nós de processamento e um
servidor que realiza acesso externo. Todos os nós de processamento podem ser acessados
somente pelo servidor, que deverá ter firewall configurado, evitando ataques externos. Cada
nó de processamento não necessita ter mouse, teclado e vídeo, pois o acesso é realizado a
partir do servidor. Há softwares apropriados para cluster que realizam o escalonamento dos
programas para serem executados nos nós de processamento: Condor, PBS, LSF, SGE,
entre outros. Além disso, os softwares para monitoramento dos nós têm conhecimento de
quais nós estão ativos e poderão receber um programa para ser executado.
Figura 2.3: Cluster Beowulf
Uma rede de estações de trabalho é uma coleção de computadores distantes uns dos
outros e switches, tipicamente localizados na mesa do usuário. São tipicamente conec-
tados via Ethernet (10Mbit/sec) ou fast Ethernet (100 Mbit/sec). O objetivo de uma rede
de estações de trabalho é servir às necessidades da pessoa que a utiliza; a execução de
um job paralelo é simplesmente uma maneira para consumir ciclos de CPU desperdiçados.
Estações de trabalho individuais podem ter diferentes sistemas operacionais e programas
26 DCC/UFLA - Programação Paralela e Distribuída
executáveis. Um usuário pode desligar sua estação de trabalho, então há necessidade de
suporte para realizar checkpoint e reiniciar jobs.
Os clusters podem ser classificados, de acordo com sua finalidade, em: cluster de alta
disponibilidade, de alto desempenho e de balanceamento de carga.
Cluster de alta disponibilidade O cluster de alta disponibilidade tem a finalidade de man-
ter um determinado serviço de forma segura o maior tempo possível. A demanda deste tipo
de cluster ocorre em bancos, empresas financeiras e de seguros, serviço público, empresas
de comércio eletrônico, indústrias, entre outros. Os computadores possuem algum recurso
para detectar, recuperar e ocultar eventuais falhas. Os recursos computacionais pertencen-
tes a essa classe possuem uma disponibilidade entre 99,99% a 99,999%, ou seja, em um
ano de operação poderá existir indisponibilidade por um período de pouco mais de cinco
minutos. A Figura 2.4 ilustra este tipo de cluster, onde há dois servidores: um primário e um
secundário. Estes dois servidores são cópias idênticas um do outro. Caso o primário apre-
sente algum problema e o programa HeartBeat (verifica se o servidor está on-line) identifica
que o servidor primário está offline, o servidor secundário torna-se o primário e continua a
execução. O sistema de armazenamento de dados é compartilhado entre entres.
Figura 2.4: Cluster de Alta Disponibilidade.
Cluster de alto desempenho O cluster de alto desempenho é voltado para prover grande
poder computacional. Neste tipo de cluster são estudados algoritmos de processamento
paralelo e construção de aplicações paralelas distribuídas. O objetivo é diminuir o tempo
para a resolução de problemas computacionais. O primeiro cluster Beowulf construído tinha
este objetivo.Plataformas para Computação Paralela 27
Cluster de balanceamento de carga O cluster de balanceamento de carga tem como
finalidade distribuir carga entre servidores de modo que um único servidor não fique so-
brecarregado e outros sem carga. A Figura 2.5 ilustra este tipo de cluster onde há dois
computadores balanceadores que recebem as requisições e distribui entre os 3 servidores
que irão atender as requisições.
Figura 2.5: Cluster de Balanceamento de Carga
2.3.2 Modelo de memória compartilhada
O modelo de memória compartilhada possui a visão do espaço de endereçamento
compartilhado com todos os processadores. Os processadores interagem modificando da-
dos armazenados no espaço de endereçamento compartilhado. As plataformas de memória
compartilhada que suportam programação SPMD Single Program Multiple Data, operam
sobre múltiplas instâncias do mesmo programa executando sobre dados diferentes. Tais
plataformas são também conhecidas como multiprocessadas. A memória em uma plata-
forma de espaço de endereçamento compartilhado pode ser local (exclusiva a um proces-
sador) ou global (comum a todos os processadores). Se o tempo gasto por um processador
acessar qualquer palavra da memória no sistema (global ou local) é idêntico, a plataforma
é classificada como um multicomputador de acesso uniforme à memória - UMA (Uniform
Memory Access). Por outro lado, se o tempo gasto para acessar certas palavras na me-
28 DCC/UFLA - Programação Paralela e Distribuída
mória for maior do que outras, a plataforma é chamada de multicomputador de acesso
não-uniforme à memória - NUMA (Non-Uniform Memory Access).
Um multiprocessador UMA pode também ser denominado multiprocessador centrali-
zado ou multiprocessador simétrico (SMP - Symmetric Multiprocessor).
Um multiprocessador NUMA pode ser também denominado multiprocessador distri-
buído.
Em um multiprocessador UMA, os dados privados são os dados usados somente por
um único processador, enquanto os dados compartilhados são os dados usados por múlti-
plos processadores. Os processadores comunicam com os demais através de variáveis de
dados compartilhados e variáveis de controle para sincronização entre os processadores.
Semáforos e monitores que foram originalmente projetados para uniprocessadores e multi-
processadores são exemplos de como a sincronização pode ser alcançada em sistemas de
memória compartilhada.
A Figura 2.6 apresenta uma visão esquemática deste tipo de multiprocessador com
memória cache e sem memória cache.
(a)
(b)
Figura 2.6: Multiprocessador UMA (a) com cache e (b) sem cache
As desvantagens de um multiprocessador UMA são que (i) o barramento de memória
compartilhada limita o número de CPUs e (ii) podem surgir problemas associados a dados
compartilhados como coerência de cache e sincronização.
Plataformas para Computação Paralela 29
UMA - Coerência de cache
Coerência de cache é o problema existente para manter consistência entre as có-
pias do dado na memória global e as caches locais dos processadores. Esta consistência
pode ser realizada através da replicação de dados entre as múltiplas caches, reduzindo a
contenção entre processadores para valores de dados compartilhados. Mas quando cada
processador tem uma visão da memória em sua cache, é necessário que seja garantido
que processadores diferentes não tenham valores diferentes para a mesma locação de me-
mória.
A Figura 2.7 ilustra o problema de coerência de cache. Pode-se observar que em (a)
a locação de memória x contém o valor 7. Em (b) a CPU A lê o valor x e uma cópia de x
é armazenada na cache da CPU A. Em (c) a CPU B lê o valor de x e uma cópia de x é
armazenada na cache da CPU B. Até este ponto, as CPUs A e B possuem o mesmo valor,
7. Em (d) a CPU B armazena 2 em x. A locação de memória x (global) recebe um novo
valor. O valor é também atualizado na cache da CPU B. Entretanto, a CPU A tem um valor
antigo de x em sua cache.
Figura 2.7: UMA: problema de coerência de cache
Para resolver o problema de coerência de cache, pode-se adotar as políticas write-
through ou write-back.
Write-through: os dados escritos em cache são atualizados imediatamente na memó-
ria global.
Write-back: os dados escritos em cache só são escritos na memória global em caso
de troca do bloco da cache.
Os protocolos snooping são usados para manter coerência de cache em multipro-
cessadores UMA. Cada controlador de cache da CPU monitora (snoops) o barramento
para identificar que blocos de cache estão sendo requisitados por outras CPUs. A solu-
ção mais comum para o problema de coerência de cache é garantir que um processador
tenha acesso exclusivo a um item de dado antes de escrever seu valor. Antes de realizar
30 DCC/UFLA - Programação Paralela e Distribuída
a escrita, todas as cópias de itens de dados armazenados em cache de outras CPUs são
invalidados. Neste ponto, o processador realiza a escrita, atualizando o valor neste bloco
de cache e na locação de memória apropriada. Quando qualquer outra CPU tenta ler uma
locação de memória daquele bloco de cache, ocorrerá um cache miss, forçando-o a re-
cuperar o valor atualizado da memória. Isto é chamado de protocolo write invalidate. Se
dois processadores tentarem, simultaneamente, escrever na mesma locação de memória,
somente um deles vence a disputa. O bloco de cache do processador que perde a disputa
é invalidado. O processador que perde deve obter uma nova cópia do dado (com o valor
atualizado) antes que ele possa fazer sua escrita.
UMA - Sincronização
Vários tipos de sincronização podem ser necessários pelos processos cooperantes
para realizar uma computação:
• Exclusão mútua: situação em que no máximo um processo pode realizar uma ati-
vidade específica em qualquer momento.
• Barreira: muito utilizado em programas para memória compartilhada. Uma sincro-
nização por barreira garante que nenhum processo continuará a execução, num
ponto específico do programa, (chamado barreira) até que todos os processadores
tenham alcançado este ponto.
NUMA
Em um multiprocessador NUMA, cada processador possui uma memória próxima a
ele e o espaço de endereçamento é distribuído. Isto permite a existência de um número
maior de processadores. Desta forma, o acesso à memória local é muito mais rápido do
que o acesso à memória não local. A Figura 2.8 (a) ilustra a arquitetura do multiprocessador
NUMA. Esta figura não considera que cada CPU possui uma memória cache. A Figura 2.8
(b) ilustra a situação onde cada CPU tem uma memória cache, além da memória local.
Uma desvantagem deste tipo de multiprocessador é o tempo de acesso a um bloco
de memória, pois depende de onde ele está: em uma memória próxima ou distante do
processador.
Na Figura 2.8(b)um dado que está em cache pode estar incoerente com o dado que
está na memória local, que é compartilhada. Para realizar a consistência de cache, o
multiprocessador NUMA utiliza um protocolo baseado em diretório. Neste protocolo há
um único diretório que contém a informação de compartilhamento de todos os blocos de
memória que podem estar em cache. Para cada bloco de cache, a entrada no diretório
indica um dos seguintes estados do bloco:
Plataformas para Computação Paralela 31
(a)
(b)
Figura 2.8: Multiprocessador NUMA: (a) sem cache e (b) com cache
• uncached - não está atualmente na cache de nenhum dos processadores;
• shared - está na cache de um ou mais processadores e a cópia em memória global
é a mais atual;
• exclusive - está em cache de exatamente um processador que escreveu no bloco,
mas a cópia na memória global está obsoleta.
É necessário manter o acompanhamento de que processador tem cópias de qual-
quer bloco de cache, tal que estas cópias possam ser invalidadas quando um processador
escreverum valor para aquele bloco.
Para prevenir que o acesso ao diretório de cache torne-se um gargalo de desempenho,
o diretório deve ser distribuído entre as memórias locais do computador. Entretanto, os
conteúdos não são replicados: a informação sobre um bloco de memória particular está
exatamente em uma locação de memória.
32 DCC/UFLA - Programação Paralela e Distribuída
Considere o exemplo do protocolo baseado em diretório de um multiprocessador de
memória distribuída simples mostrado na Figura 2.9. (a) x tem o valor 7. Bloco contendo x
não está em cache. (b) Estado após CPU 0 ler x. (c) Estado após CPU 2 ler x. (d) Estado
após CPU 0 escrever valor 6 em x. (e) Estado após CPU 1 ler x. (f)Estado após CPU 2
escrever 5 em x. (g) Estado após CPU 0 escrever 4 em x. (h) Estado após CPU 0 apagar o
bloco de cache contendo x.
Na Figura 2.9 (a), o computador tem 3 CPUs: CPU 0, CPU 1 e CPU 2. Com cada
processador há associada uma memória cache, uma memória local e um diretório. No mo-
delo NUMA, as memórias locais dos processadores formam um espaço de endereçamento
único e qualquer CPU pode se referir a qualquer um destes endereços. A variável X está
armazenada na memória controlada pela CPU 2. Ela contém o valor 7. CPU 2 tem uma
entrada no diretório correspondente ao bloco de cache contendo X: "U000". Esta entrada
mostra que atualmente o bloco não está em cache (uncached).
Suponha que CPU 0 tente ler o valor de X. O bloco de cache contendo X não está na
cache da CPU 0. Uma mensagem read miss é enviada de CPU 0 para CPU 2. O status do
bloco de cache contendo X em CPU 2 é trocado para shared, o vetor de bits é atualizado
para mostrar que CPU 0 tem uma cópia do bloco de cache ("S100") e o bloco é enviado
para CPU 0 (Figura 2.9 b).
Em seguida, CPU 2 tenta ler o valor de X. O valor de X não está na cache da CPU 2.
Como resultado do read miss, o valor de X é copiado para a cache de CPU 2 (Figura 2.9 c).
Suponha que CPU 0 escreva 6 na variável X. Uma mensagem write miss é enviada de
CPU 0 para CPU 2. O controlador de diretório invalida a cópia do bloco de cache atualmente
na cache da CPU 2, atualiza o vetor de bits para mostrar que CPU 2 não tem mais uma
cópia do bloco e troca o estado do bloco de cache para exclusive ("E000"). A Figura 2.9 d
mostra o novo estado do sistema. Note que o valor de X em memória local de CPU 2 está
desatualizado.
Se CPU 1 tentar ler X, gerando um read miss, o controlador de diretório para CPU
2 envia uma mensagem switch to shared para CPU 0, que envia uma cópia do bloco de
cache de volta para CPU 2, de forma que fique uma cópia atualizada em memória local.
Então o bloco atualizado é enviado para CPU 1 (Figura 2.9 e).
Suponha que a próxima ação envolvendo o bloco de cache que está na CPU 2 escre-
vendo 5 a X. Já que o bloco não está mais em cache, ele gera uma mensagem de cache
miss de volta para o controlador de diretório. O controlador de diretório envia uma mensa-
gem de invalidação para CPU 0 e CPU 1, que removem os blocos de suas caches. Agora
um status do bloco é trocado para exclusive com owner CPU 2. Uma cópia do bloco é
enviada para cache da CPU 2 e CPU 2 atualiza o valor de X (2.9 f).
Em seguida, CPU 0 tenta escrever o valor 4 a X. Já que o bloco apropriado não está
em sua cache, CPU 0 gera uma mensagem write miss para o diretório de CPU 2, que
Plataformas para Computação Paralela 33
(a) (b)
(c) (d)
(e) (f)
(g) (h)
Figura 2.9: Ilustração do protocolo baseado em diretório para implementar coerência de cache em
um multiprocessador distribuído.
envia uma mensagem take away para controlador de cache de CPU 2. O bloco de cache é
copiado de volta para a memória. O estado do bloco permanece exclusive, mas o vetor de
34 DCC/UFLA - Programação Paralela e Distribuída
bits é atualizado para mostrar que o owner agora é CPU 0. Uma cópia do bloco é enviada
para cache de CPU 0 e o valor de X é atualizado (Figura 2.9 g).
Finalmente, suponha que CPU 0 decida limpar o bloco de cache contendo X. Já que
ele tem acesso exclusivo ao bloco, deve copiar o conteúdo do bloco de volta à memória de
CPU 2. O estado final é ilustrado na Figura 2.9 h.
A presença de um espaço de memória global torna a programação de computadores
com espaço de endereçamento de memória compartilhada muito mais fácil. Todas as in-
terações realizadas somente para leitura são invisíveis ao programador, quando elas são
codificadas igual a um programa seqüencial. Isto facilita a escrita de programas paralelos.
Interações de leitura e escrita são mais complicadas para programar do que interações so-
mente leitura, pois estas operações requerem exclusão mútua para acesso concorrente. Os
paradigmas de programação para espaço de endereçamento compartilhado tais como thre-
ads (POSIX, NT) e diretivas (OpenMP) suportam sincronização usando locks e mecanismos
relacionados.
É importante notar a diferença entre dois termos que são muito usados: computadores
de espaço de endereçamento compartilhado-distribuído e computadores de memória com-
partilhada. O termo computador de memória compartilhada é historicamente usado para
arquiteturas em que a memória está fisicamente compartilhada entre vários processadores,
isto é, cada processador tem acesso a qualquer segmento de memória. Isto é idêntico ao
modelo UMA. Isto é o contrário de computador com memória compartilhada-distribuída, em
que diferentes segmentos de memória são fisicamente associados a diferentes elementos
de processamento. Um computador com espaço de endereçamento compartilhado com
memória distribuída é idêntico a uma máquina NUMA.
2.3.3 Sistema de memória compartilhada multicore
Em um sistema de memória compartilhada com múltiplos processadores multicore a
interconexão pode conectar todos os processadores diretamente à memória principal ou
cada processador pode ter uma conexão direta ao bloco de memória principal e os proces-
sadores podem acessar os blocos de memória principal dos outros através de hardware
especial construído nos processadores. As figuras 2.10 e 2.11 ilustram um sistema multi-
core UMA e um NUMA, respectivamente. No primeiro tipo de sistema, o tempo de acesso
a todas as locações de memória será o mesmo para todos os cores. No segundo tipo, o
acesso a uma locação de memória para um core diretamente conectado à memória é mais
rápido do que o acesso de um core localizado em outro chip.
Plataformas para Computação Paralela 35
Figura 2.10: Um sistema multicore UMA [Pacheco (2011)]
Figura 2.11: Um sistema multicore NUMA [Pacheco (2011)]
36 DCC/UFLA - Programação Paralela e Distribuída
2.3.4 Comparação entre espaço de endereçamento compartilhado, distribuído e com-
partilhado-distribuído
A classificação das plataformas computacionais em plataforma de espaço de endere-
çamento compartilhado, espaço de endereçamento compartilhado distribuído e passagem
de mensagens refere-se à forma como o programa visualiza o acesso à memória da má-
quina paralela.
Na plataforma de espaço de endereçamento compartilhado, todos os processadores
acessam umamemória comum a eles. Na Figura 2.12 pode-se observar que há umamemó-
ria compartilhada que pode ser acessada por qualquer processador através do barramento.
Na Figura 2.13 pode-se observar que a memória que é local a cada processador é visuali-
zada pelo programador como uma memória compartilhada, representada por um retângulo
com linha mais grossa. O acesso a posições de memória necessita de uma camada de
software que deixe transparente para o desenvolvedor a troca de mensagens entre os pro-
cessadores para atualização das posições de memória. Na Figura 2.14, pode-se observar
que cada processador está em uma máquina diferente e a memória é local a esta máquina.
O programador somente consegue compartilhardados entre os processadores fazendo a
troca de mensagens de forma explícita. Não há a noção de memória compartilhada.
Figura 2.12: Multiprocessador UMA
Vantagens e desvantagens
Espaço de endereçamento compartilhado
Plataformas para Computação Paralela 37
Figura 2.13: Multiprocessador NUMA
Figura 2.14: Cluster
38 DCC/UFLA - Programação Paralela e Distribuída
As vantagens da programação para esta plataforma é que o tempo gasto para um
processador acessar uma palavra da memória no sistema é idêntico, de forma que a latên-
cia de acesso é igual para todos os processadores. Além disso, nesta arquitetura é fácil
programar, pois não é necessário realizar troca de mensagens de forma explícita para ter
acesso a um dado.
Entre as desvantagens podemos citar que o barramento de acesso à memória com-
partilhada limita o número de CPUs que podem ser colocadas em uma mesma máquina.
Podem surgir problemas associados a dados compartilhados como coerência de cache e
sincronização.
Espaço de endereçamento compartilhado distribuído
As vantagens desta plataforma é que permite a utilização de maior número de pro-
cessadores, aproveitando recursos computacionais já existentes. O programador tem uma
camada de software que deixa a troca de mensagens entre os processadores transparente
para ele. Desta forma, o programador pode programar da mesma forma que no espaço de
endereçamento compartilhado.
Como desvantagens pode-se citar o fato de haver uma grande quantidade de troca
de mensagens para o compartilhamento dos dados entre os processadores; o tempo de
acesso a um bloco de memória que não está local pode ser alto e o protocolo de coerência
de cache é mais complexo.
Espaço de endereçamento distribuído
As vantagens desta plataforma é que podem ser utilizadas máquinas conectadas em
rede para obter alto desempenho, como ocorre com os clusters de estações de trabalho. O
número de máquinas que podem ser utilizadas é ilimitado.
Como desvantagem, o programador precisa realizar a troca de mensagens de forma
explícita para obter os dados, usando uma biblioteca de passagem de mensagens.
Emulando passagem de mensagens em um sistema de memória compartilhada
O espaço de endereçamento compartilhado pode ser particionado em partes disjun-
tas, uma parte sendo atribuída a cada processador. Operações "send"e "receive"podem
ser implementadas pela escrita e leitura no espaço de endereçamento dos processadores
receptor e emissor. Especificamente, uma locação separada pode ser reservada como uma
caixa de mensagens para cada par ordenado de processos. Uma passagem de mensagens
Pi−Pj pode ser emulada por uma escrita de Pi na caixa de mensagens e uma leitura de Pj
da caixa de mensagens. O caso mais simples de uma caixa de mensagens assume que o
tamanho é ilimitado. Operações de leitura e escrita precisam ser controladas usando primi-
tivas de sincronização para informar ao receptor/emissor que o dado foi enviado/recebido.
Plataformas para Computação Paralela 39
Emulando memória compartilhada em um sistema de passagem de mensagens
Envolve o uso de operações "send"e "receive"para operações de escrita e leitura.
Cada locação compartilhada pode ser modelada como um processo separado; escrever
para uma locação compartilhada é emulada pelo enviado de uma mensagem de atualiza-
ção para o processo owner correspondente; uma leitura de uma locação compartilhada é
emulada pelo envio de uma mensagem de consulta do processo owner. Como acessar a
memória de outro processador requer operações de envio e recebimento, esta emulação
é cara. Portanto, esta emulação em um sistema distribuído envolve altas latências nas
operações de escrita e leitura.
Uma aplicação pode usar uma combinação de memória compartilhada e passagem
de mensagens. Em sistemas multiprocessadores, os processadores comunicam-se via
memória compartilhada. Entre dois computadores, a comunicação é feita por passagem de
mensagens.
2.3.5 Taxonomia de Flynn
A taxonomia de Flynn é uma classificação que depende do paralelismo exibido no seu
fluxo de instrução e no seu fluxo de dados. Os processadores podem executar o mesmo ou
diferentes fluxos de instrução ao mesmo tempo e processar ou não os mesmos (idênticos)
dados ao mesmo tempo. Assim, um processo pode ser visto como a execução de uma
seqüência de instruções (fluxo de instruções) que manipulam uma seqüência de operandos
(fluxo de dados). O foco é na multiplicidade de hardware usado para manipular o fluxo de
instruções e de dados. Desta forma, Flynn classificou os computadores em 4 categorias:
• SISD (Single Instruction Stream, Single Data Stream);
• SIMD (Single Instruction Stream, Multiple Data Stream);
• MISD (Multiple Instruction Stream, Single Data Stream) e
• MIMD (Multiple Instruction Stream, Multiple Data Stream).
A Figura 2.15 ilustra o fluxo de dados e de instruções destas categorias.
2.3.6 SISD
A arquitetura SISD refere-se aos computadores com um único fluxo de instrução e um
único fluxo de dados. Um sistema clássico de von Neumann é um sistema SISD. Corres-
ponde a computadores com uma única CPU e uma única unidade de memória conectada
por um sistema de barramento. Apesar de ter somente uma única CPU (Central Processing
Unit) executando um único fluxo de instruções, um uniprocessador pode ainda exibir alguma
concorrência de execução. Por exemplo, arquiteturas superescalares suportam identifica-
ção dinâmica e seleção de múltiplas operações independentes que podem ser executadas
40 DCC/UFLA - Programação Paralela e Distribuída
Figura 2.15: Fluxo de instruções e fluxo de dados na 4 categorias definidas por Flynn.
simultaneamente. Instrução de prefetching e execução pipeline de instruções são outros
exemplos de concorrência tipicamente encontrados em computadores modernos do tipo
SISD.
2.3.7 SIMD
A categoria SIMD refere-se a múltiplos processadores homogêneos que executam
sobre diferentes dados. Uma única unidade de controle despacha instruções para cada
unidade de processamento. Em um computador paralelo SIMD, a mesma instrução é exe-
cutada sincronamente por todas as unidades de processamento. A execução do Exemplo
1 neste modelo, ocorreria com o despacho da instrução add para todos os processadores
seguido de sua execução concorrente por eles.
Estas arquiteturas são ideais para trabalhar com estruturas de dados regulares como
vetores e matriz, aumentando o desempenho de aplicações que envolvem sistemas de
equações, análise de imagem e reconhecimento de padrões. Como exemplo de aplicações,
pode-se citar: aerodinâmica, sismologia, meteorologia, física nuclear, química molecular e
dinâmica dos fluidos. Tais aplicações necessitam de alta precisão, operam com grandes
matrizes e/ou vetores, realizam repetitivas operações com dados representados em ponto
Plataformas para Computação Paralela 41
flutuante e geralmente consistem em problemas envolvendo modelagem 3D. Neste tipo de
modelagem, as superfícies são aproximadas por uma malha de pontos e uma série de
equações define o comportamento de cada ponto. Geralmente, as mesmas equações são
utilizadas para operar sobre o conjunto completo de pontos. Este tipo de característica pode
ser explorada pelas arquiteturas SIMD, uma vez que esta permite a operação da mesma
instrução sobre um grande conjunto de dados.
Propriedades importantes de instruções vetoriais:
• o cálculo de cada resultado é independente do cálculo dos demais, permitindo a
utilização de pipelines profundos;
• uma única instrução vetorial é responsável por uma grande fatia de trabalho;
• o atraso no acesso à memória não causa maiores problemas devido à possibilidade
de operação com dados em endereços contíguos. A organização da memória pode
tirar proveito da estrutura dos dados;
• as dependênciasde controle que ocorreriam em uma estrutura do tipo laço, não
existem, uma vez que o comportamento da instrução vetorial é pré-determinado.
Os elementos principais da arquitetura são apresentados a seguir.
• Registradores vetoriais: cada registrador vetorial é um banco de tamanho fixo con-
tendo apenas um vetor. Por exemplo, considere uma arquitetura com 8 registrado-
res de 64 elementos cada;
• Unidades funcionais vetoriais: cada unidade é totalmente pipeline e pode iniciar
uma nova operação a cada ciclo de clock. Na arquitetura exemplo, existem 5 uni-
dades funcionais, capazes de operar valores escalares.
• Unidade load-store vetorial: totalmente pipeline, é capaz de acessar 1 dado a cada
ciclo de clock, após a latência inicial.
• Registradores escalares: são os registradores de uso geral, utilizados para o cál-
culo de endereço e operações comuns.
Os primeiros computadores que pertenciam a esta categoria compreendiammáquinas
vetoriais e processadores matriciais.
• Máquinas vetoriais (Vector Processors): a mesma operação é executada em para-
lelo sobre todos os elementos de um vector (em células de memória ou em regis-
tadores) (Exemplo: CRAY).
• Processadores matriciais (Array Processors): a execução das instruções, por um
conjunto de processadores, é controlada por uma unidade de controle única, mas
cada processador tem as suas células de memória/registadores privados (Exem-
plos: ILLIAC IV, 64 processadores, 1970; Connection Machine (CM-1), 64.000 pro-
cessadores, 1985).
42 DCC/UFLA - Programação Paralela e Distribuída
Recentemente, arquiteturas SIMD incluem unidades de co-processamento tais como
as unidades MM em processadores Intel (por exemplo, Pentium com as opções SSE (Stre-
aming SIMD Extensions)) e chips DSP como o Sharc.
A Figura 2.16 apresenta uma arquitetura SIMD típica. Nesta figura, pode-se obser-
var que há uma única unidade de controle para todos os processadores; há também uma
interconexão de rede entre os processadores. Cada processador possui sua memória local.
Figura 2.16: Arquitetura SIMD
Recentemente, as unidades de processamento gráfico ou GPUs (Graphics Proces-
sing Units) possuem característica de computação SIMD, mas não são puramente SIMD.
Embora as ALUs de um dado core use paralelismo SIMD, a geração atual de GPUs pod ter
dezenas de cores, que são capazes de executar fluxos de instruções independentes.
2.3.8 MISD
A categoria MISD é para computadores com múltiplos fluxos de instrução e um único
fluxo de dados.Um computador MISD é "um pipeline de múltiplas unidades funcionais exe-
cutando independentemente operandos sobre um único fluxo de dados, passando resulta-
dos de uma unidade funcional para a seguinte".
Um vetor systolic encaixa-se nesta categoria. A palavra systole refere-se à contração
do coração. Um vetor systolic é uma rede de elementos de processamento primitivos que
bombeia (pump) dados.
Plataformas para Computação Paralela 43
Por exemplo, considere uma ordenação primitiva de elementos, onde o ordenador tra-
balha em duas fases. Na primeira fase ele entra com 3 valores de dados (a, b, c). Na
segunda fase saem os valores mínimo (min(a,b,c)), médio (med(a,b,c)) e máximo (max(a,b,c)).
Pode-se criar uma fila de prioridade em hardware relacionada a um vetor linear destes ele-
mentos ordenados. A fila de prioridade suporta duas operações: inserir uma chave e extrair
a chave com o valor mínimo. Cada operação leva dois ciclos, isto é, tempo constante.
Para inserir a chave x, o processador host insere x e -∞ à esquerda da fila de priori-
dade durante o primeiro ciclo. No segundo ciclo a fila sai -∞ no seu lado esquerdo. O host
descarta este valor.
Par extrair a chave mínima, o host insere duas cópias da chave ∞ durante o primeiro
ciclo e extrai a chave mínima durante o segundo ciclo. Para todas as operações a chave ∞ é
inserida do lado direito do vetor systolic durante o primeiro ciclo de clock. No segundo ciclo
de clock, as cópias de ∞ devem sair do lado direito do vetor systolic. Se uma das chaves
não é ∞, a fila de prioridade tem overflow.
Neste caso, todos os elemento do vetor systolic são idênticos. Entretanto, um vetor
systolic pode conter uma variedade de elementos e desempenhar diferentes funções.
Os sistemas paralelos baseados em princípios MISD foram desenvolvidos para apli-
cações particulares, tais como processamento de sinais digitais.
2.3.9 MIMD
Computadores com múltiplos fluxos de instrução e múltiplos fluxos de dados. Multi-
processadores e multicomputadores entram nesta categoria. Diferentes CPUs podem si-
multaneamente executar diferentes fluxos de instruções manipulando diferentes fluxos de
dados.
A Figura 2.17 apresenta uma arquitetura MIMD típica.
Uma variante deste modelo, chamada de SPMD (Single Program Multiple Data, opera
sobre múltiplas instâncias do mesmo programa executando sobre dados diferentes. O mo-
delo SPMD tem a mesma expressividade pois múltiplos programas podem ser inseridos
em um bloco i f − else grande com condições especificadas pelos identificadores da tarefa.
Exemplos de tais plataformas incluem Servidores Sun Ultra, PCs multiprocessados, clusters
de estações de trabalho e o IBM SP.
Os computadores SIMD requerem menos hardware do que os computadores MIMD
porque eles têm somente uma unidade de controle global. Além disso, computadores SIMD
requerem menos memória porque somente uma cópia do programa precisa ser armaze-
nada. Em computadores MIMD é necessário armazenar o programa e o sistema operacio-
nal em cada processador. Plataformas suportando o paradigma SPMD podem ser construí-
das de componentes mais baratos com esforço relativamente pequeno em um curto tempo.
Computadores SIMD requerem esforço de projeto extenso resultando em tempo de desen-
44 DCC/UFLA - Programação Paralela e Distribuída
Figura 2.17: Arquitetura MIMD
volvimento de produto mais longo. A rápida mudança de processadores seriais e a natureza
irregular de muitas aplicações faz com que as arquiteturas SIMD não sejam apropriadas.
2.4 PROGRAMAÇÃO EM COMPUTADORES PARALELOS
Atualmente, computadores paralelos estão mais disponíveis do que nunca, mas para
aproveitar bem os múltiplos processadores, programadores e/ou compiladores devem ser
capazes de identificar operações que podem ser realizadas em paralelo. Para isso, o pro-
gramador deve identificar a dependência entre dados e entre tarefas. Além disso, é preciso
saber como agrupar as tarefas de forma a explorar o máximo de paralelismo.
Após a identificação de paralelismo em um problema, o próximo passo é desenvolver
um algoritmo implementado em uma linguagem de programação.
Em 1988, McGraw e Axelrod identificaram quatro formas distintas para o desenvolvi-
mento de aplicações de software para computadores paralelos:
1. Estender um compilador existente para traduzir programas seqüenciais em progra-
mas paralelos;
2. Estender uma linguagem existente com novas operações que permitem aos usuá-
rios expressar paralelismo;
Plataformas para Computação Paralela 45
3. Adicionar uma nova camada de linguagem paralela no topo de uma linguagem
seqüencial existente;
4. Definir uma nova linguagem paralela e um sistema compilador totalmente novos.
Estender um compilador existente: Uma forma de resolver o problema da pro-
gramação paralela de computadores é o desenvolvimento de compiladores paralelos que
possam detectar e explorar o paralelismo em programas existentes escritos em uma lin-
guagem seqüencial. Dependendo da forma como o programador implementa o código, o
compilador pode ter dificuldades em identificar o paralelismo de um segmento de código.
Para não ocorrer esta situação, uma solução é permitir ao programador anotar o programa
seqüencial com diretivas de compilador para fornecerao compilador informações de forma
que ele possa paralelizar corretamente os segmentos do programa. Foram realizadas vá-
rias pesquisas nesta área e um exemplo é um compilador, desenvolvido pela empresa Pa-
rallel Software Products, que traduz o código do Fortran 77 em programas paralelos para
arquiteturas tanto de passagem de mensagens quanto de memória compartilhada.
Estender uma linguagem de programação seqüencial: Um forma mais conser-
vative para desenvolver um ambiente de programação paralela é estender uma linguagem
de programação seqüencial com funções que permitam ao programador criar e terminar
processos paralelos, sincronizá-los e permitir que eles se comuniquem. Deve também ha-
ver uma maneira de distinguir entre dados públicos (compartilhados entre os processos) e
dados privados (para que cada processo tenha uma cópia).
Estender uma linguagem de programação seqüencial é a forma mais fácil, rápida, ba-
rata e mais popular de programação paralela, porque simplesmente requer que seja desen-
volvida uma biblioteca de subrotinas. A linguagem existente e seu compilador podem ser
usados sem alteração. A facilidade relativa com que as bibliotecas podem ser desenvolvi-
das permite que elas sejam construídas rapidadmente para novos computadores paralelos.
Por exemplo, bibliotecas com o padrão MPI existem para virtualmente todo tipo de computa-
dor paralelo. Além disso, programas escritos com chamadas de funções MPI são altamente
portáveis (podem ser usados em muitos computadores com pequena ou nenhuma modifi-
cação).
Como o compilador não está envolvido na geração do código paralelo, ele não pode
mostrar onde estão os erros. Por isso, é fácil escrever um programa paralelo que seja difícil
de depurar.
Adicionar uma camada de programação paralela: Um programa paralelo pode ser
pensado como tendo duas camadas. A camada mais baixa contém o core da computação,
onde um processo manipula sua porção de dados para produzir sua porção de resultados.
Uma linguagem de programação seqüencial existente poderia ser alterada para expressar
esta porção de atividade. A camada superior controla a criação e sincronização de proces-
sos e o particionamento dos dados entre os processos. Estas ações poderiaam ser pro-
46 DCC/UFLA - Programação Paralela e Distribuída
gramadas usando uma linguagem paralela (talvez uma linguagem de programação visual).
Um compilador deveria ser responsável por traduzir este programa paralelo da segunda
camada em código apropriado para execução em um computador paralelo.
Criar uma linguagem paralela:
A criação de uma linguagem paralela envolve a habilidade de expressar operações pa-
ralelas explicitamente. A linguagem de programação Occam é um exemplo. Com uma sin-
taxe diferente das linguagens imperativas tradicionais, ela suporta tanto execução seqüen-
cial quanto paralela de processos e comunicação automática de processos e sincronização.
Outra forma de suportar paralelismo explícito é adicionar construtores paralelos a uma
linguagem existente. Fortran 90, High Performance Fortran (extensão do Fortran 90) e C*
são exemplos destas estratégias. O High Performance Fortran possui diretivas de compi-
lador que permite ao programador especificar como os dados devem ser mapeados aos
processadores. O C* é uma extensão da linguagem de programação C com a noção de um
shape, que especifica a maneira em que os dados paralelos são organizados. Um shape
é um objeto como um vetor, mas enquanto os elementos do vetor devem ser manipulados
um de cada vez, os elementos de um shape podem ser manipulados simultaneamente.
Adicionar construtores paralelos a uma linguagem de programação existente ou criar
uma nova linguagem de programação paralela requer o desenvolvimento de novos compi-
ladores. Mesmo após uma linguagem padrão ser adotada, ela tipicamente leva anos para
as empresas desenvolverem compiladores de alta qualidade para seus sistemas paralelos.
Algumas linguagens paralelas, como o C*, não foram adotadas como um padrão.
Nesta situação muitas empresas competidoras podem decidir não prover compiladores para
a linguagem em suas máquinas. Quando isto acontece, a portabilidade dos códigos é se-
veramente comprometida.
Outra barreira na adoção de novas linguagens de programação é a resistência do
usuário. Quando novos construtores paralelos são adicionados a uma linguagem de pro-
gramação, os programadores devem aprender como usar estes novos contrutores. Muitos
programadores são relutantes a fazer a transição.
2.5 ESTRUTURA DE CONTROLE DE PLATAFORMAS PARALELAS
Tarefas paralelas podem ser especificadas em vários níveis de granulosidade (ou gra-
nularidade). Em uma visão macro, cada programa em um conjunto de programas pode
ser visto como uma tarefa paralela. Em uma visão mais detalhada, instruções individuais
dentro de um programa podem ser vistas como tarefas paralelas. Entre estes dois extre-
mos existem vários modelos para especificação de estruturas de controle de programas e
o suporte arquitetural correspondente para eles.
Plataformas para Computação Paralela 47
Exemplo 1: Considere o seguinte fragmento de código que adiciona dois vetores, a e
b, e armazena o resultado no vetor c:
1 for (i=0; i<1000; i++)
2 c[i] = a[i] + b[i];
Neste exemplo, várias interações do loop são independentes. Isto significa que
c[0] = a[0]+ b[0]; c[1] = a[1] + b[1], etc., podem ser executadas independen-
temente. Conseqüentemente, se há um mecanismo para executar a mesma instrução
(neste exemplo a instrução add) em todos os processadores com dados apropriados, po-
deríamos executar este loop mais rapidamente.
As unidades de processamento em computadores paralelos podem operar sob o con-
trole centralizado de uma única unidade de controle ou trabalhar independentemente.
2.6 CONTROLE DE ESTRUTURA DE PLATAFORMAS PARALELAS
Há duas formas principais de troca de dados entre tarefas paralelas - acessando um
espaço de dados compartilhado e trocando mensagens.
2.6.1 Plataformas de passagem de mensagens
A visão lógica de uma máquina em uma plataforma de passagem de mensagens con-
siste de p nós de processamento, cada um cada um com seu espaço de endereçamento
exclusivo. Cada um dos nós de processamento podem ser processadores únicos ou um
multiprocessador com espaço de endereçamento compartilhado. Exemplos de tal visão
são os cluster de estações de trabalho (workstations e os multicomputadores de espaço
de endereçamento não-compartilhado. Em tais plataformas, interações entre processos
executando em diferentes nós devem ser realizadas usando mensagens, por isso surgiu o
termo passagem de mensagens. Esta troca de mensagens é usada para transferir dados,
trabalho e para sincronizar ações entre os processos. De uma forma geral, os paradigmas
de passagem de mensagens suportam a execução entre diferentes programas em cada p
nós.
As interações são realizadas enviando e recebendo mensagens. Assim, as operações
básicas neste paradigma de programação são enviar (send) e receber receive (as chama-
das correspondentes podem diferir entre as APIs, mas as semânticas são idênticas). Além
disso, já que nas operações de envio e recebimento os endereços destino devem especifi-
car os endereços destinos, deve haver um mecanismo para atribuir um identificador único
ou ID a cada um dos múltiplos processos que estão executando um programa paralelo. Este
identificador é utilizado em programas paralelos. Para obtê-lo existem funções específicas,
de acordo com a API utilizada. Existe também uma função para descobrir o número de pro-
48 DCC/UFLA - Programação Paralela e Distribuída
cessos que participando da computação. Com estas 4 funções básicas (send, receive, para
obter o identificador de processo e o número de processos) é possível escreverqualquer
programa de passagem de mensagens. Diferentes APIs de passagem de mensagens, tais
como MPI e PVM, suportam estas operações básicas e uma variedade de funcionalidades
de mais alto nível sob diferentes nomes de funções. Cluster de estações de trabalho é um
exemplo de uma plataforma paralela que suporta o paradigma de passagem de mensagens.
É fácil emular um arquitetura de passagem de mensagens contendo p nós em um
computador de espaço de endereçamento compartilhado com um número idêntico de nós.
Assumindo nós uniprocessados isto pode ser feito particionando o espaço de endereça-
mento compartilhado em p partes disjuntas e atribuindo uma partição exclusiva para cada
processador. Um processador pode então "enviar"ou "receber"mensagens escrevendo ou
lendo de outra partição do processador usando primitivas de sincronização apropriadas
para informar seu padrão de comunicação quando ele tiver terminado de ler ou escrever o
dado. Entretanto, emular uma arquitetura de espaço de endereçamento compartilhado em
um computador de passagem de mensagens é custoso, já que acessar memória de outro
nó requer enviar e receber mensagens.
Plataformas para Computação Paralela 49
2.7 MODELOS PARALELOS
A modelagem na computação paralela é desafiadora devido à presença de muitos pro-
cessadores interconectados. Os modelos algorítmicos podem ser usados como frameworks
para descrever e analisar algoritmos paralelos. Porém, nenhum algoritmo paralelo provou
ser aceitável para a maioria dos pesquisadores em processamento paralelo. As seções
seguintes apresentam dois modelos paralelos: DAG e PRAM.
2.7.1 Grafos acíclicos orientados (DAGs)
Muitas computações podem naturalmente ser representadas por grafos acíclicos ori-
entados (directed acyclic graphs - DAGs). Cada entrada é representada por um nó que não
tem arcos incidentes. Cada operação é representada por um nó que tem arcos incidentes
de nós que representam os operandos. O grau máximo devido às arestas incidentes de um
nó interno é no máximo 2. Um nó cujo grau devido às arestas com origem no nó é igual a
zero, representa uma saída. Será assumido o critério de custo unitário, onde cada um dos
nós representa uma operação que gasta uma unidade de tempo.
Um DAG com n nós de entrada representa uma computação que não possui instruções
de desvio e que tem uma entrada de tamanho n. Entretanto, um algoritmo é representado
por uma família de DAGs {Gn}, onde Gn corresponde ao algoritmo com entrada de tamanho
n.
Este modelo é razoável para analisar computações numéricas, já que instruções de
desvio são tipicamente usadas para executar uma seqüência de operações em um certo
número de vezes, dependente do tamanho da entrada n. Neste caso, pode-se desenrolar
uma instrução de desvio duplicando a seqüência de operações a ser repetida o número
apropriado de vezes.
Um DAG especifica as operações realizadas pelo algoritmo e implica em restrições de
precedência na ordem em que estas operações devem ser realizadas. É completamente
independente da arquitetura.
Exemplo 2: Considere o problema de computar a soma S de n = 2k elementos de um
vetor A. Dois algoritmos possíveis são representados por seus DAGs na Figura 2.18 para
n = 8. O algoritmo seqüencial computa a soma parcial consecutivamente, começando com
A(1)+A(2), seguido por (A(1)+A(2))+A(3) e assim por diante. O outro algoritmo funciona
em uma árvore binária completa que começa pela computação da soma A(1)+A(2),A(3)+
A(4), ...,A(n− 1)+A(n) no nível mais baixo e repete o processo no próximo nível com n/2
elementos e assim por diante até a soma ser computada na raiz.
No Exemplo 2, para um número n de elementos, o melhor escalonamento para o
algoritmo representado na Figura 2.18 (a) gasta tempo O(n), considerando que há proces-
50 DCC/UFLA - Programação Paralela e Distribuída
sadores disponíveis. O tempo gasto na Figura 2.18 (b) é O(logn) com n/2 processadores.
Todos os nós no mesmo nível têm o mesmo tempo de execução.
Figura 2.18: Os DAGs de 2 possíveis algoritmos para o Exemplo 2. (a) Um DAG para computação
da soma de 8 elementos. (b) Um DAG alternativo para computação da soma de 8 elementos de um
esquema de árvore binária balanceada.
Exemplo 3: (Multiplicação de matrizes)
Sejam A e B duas matrizes nxn. Considere o algoritmo padrão para computar o produto
C = AB. Cada C(i, j) é computado usando a expressão C(i, j) = ∑nl=1A(i, l)B(l, j). Um DAG
para computar C(i, j) para n = 4 é mostrado na Figura 2.19. Dados n3 processadores, as
operações podem ser escalonadas nível por nível, usando n processadores para computar
cada entrada deC; então, o DAG pode ser escalonando para computarC em tempo O(logn).
2.7.2 O modelo PRAM
O tempo de execução de um algoritmo seqüencial é estimado pelo número de opera-
ções básicas requeridas pelo algoritmo em função do tamanho da entrada. A definição de
operação básica depende do problema tratado e o modelo de computação utilizado. Con-
siderando uma unidade de tempo para as operações de leitura e escrita em memória e as
operações aritméticas e lógicas (tais como adição, subtração, comparação ou multiplicação
de números e a computação de OR ou AND lógico bit-a-bit de duas palavras). Quando o
custo de uma operação não depende do tamanho da palavra é porque está sendo usado o
critério de custo uniforme. Um modelo computacional para este propósito é o Random Ac-
cess Machine (RAM), que assume a presença de uma unidade de processamento central
Plataformas para Computação Paralela 51
Figura 2.19: DAG para computação de uma entrada C(i, j) do produto de matrizes C = AB para o
caso de matrizes 4x4
com uma memória de acesso aleatório junto dela e alguma forma de tratar a entrada e a
saída das operações.
O modelo RAM tem sido usado para prever o desempenho de algoritmos seqüenciais.
A modelagem da computação paralela é consideravelmente mais desafiante dada a nova
dimensão introduzida pela presença de muitos processadores interconectados.
Um modelo algorítmico para descrever e analisar algoritmos paralelos deve ser sim-
ples o suficiente para permitir a descrição dos algoritmos paralelos facilmente e para anali-
sar matematicamente medidas de desempenho tais como speedup, comunicação e utiliza-
ção de memória. Além disso, o modelo deve ser independente de hardware e os algoritmos
paralelos desenvolvidos para o modelo devem ser facilmente implementáveis em compu-
tadores paralelos. A análise realizada deve capturar de forma significativa o desempenho
real destes algoritmos em computadores paralelos.
O modelo PRAM é uma versão síncrona do modelo de memória compartilhada. No
modelo síncrono, todos os processadores operam sincronamente sob o controle de um re-
lógio comum (relógio global). No modelo assíncrono, cada processador opera sob relógios
diferentes. E neste caso, é responsabilidade do programador estabelecer os pontos de sin-
cronização apropriados. Se o processador necessitar acessar dados é responsabilidade
do programador garantir que os valores corretos sejam obtidos, já que o valor de uma va-
riável compartilha é determinado dinamicamente durante a execução dos programas em
diferentes processadores.
No modelo PRAM, os algoritmos desenvolvidos são do tipo SIMD. Isto é, todos os
processadores executam o mesmo programa tal que durante cada unidade de tempo todos
os processadores ativos estão executando a mesma instrução, mas com diferentes dados.
Entretanto, pode-se carregar diferentes programas em memórias locais dos processadores,
52 DCC/UFLA - Programação Paralela e Distribuída
de forma que os processadores possam operar sincronamente; então, diferentes tipos de
instruções podem ser executadas durante a unidade de tempo alocada para um passo.
No modelo PRAM, os seguintes pontos devem serdestacados:
• Há técnicas bem definidas e métodos para tratar muitas classes diferentes de pro-
blemas computacionais no modelo PRAM.
• O modelo PRAM remove detalhes algorítmicos de sincronização e comunicação, e
permite ao projetista do algoritmo focar em propriedades estruturais do problema.
• O modelo PRAM captura diversos parâmetros importantes de computações parale-
las. Um algoritmo PRAM inclui um entendimento explícito de operações para serem
realizadas em cada unidade de tempo e explícita alocação de processadores para
jobs em cada unidade de tempo.
• O paradigma de projeto PRAM tem se tornado robusto. Muitos dos algoritmos da
área de redes podem ser diretamente derivados de algoritmos PRAM.
• É possível incorporar características tais como sincronização e comunicação no
modelo de memória compartilhada. Então os algoritmos PRAM podem ser analisa-
dos dentro deste framework mais geral.
Desde ponto em diante, considere o modelo PRAM como sendo um modelo de memó-
ria compartilhada síncrono (arquitetura como vista na Figura 2.6(a)). Assim, ele consiste de
p processadores e uma memória global de tamanho ilimitado que é uniformemente aces-
sível a todos os processadores. Todos os processadores acessam o mesmo espaço de
endereçamento global, mas cada processador tem sua memória local. Os processadores
compartilham um relógio comum mas podem executar diferentes instruções em cada ci-
clo. Como o modelo PRAM permite o acesso concorrente a várias locações de memória,
dependendo de como os acessos simultâneos à memória são tratados, PRAM pode ser
dividido em 4 subclasses.
1. EREW (Exclusive-Read, Exclusive-Write): nesta classe, o acesso à locação de me-
mória é exclusivo. Conflitos de leitura ou escrita não são permitidos. Este modelo
apresenta concorrência mínima em acesso à memória.
2. CREW (Concurrent-Read Exclusive-Write): nesta classe, acessos de leituras múlti-
plas à locação de memória são permitidos. Entretanto, múltiplos acessos de escrita
na mesma locação de memória são seqüencializados (conflitos de escrita não são
permitidos).
3. ERCW (Exclusive-Read Concurrent-Write): múltiplos acessos de escrita são per-
mitidos à locação de memória, mas múltiplos acessos de leitura são sequenciali-
zados.
4. CRCW (Concurrent-Read Concurrent-Write): permite múltiplos acessos de leitura
e escrita à locação de memória comum.
Plataformas para Computação Paralela 53
Permitir acesso de leitura concorrente não cria qualquer discrepância de semântica
no programa. Entretanto, acesso de escrita concorrente a uma locação de memória re-
quer cuidado. Existem diferentes protocolos para resolver escritas concorrentes no mesmo
endereço global. Os protocolos mais freqüentemente usados são:
• Comum: processadores escrevendo concorrentemente no mesmo endereço de
memória global devem escrever o mesmo valor.
• Arbitrária: se múltiplos processadores concorrentemente escrevem no mesmo en-
dereço global, somente um dos processadores que está competindo é arbitraria-
mente escolhido como vencedor e seu valor é escrito.
• Prioritária: todos os processadores são organizados em uma lista de prioridade
pré-definida e o processador com a maior prioridade realiza a escrita e os demais
falham.
• Sum: a soma de todas as quantidades é escrita (o modelo de resolução de conflito
de escrita baseado em soma pode ser estendido a qualquer operador associativo
definido sobre as quantidades sendo escritas).
Antes de apresentar os algoritmos, considere a linguagem algoritmica com os cons-
trutores: leitura_global(X,Y) e escrita_global(U,V).
O efeito da instrução leitura_global(X,Y) é mover o bloco de dados X armazenado na
memória compartilhada para a variável localY . Similarmente, o efeito de escrita_global(U,V)
é escrever o dado local U na variável compartilhada V .
Exemplo 4: Dado um vetor A composto por n = 2k números e um PRAM com n pro-
cessadores {P1,P2, ...,Pn}, deseja-se computar a soma S = A(1) + A(2) + ...+ A(n). Cada
processador executa o mesmo algoritmo, dado aqui para o processador Pi.
A Figura 2.20 ilustra o algoritmo para o caso quando n = 8. Durante os passos 1 e 2,
uma cópia B de A é criada e armazenada na memória compartilhada. O esquema de com-
putação (passo 3) é baseado na árvore balanceada binária cujas folhas correspondem aos
elementos de A. O processador responsável por desempenhar uma operação é indicado
abaixo no nó representante da operação. Note que P1, que é responsável por atualizar o va-
lor de B(1) e por escrever a soma em S, está sempre ativo durante a execução do algoritmo,
e P5, P6, P7 e P8 estão ativos somente durante os passos 1 e 2.
Este exemplo considera que os vetores A e B são armazenados em memória global e
podem ser acessados por qualquer processador. Além disso, em cada unidade de tempo, a
cada processador é permitido executar uma instrução ou ficar ocioso. Note que a condição
do comando i f no loop definido no passo 3 é satisfeito por somente alguns processadores.
Os demais processadores ficam ociosos durante aquele tempo.
54 DCC/UFLA - Programação Paralela e Distribuída
Algoritmo 1: (Soma no Modelo PRAM)
Entrada: Um vetor A de ordem n = 2k armazenado na memória compartilhada
de um PRAM com n processadores. As variáveis locais inicializadas são n e o
processador número i.
Saída: A soma das entradas de A armazenadas na locação compartilhada S.
O vetor A possui seu valor inicial.
begin
1. leitura_global(A(i),a)
2. escrita_global(a,B(i))
3. for h = 1 to log n do
if( i ≤ n/2h) then
begin
leitura_global(B(2i - 1),x)
leitura_global(B(2i),y)
Set z := x+y
escrita_global(z,B(i))
end
4. if i = 1 then escrita_global(z,S)
end
2.8 EXERCÍCIOS
1. Por que o número de processadores em um multiprocessador centralizado é limi-
tado a poucas dezenas?
2. Quais são as principais diferenças entre computadores de passagem de mensa-
gens e espaço de endereçamento compartilhado? Descreva as vantagens e des-
vantagens de cada um deles.
3. Por que é difícil construir um computador de memória compartilhada verdadeira?
Qual é o número mínimo de switches para conectar p processadores a uma me-
mória compartilhada com b palavras ( onde cada palavra pode ser acessada inde-
pendentemente)?
Plataformas para Computação Paralela 55
Figura 2.20: Soma no modelo PRAM com 8 elementos

Continue navegando