Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas

Implementações Comparadas de Sistemas de Sincronização: Modelo Base vs Modelo CSP, Notas de aula de Comunicação

A comparação de implementações de sistemas de sincronização entre o modelo base e o modelo communicating sequential processes (csp). O texto inclui programas para o banquete dos filósofos, produtor-consumidor e semáforos, com análises de desempenho e discussão sobre as vantagas e desvantagas de cada modelo. O documento também menciona possíveis trabalhos futuros.

O que você vai aprender

  • Como é implementado o produtor-consumidor no modelo CSP?
  • Qual é a diferença de implementação entre o modelo base e o modelo CSP para o banquete dos filósofos?
  • Quais são as possíveis melhorias para a implementação do modelo CSP?
  • Quais são as vantagas e desvantagas de usar o modelo base em comparação ao modelo CSP?
  • Quais são as operações de comunicação no modelo CSP e como elas afetam o desempenho?

Tipologia: Notas de aula

2022

Compartilhado em 07/11/2022

A_Santos
A_Santos 🇧🇷

4.4

(111)

214 documentos

1 / 62

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
Uma comparação entre sistemas multiprocessados de memória
compartilhada e de troca de mensagens baseada em CSP
Dalmon Ian Martins de Oliveira
Bacharelado em Ciência da Computação
Departamento de Informática UFPR
12 de agosto de 2021
Resumo
Sistemas multiprocessados têm como objetivo melhorar a performance de programas uti-
lizando paralelismo. Para o funcionamento correto do sistema, é necessário que os proces-
sadores se comuniquem e se coordenem. Este trabalho compara dois modelos de multipro-
cessamento: memória compartilhada e sincronização com semáforos; e rede de processadores
com memória distribuída e sincronização por rendezvous. Foi implementado um simulador
da arquitetura MIPS que permite extrair informações dos programas nele executados para
a análise e comparação da performance dos modelos. O trabalho conclui comentando os
resultados dos experimentos.
1 Introdução
Sistemas multiprocessados têm como objetivo melhorar a performance de programas utilizando
paralelismo. A possibilidade do paralelismo advém da natureza concorrente dos programas exe-
cutados nesse tipo de sistema. Devido à concorrência, é necessário que os processadores se
comuniquem e se coordenem para o funcionamento correto do sistema.
São comparados dois modelos de multiprocessamento: (i) memória compartilhada, interco-
nexão entre os elementos por barramento, sincronização com semáforos (modelo base); e (ii) rede
de processadores com memória distribuída e sincronização por rendezvous (modelo CSP). A com-
paração parte da execução de dois problemas clássicos de sincronização (produtor-consumidor
e banquete dos filósofos), e da análise dos contadores de performance para cada um dos mode-
los. A principal motivação do trabalho é investigar a utilização de um modelo de programação
baseado em troca de mensagens com a comunicação explícita.
No modelo base, o barramento utilizado é implementado com uma estrutura de dados que
garante que todos os processadores que tentem acessá-lo eventualmente consigam realizar esse
acesso. Os semáforos utilizados são semáforos contadores, implementados utilizando instruções
padrão do conjunto MIPS.
No modelo CSP, a rede consiste em nós de processamento dispersos em uma malha retangu-
lar. A troca de mensagens e sincronização entre os processadores foi implementada como uma
extensão do conjunto de instruções do MIPS. A transmissão de mensagens é realizada por todos
os nós de processamento.
Na comparação entre os modelos, para o problema produtor-consumidor foi observado que
o modelo CSP apresenta vantagens sobre o modelo base em relação a tamanho do código da
solução e performance. Essa situação se inverte para o problema do banquete dos filósofos. Um
equívoco foi cometido na implementação dos problemas no modelo CSP, imitar o modelo base
com a sincronização por semáforos. Além de resultados contraintuitivos, até mesmo os resultados
positivos são afetados pelo equívoco.
Este trabalho é similar a [5], embora tenha sido desenvolvido de forma independente.
1
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36
pf37
pf38
pf39
pf3a
pf3b
pf3c
pf3d
pf3e

Pré-visualização parcial do texto

Baixe Implementações Comparadas de Sistemas de Sincronização: Modelo Base vs Modelo CSP e outras Notas de aula em PDF para Comunicação, somente na Docsity!

Uma comparação entre sistemas multiprocessados de memória

compartilhada e de troca de mensagens baseada em CSP

Dalmon Ian Martins de Oliveira

Bacharelado em Ciência da Computação

Departamento de Informática – UFPR

12 de agosto de 2021

Resumo Sistemas multiprocessados têm como objetivo melhorar a performance de programas uti- lizando paralelismo. Para o funcionamento correto do sistema, é necessário que os proces- sadores se comuniquem e se coordenem. Este trabalho compara dois modelos de multipro- cessamento: memória compartilhada e sincronização com semáforos; e rede de processadores com memória distribuída e sincronização por rendezvous. Foi implementado um simulador da arquitetura MIPS que permite extrair informações dos programas nele executados para a análise e comparação da performance dos modelos. O trabalho conclui comentando os resultados dos experimentos.

1 Introdução

Sistemas multiprocessados têm como objetivo melhorar a performance de programas utilizando paralelismo. A possibilidade do paralelismo advém da natureza concorrente dos programas exe- cutados nesse tipo de sistema. Devido à concorrência, é necessário que os processadores se comuniquem e se coordenem para o funcionamento correto do sistema. São comparados dois modelos de multiprocessamento: (i) memória compartilhada, interco- nexão entre os elementos por barramento, sincronização com semáforos (modelo base); e (ii) rede de processadores com memória distribuída e sincronização por rendezvous (modelo CSP). A com- paração parte da execução de dois problemas clássicos de sincronização (produtor-consumidor e banquete dos filósofos), e da análise dos contadores de performance para cada um dos mode- los. A principal motivação do trabalho é investigar a utilização de um modelo de programação baseado em troca de mensagens com a comunicação explícita. No modelo base, o barramento utilizado é implementado com uma estrutura de dados que garante que todos os processadores que tentem acessá-lo eventualmente consigam realizar esse acesso. Os semáforos utilizados são semáforos contadores, implementados utilizando instruções padrão do conjunto MIPS. No modelo CSP, a rede consiste em nós de processamento dispersos em uma malha retangu- lar. A troca de mensagens e sincronização entre os processadores foi implementada como uma extensão do conjunto de instruções do MIPS. A transmissão de mensagens é realizada por todos os nós de processamento. Na comparação entre os modelos, para o problema produtor-consumidor foi observado que o modelo CSP apresenta vantagens sobre o modelo base em relação a tamanho do código da solução e performance. Essa situação se inverte para o problema do banquete dos filósofos. Um equívoco foi cometido na implementação dos problemas no modelo CSP, imitar o modelo base com a sincronização por semáforos. Além de resultados contraintuitivos, até mesmo os resultados positivos são afetados pelo equívoco. Este trabalho é similar a [5], embora tenha sido desenvolvido de forma independente.

Este trabalho é organizado da seguinte forma: a Seção 2 introduz e define os conceitos utili- zados para a compreensão das descrições do simulador e dos experimentos; a Seção 3 escreve o simulador e os experimentos; a Seção 4 contém uma argumentação informal sobre a equivalência dos mecanismos de sincronização; a Seção 5 mostra os códigos utilizados no experimento; a Se- ção 6 apresenta, analisa e comenta o resultado dos experimentos; a Seção 7 faz uma comparação geral entre os modelos considerando os resultados das simulações; e a Seção 8 conclui o trabalho.

2 Definições

Esta seção define e descreve conceitos utilizados no trabalho. A seção inicia apresentando concei- tos essenciais para o entendimento da concorrência e os mecanismos necessários para a solução de problemas do tipo. Em seguida são apresentados a noção de execução paralela, o tipo de sistema computacional que utiliza dos conceitos anteriores para obter melhor performance e como a memória desses sistemas podem ser implementadas. Logo após, são introduzidas lingua- gens para a descrição e programação de problemas de concorrência. A seção finaliza com breves descrições dos processadores que inspiraram e são utilizados no trabalho.

2.1 Eventos e processos

Considere o seguinte trecho de código:

Programa 1: Implementação da Formula de Bhaskara

Bhaskara () { a = 1; /* E0 */ b = 2; /* E1 */ c = 3; /* E2 */

bb = b * b ; /* E3 */ aux = 4 * a * c ; /* E4 */

delta = bb - aux ; /* E5 */

sqrt_delta = sqrt ( delta ); /* E6 */

x0 = b + sqrt_delta ; /* E7 */ x1 = b - sqrt_delta ; /* E8 */

print ( x0 , x1 ); /* E9 */ }

Eventos são um conjunto de instruções de máquina que executam alguma computação, no caso do exemplo, os eventos (denotados En ) realizam atribuições, operações aritméticas e im- primem valores de variáveis. Tem-se então que o Programa 1 pode ser descrito como o conjunto de eventos:

Bhaskara = { E 0 , E 1 , E 2 , E 4 , E 5 , E 6 , E 7 , E 8 , E 9 }. (1) Processos são uma sequência de eventos [8]. Seja o operador “;” indicador da execução sequencial dos eventos, o operador “. ” indicador o término da execução. Executando os eventos do exemplo em ordem numérica, tem-se então o seguinte processo:

E 0 ; E 1 ; E 2 ; E 3 ; E 4 ; E 5 ; E 6 ; E 7 ; E 8 ; E 9 ;. (2) A ordem de execução dos eventos no processo não é totalmente arbitrária. Dada a definição da relação acontece antes em [8], denotada como “→”, os eventos do subconjunto { E 0 , E 1 , E 2 }

2.3.2 Região crítica

Dado um sistema computacional executando múltiplos processos, e os processos compartilham de um recurso em comum, a região crítica é o conjunto de eventos que manipulam o recurso compartilhado. Para evitar condições de corrida, as regiões críticas dos processos envolvidos devem satisfazer as seguintes condições: (i) apenas um processo executa sua região crítica ( exclusão mútua ); (ii) nenhum processo espera indefinidamente para entrar na região crítica; e (iii) nenhum processo fora da região crítica pode bloquear os demais processos.

2.3.3 Operações atômicas

Operações atômicas ou operações indivisíveis são operações que não interrompem ou interferem umas com as outras. Para que uma operação seja atômica, dois eventos a e b envolvidos na operação, devem satisfazer a seguinte condição:

abba. (8) O operador “⊕” denota a operação ou exclusivo.

2.3.4 Semáforos

Os semáforos foram propostos por Dijkstra [4], e utilizam o princípio da exclusão mútua para sincronizar os processos concorrentes. Sejam “sem” uma variável de tipo inteiro inicializada com um valor maior ou igual a zero, e P(), V() duas funções. P(sem) decrementa o valor do semáforo em um. Se o valor resultante for maior que zero, então a execução prossegue normalmente. Caso contrário, o processo espera até que recurso seja liberado, i.e , até que o semáforo seja incrementado para um valor maior que zero. V(sem) incrementa o valor do semáforo em um. P(), V() são operações atômicas.

2.3.5 Rendezvous

Rendezvous é um mecanismo de sincronização no qual os processos envolvidos aguardam uns aos outros em um determinado ponto de suas execuções até que todos os demais processos também atinjam esse ponto. Dados dois processos A, B , dois eventos ax, arA , dois eventos by, brB , ar, br são os eventos de sincronização, ax, by são eventos quaisquer de seus respectivos processos que executam antes de ar, br. O processo A faz rendezvous com o processo B quando:

arbraxarbybr. (9)

2.3.6 Inanição e impasse

Starvation ou inanição é a condição de um processo que necessita de um recurso para continuar sua execução, mas, o processo nunca consegue acessar o recurso. Deadlock ou impasse é um caso específico da inanição: um processo está em espera para acessar a região critica e depende de algum outro processo para liberar o acesso, mas o segundo processo nunca o libera.

2.4 Execução paralela

Execução paralela é a execução simultânea dos eventos de um processo, ou de diversos processos. Note que concorrência e paralelismo não são a mesma coisa. Concorrência diz respeito à compo- sição dos eventos dos processos ao passo que paralelismo é a execução simultânea desses eventos. A partir desse ponto, é considerado que “execução paralela” e paralelismo são sinônimos.

Paralelismo em dados consiste em explorar as características dos dados processados de forma a permitir que os processadores operem sobre blocos de dados simultaneamente. Por exemplo, o cálculo dos elementos do vetor b , obtido pela multiplicação dos elementos do vetor a por uma constante K , pode ser definido por:

bi =

∑^ n

i =

aiK. (10)

Nota-se que não há dependências entre o cálculo dos elementos individuais de b , o que permite que múltiplos processadores calculem elementos do vetor resultado sem interferir com a computação efetuada pelos outros processadores. Paralelismo no nível de tarefa consiste em quebrar o problema computacional em diferentes processos e atribuir esses processos a diferentes processadores. Com base no exemplo da Equa- ção 10, dado um sistema com p processadores, sejam^1 n = | a |; p < n e p múltiplo de n ; i ∈ [0 ..n ); e j ∈ [0 ..p ), pode-se dividir a tarefa de multiplicação em p processos:

procj : bi =

( j +1)( ∑ n/p )

i = j ( n/p )

aiK (11)

Esse é o tipo de paralelismo normalmente explorado por sistemas multiprocessados, e é o foco deste trabalho.

2.5 Multiprocessamento

Um sistema multiprocessado é definido neste trabalho como um sistema computacional que possui mais de um processador. O objetivo de um sistema multiprocessado é melhorar algum parâmetro relacionado à per- formance (tempo de execução, consumo de energia, vazão dos dados, ou alguma combinação destes) dos processos executados. Em um sistema implementado adequadamente, esse objetivo é cumprido dada a habilidade inata de paralelismo do sistema computacional e da independência dos eventos concorrentes nele executados. Sistemas multiprocessados se apresentam em várias formas e tamanhos: Desde meados da primeira década do século XXI, o público geral tem acesso a circuitos integrados com mais de um processador, e com os avanços na tecnologia de fabricação cada vez mais processadores podem ser incluídos num circuito integrado.

2.6 Organização de memória

Este trabalho define como organização de memória o conceito de como a memória é visualizada do ponto de vista lógico pelos programadores do sistema.

2.6.1 Memória compartilhada

O modelo de organização de memória compartilhada consiste nos processadores acessando uma memória unificada do ponto de vista do programador. A comunicação entre os processadores acontece na memória por meio de operações de load e store : a operação load lê um valor da memória e a operação store armazena um valor na memória. Essas operações são o equivalente lógico das instruções load e store.

(^1) Notação: um intervalo em Z é representado por [ a..b ] se a e b pertencem ao intervalo (fechado-fechado), ou ( a..b ) se a e b não pertencem ao intervalo (aberto-aberto). Um intervalo pode ser fechado-aberto ([ a..b )) ou aberto-fechado (( a..b ]).

2.7.3 Comando de seleção de alternativas O comando de seleção de alternativas é uma estrutura de execução condicional na qual uma determinada lista de comandos só é executada quando a respectiva condição ( guarda ) é satisfeita.

Programa 4: Definição do comando de seleção de alternativas

< sele ç ão > ::= [ < comando de guarda >{# < comando de guarda >}] < comando de guarda > ::= < guarda > - > < lista de comandos > < guarda > :: = < lista de guardas >| < lista de guardas >; < comando de entrada > < lista de guardas > :: = < elemento da guarda >{; < elemento da guarda >} < elemento da guarda > ::= < express ã o booliana >| < declara ç ão >

A diferença deste comando com relação a outras estruturas de mesmo tipo como if...else ou switch é que a ordem de execução das guardas é arbitrária. Se a guarda escolhida falhar, outra é escolhida até que se esgotem as alternativas. Caso todas as guardas falhem o comando de seleção de alternativas falha. Por exemplo, para selecionar entre três alternativas:

[x >y - > PROCESSA_XY # P ?a - > PROCESSA_A # Q ?b - > PROCESSA_B ]

Os comandos de guarda são:

x > y -> PROCESSA_XY /* CG0 / P? a -> PROCESSA_A / CG1 / Q? b -> PROCESSA_B / CG2 */

O comando de seleção de alternativas arbitrariamente seleciona um comando de guarda. Por exemplo, seja CG 0 o primeiro comando a ser executado e a guarda x > y é avaliada. Supondo que falhe, o comando de seleção de alternativas escolhe CG 2 , mas o comando de entrada da guarda Q? b não está pronto. Por fim, o comando de seleção de alternativas seleciona CG 1 , cuja guarda P? a está pronta, então a lista de comandos indicada por PROCESSA_A é executada.

2.8 occam

Por ser uma linguagem formal para a descrição de sistemas, CSP não se adéqua enquanto linguagem de programação. Em 1982 foi introduzida a linguagem de programação occam, que implementa os conceitos definidos em CSP [2]. A sintaxe da linguagem é diferente da que é usada em CSP, mas como implementa as mesmas estruturas já vistas anteriormente, essa não é discutida neste trabalho.

2.9 Transputer

O Transputer é uma implementação em silício do modelo de computação da linguagem occam. O objetivo dos projetistas era explorar o decrescente custo de fabricação de circuitos integrados para implementar sistemas multiprocessados. Este trabalho faz uso da arquitetura do transputer descrita em [9]. O transputer diferencia-se das demais arquiteturas da época devido ao seu conjunto mí- nimo de instruções (16 instruções, com um número maior em versões mais recentes), memória integrada e facilidade de conectar-se a outros transputers. Os transputers se conectam por par de links, um par para cada direção da comunicação (Norte, Sul, Leste, Oeste). A comunicação ponto-a-ponto emprega um protocolo serial. Por ser uma comunicação ponto-a-ponto, múltiplos transputers podem comunicar-se concorrentemente, aumentando a quantidade de dados que o sistema pode processar em paralelo.

2.10 MIPS

MIPS é uma arquitetura de processadores do tipo RISC, desenvolvido no início da década de 1980 [7]. Suas características principais são: (i) os dados são operados dentro dos registradores;

(ii) os únicos tipos de instrução que alteram a memória são as instruções load e store; e (iii) as instruções têm tamanho fixo de 32 bits.

3 Experimentos

Para efetuar esse trabalho foi implementado um simulador de sistemas multiprocessados. Dois modelos de sistemas multiprocessados são simulados, um com memória compartilhada, interco- nexão dos processadores por barramento e sincronização por semáforos, e outro com memória distribuída, interconexão dos processadores ponto-a-ponto (formando uma malha retangular) e sincronização por rendezvous. Os experimentos efetuados nesse trabalho consistem na execução de dois problemas clássicos de sincronização.

3.1 Problemas clássicos de sincronização

Os problemas escolhidos para a comparação de desempenho dos dois modelos são pares produtor- consumidor e o banquete dos filósofos. Esses problemas clássicos são normalmente utilizados para testar os mecanismos de sincronização [1].

3.1.1 Produtor-consumidor

O processo produtor produz algum dado que é consumido pelo processo consumidor. Após o processo produtor produzir o dado, este dado é inserido em uma área de transferência ( buffer ) na qual o processo consumidor o remove. O buffer é implementado como uma fila circular e pode armazenar até MAXELEM elementos. A inserção dos elementos do buffer ocorre no fim na fila. A remoção dos elementos dá-se no início da fila. Para garantir que os apontadores do início e fim da fila sejam consistentes, os processos não devem acessar o buffer concorrentemente. Caso o buffer esteja cheio, o processo produtor aguarda até que seja liberada uma posição para a inserção de um novo elemento. Caso o buffer esteja vazio, o processo consumidor aguarda até que algum elemento seja inserido. Este trabalho explora duas variações do problema. Na primeira há um processo produtor e C consumidores, com C ≥ 1. Na segunda versão são dois produtores e C consumidores, C ≥ 1.

3.1.2 Banquete dos filósofos

No problema do banquete dos filósofos , têm-se 5 instâncias de um processo de tipo filósofo que realizam duas ações, comer e pensar. Para comer, os filósofos sentam-se em uma mesa circular com 5 pratos, 5 garfos (cada garfo posicionado entre dois pratos) e no meio da mesa há um prato com macarrão que é (magicamente) reposto a todo momento. Para retirar e comer sua porção, o filósofo utiliza o garfo que está a sua esquerda mais o garfo que está a sua direita. Após comer (e somente após comer), o filósofo devolve os dois garfos à mesa, levanta-se, e vai realizar a ação de pensar até que sinta fome novamente e repita o processo. O problema consiste em como os filósofos vão organizar-se à mesa de forma a que todos tenham acesso aos dois garfos e que nenhum filósofo morra de fome.

3.2 Modelo base

O modelo base consiste em um sistema com p > 1 processadores, uma única memória compar- tilhada uniformemente entre os processadores por meio de um barramento. Apenas um proces- sador obtém acesso ao barramento por ciclo da simulação. A sincronização entre os processos utiliza semáforos.

arbitragem pelo acesso ao barramento é implementado com uma estrutura de dados FIFO ( first- in-first-out ). A implementação dessa FIFO garante que nenhum processador fique a esperar eternamente pelo acesso ao barramento. Um processador ganha acesso ao barramento caso todas requisições anteriores já tenham sido atendidas. Dado um sistema com p processadores, o processador que deseja utilizar a memória para a operação load ou store primeiro faz uma requisição ao barramento, que consiste em enfileirar o ID do processador em uma fila circular de tamanho p , caso o ID já se encontre na fila, este não é enfileirado novamente. O barramento possui um variável booliana de controle (used) que indica se o barramento foi utilizado no ciclo atual. Se used tiver valor 0, o controlador do barramento então verifica o elemento inicial da fila, caso o valor seja o mesmo valor do ID do processador requerente, este é desenfileirado e processador ganha o acesso ao barramento e executa a operação de acesso à memória. Caso used tenha valor 1, a fila está cheia ou o elemento inicial da fila tem valor diferente do ID do processador requerente, o processador não ganha acesso ao barramento e não executa a operação de acesso à memória, devendo tentar novamente no próximo ciclo (seu PC não é incrementado). A função de requisição de acesso ao barramento permite a execução de apenas um processador por vez, e o pseudocódigo do Programa 5 indica a implementação. A variável global bus_queue é a fila FIFO do barramento.

Programa 5: Implementação da requisição de acesso ao barramento

busacc ( ID ) { enqueue ( bus_queue , ID ); /* enfileira ID */

if ( used ) { /* barramento utilizado? */ return FAILURE ; /* repete op. no pr ó x. ciclo */ }

if ( head ( bus_queue ) != ID ) { /* ID é o elem. inicial da fila? */ return FAILURE ; /* caso n ão , repete opera ç ã o */ }

dequeue ( bus_queue ); /* desenfileira ID */

used = 1; /* utiliza barramento */

return SUCCESS ; /* ID ganha acesso */ }

3.4.4 Comunicação ponto-a-ponto Para a comunicação ponto-a-ponto, cada processador do sistema simulado possui quatro pares de links (Norte, Sul, Leste, Oeste) que encaminham as mensagens na rede. Os links são modelados por uma fila com as mensagens e representam a comunicação em apenas um sentido. Outra estrutura da comunicação é a caixa de mensagens , na qual são depositadas as men- sagens que o processador recebe. A caixa de mensagens contém um vetor das mensagens, cujos índices representam o remetente. O conjunto formado pelos links, processador e caixa de mensagens recebe o nome de nó da rede , cuja ilustração é a Figura 1. A cada ciclo do simulador, as mensagens saltam do nó atual para algum dos nós vizinhos. Do ponto de vista do processador, o envio ou recebimento das mensagens é realizado apenas pelos comandos CSP. O simulador implementa, para cada nó, as máquinas de estado que controlam o tráfego de mensagens pela rede.

CPU

Memória

COP 2

MBOX oeste

sul

leste

norte

Figura 1: Diagrama de blocos do nó de processamento

As mensagens não são encaminhadas para o nó vizinho caso o link de destino da mensagem não tenha espaço para acomodar a mensagem. Quando isso acontece, é dito que a mensagem foi postergada. Para transmitir a mensagem, compara-se o ID do nó atual (cid) com o nó destino (dest). Caso cid < dest e os nós pertençam a linhas diferentes, a mensagem é enviada ao nó do norte; se os nós pertencem a mesma linha, a mensagem é enviada para o nó do leste. O caso cid > dest é análogo ao caso anterior, porém enviando as mensagens para os nós do sul e oeste, respectivamente. Caso cid = dest, a mensagem é enviada à caixa de mensagens do nó. Esse algoritmo de roteamento evita a congestão da rede e o pseudocódigo que o implementa é indicado no Programa 6.

Programa 6: Algoritmo de roteamento das mensagens na malha 2D

guidance ( cid , dest ) { if ( cid < dest ) { if ( sameline ( cid , dest )) { /* ID atual é oeste do destino? */ return EAST ; } return NORTH ; /* ID atual é sul do destino? */ }

if ( cid > dest ) { if ( sameline ( cid , dest )) { /* ID atual é leste do destino? */ return WEST ; } return SOUTH ; /* ID atual é norte do destino? */ }

return MBOX ; /* mensagem alcan ç ou o destino */ }

3.4.5 Implementação dos semáforos A arquitetura MIPS possui duas instruções para a implementação de operações atômicas: ll ( load-linked ) e sc ( store-conditional ). O par de instruções implementa a operação read-modify- write , que consiste nos processadores de um sistema multiprocessado lerem um dado, modificá-lo e escrevê-lo de volta na memória, sem que o dado seja acessado por outro(s) processador(es). O processador que executa a instrução ll reserva um endereço, indicando o início da operação

Programa 10: Implementação da operação V()

Sem_V: ll $t0 , 0( $a0 ) # tenta acesso ao sem á foro addiu $t0 , $t0 , 1 sc $t0 , 0( $a0 ) # tenta atualizar sem á foro beqz $t0 , Sem_V # sc falhou? Repete nop jr $ra nop

3.4.6 Implementação dos comandos CSP Os comandos CSP que o simulador implementa são os de entrada, saída e seleção de alternativas. Os comandos são implementados utilizando o coprocessador 2 (CP2). A interface consiste em 4 registradores, indicados na Tabela 1, juntamente com 3 operações do CP2, indicados na Tabela 2.

Tabela 1: Registradores do CP

Nº do registrador Nº de seleção Função 0 0 processador correspondente 0 1 dado enviado/recebido 0 2 status 0 5 número de alternativas

Tabela 2: Operações do CP

Nº da operação Função Apelido 2 Comando de entrada INPUT 3 Comando de saída OUTPUT 4 Comando de seleção de alternativas ALT

O texto utiliza a notação “CP2[reg][sel]” para se referir ao registrador do CP2 com o número de registrador “reg”, e número de seleção “sel”. As operações INPUT, OUTPUT e ALT modificam CP2[0][2] ( status ) para indicar a operação atual. Enquanto o registrador status for diferente de zero, o processador é bloqueado pelo con- trolador de rede ( stall ) e não pode executar a próxima instrução. O registrador status é utilizado pelo controlador da rede para identificar o estado da operação de comunicação atual.

3.4.6.1 Comando de entrada O comando de entrada comporta-se como foi definido na Seção 2.7.2. Para implementar o comando de entrada o programador escreve o ID do processador fonte em CP2[0][0] ( corr ), executa a operação INPUT e lê de CP2[0][1] ( data ) o dado recebido. Não há verificação de tipo. O código do Programa 11 mostra a implementação do comando em uma função que pode ser chamada pelo programador.

Programa 11: Implementação do comando de entrada

C2_input: mtc2 $a0 , $0 , 0 # corr cop2 2 # INPUT mfc2 $v0 , $0 , 1 # data jr $ra nop

3.4.6.1.1 Implementação no simulador Ao identificar a operação INPUT, o controla- dor da rede verifica se a mensagem do processador correspondente está na caixa de mensagens; se a mensagem não chegou, o processador fica bloqueado até o próximo ciclo. Caso a mensagem tenha chegado, o controlador de rede cria uma mensagem ACK e tenta a enviar ao processador correspondente. Se o enlace estiver ocupado, o controlador repete a operação no próximo ciclo. Se o ACK foi enviado com sucesso, o controlador retira a mensagem da caixa de mensagens, os dados da mensagem são escritos em data e por fim, escreve o valor zero em status , o que desbloqueia o processador que efetuou a operação INPUT (libera o stall ). O pseudocódigo do Programa 12 indica a implementação do controle. mbox é a caixa de mensagens do nó, insmsg(dir, msg) insere a mensagem msg no link de saída do nó na direção dir, a função newack(to, from) cria uma mensagem ACK a ser enviada para o nó to pelo nó from.

Programa 12: Implementação do controle do comando de entrada

input ( mbox , * status , corr , ID , * data ) { Direction dir ;

struct msg * msg , * ack ;

msg = mbox [ corr ]; if (! msg ) { /* se n ã o chegou nova msg , */ return TRYAGAIN ; /* stall */ }

/* cria ACK */ ack = newack ( corr , ID );

dir = guidance ( ID , corr ); if ( insmsg ( dir , ack ) == FAILURE ) { return TRYAGAIN ; /* se enlace ocupado , */ } /* stall */

/* retira mensagem */ mbox [ corr ] = NULL ;

  • data = msg - > data ;

  • status = 0; /* fim da execu ç ã o do comando */

return SUCCESS ; }

3.4.6.2 Comando de saída O comando de saída é o mesmo definido na Seção 2.7.2. Para implementar o comando de saída o programador escreve o ID do processador destino em corr , escreve o dado a ser enviado em data e executa a operação OUTPUT. Não há verificação de tipo. O

3.4.6.3 Comando de seleção de alternativas O comando de seleção de alternativas difere do apresentado na Seção 2.7.3, pois a única expressão válida para as guardas são comandos de entrada com mais uma cláusula de escape. Para implementar o comando de seleção de alternativas o programador escreve em corr o endereço do vetor de alternativas (um vetor de números inteiros de 32 bits em que cada elemento do vetor é um processador fonte de um comando de entrada), escreve em CP2[0][5] ( nalt ) o tamanho do vetor de alternativas, executa a operação ALT e lê de data o dado recebido. Não há verificação de tipo. O Programa 15 mostra a implementação.

Programa 15: Implementação do comando de seleção de alternativas

C2_alt: mtc2 $a0 , $0 , 0 # corr mtc2 $a1 , $0 , 5 # nalt cop2 4 # ALT mfc2 $t0 , $0 , 1 # data sw $t0 , 0( $a2 ) addiu $v0 , $k1 , 0 jr $ra nop

3.4.6.3.1 Implementação no simulador Ao identificar a operação ALT, o controlador de rede busca na caixa de mensagens por alguma mensagem de algum dos processadores indi- cados no vetor de alternativas. Caso alguma dessas mensagem tenha chegado, o controlador da rede carrega data com os dados da mensagem, remove a mensagem da caixa de mensagens, envia ao processador correspondente uma mensagem ACK, carrega GPR[K1] com o ID da alternativa selecionada e carrega status com o valor zero. Se o último elemento do vetor de alternativas for o número − 1 , a operação completa com sucesso, mesmo que nenhum dado tenha sido rece- bido – essa alternativa é chamada cláusula default. Se nenhuma mensagem chegou na caixa de mensagens, o controlador bloqueia o processador que tentará novamente no próximo ciclo. A operação ALT possui uma estrutura de controle que é interna ao controlador de rede. A cada ciclo, o controlador começa a verificação da caixa de mensagens a partir do índice start (variável privada de cada nó, inicializada com valor zero), incrementando o índice de forma circular. Os valores da variável start são mantidas entre as execuções da operação. Esse algoritmo tenta garantir que todos os enlaces sejam pesquisados pelo do controlador. O pseudocódigo indicado no Programa 16 mostra a implementação do controle da operação. A variável global netsize é o número de nós na rede, belongs(arr, val) indica se o valor val pertence ao vetor arr.

Espaço em branco proposital.

Programa 16: Implementação do controle do comando seleção de alternativas

alt (* mbox , * start , * status , * corr , ID , ncl , * data ) { int defaults , found , done , sel ;

if ( corr [ ncl - 1] == -1) { /* default presente? */ defaults = 1; }

/* seleciona alternativa */ sel = * start ; found = 0; while (! done ) { if (( mbox [ sel ] != NULL ) && belongs ( corr , sel )) { done = found = 1; } else { sel = ( sel + 1) % netsize ; if ( sel == start ) { done = 1; } } }

if (! found ) { /* selecionou alternativa? */ if ( defaults ) { /* caso n ão , default presente? */ return SUCCESS ; /* sucesso */ } else { /* sen ão , stall */ return TRYAGAIN ; } }

  • start = (* start + 1) % netsize ; /* atualiza round - robin */

/* completa comando de entrada */ input ( mbox , status , sel , ID , data ); GPR [ K1 ] = sel ; return SUCCESS ; }

3.4.7 Temporização das instruções Dado um sistema com p processadores, as instruções que realizam operações de load e store demoram entre 1 e p ciclos para completar devido a implementação do controle do barramento, que impede inanição. As instruções que implementam os comandos de entrada e seleção de alternativas somente completam quando a mensagem com o dado é recebida pelo nó. No melhor caso, a instrução completa em um ciclo (a mensagem já estava na caixa de mensagens do nó). No caso geral, seja w ≥ 0 o tempo de espera pelo processador fonte para executar o comando de saída correspon- dente e m ≥ 1 o tempo esperando a transmissão da mensagem na rede, w e m contabilizados com base na execução da instrução. O tempo de execução em ciclos é:

w + m (12)

A instrução que implementa o comando de saída somente completa quando o nó recebe uma mensagem ACK. Seja y ≥ 0 o tempo de espera no processador destino para a execução do comando de entrada ou seleção de alternativas correspondente, y é contabilizado a partir da

Programa 18: Implementação do comando de saída no modelo base

EQV_output ( to , data ) { Sem_P (& rndz [ processor_id ()]; /* bloqueia */ mbox [ to ] = data ; /* envia dado */ Sem_V (& rndz [ to ]); /* libera quem vai ler o dado */ }

Para o comando de seleção de alternativas é necessário empregar um mecanismo de tipo round-robin para garantir justiça na seleção das alternativas. Uma variável global rridx[NPROC], um vetor de inteiros de 32 bits de inicializado com − 1 , usada para implementar o round-robin. Cada elemento do vetor armazena o índice que o processo utiliza no vetor de alternativas. Esse índice é incrementado a cada chamada ao comando de seleção de alternativas. Após incrementar o índice do round-robin , o processo atual efetua um comando de entrada para o processo indicado no elemento do vetor de alternativas. Ao completar, o comando retorna o ID do processo que foi selecionado.

Programa 19: Implementação do comando de seleção de alternativas no modelo base

EQV_alt (* clauses , ncl , * data ) { int id ;

id = processor_id ();

rridx [ id ] = ( rridx [ id ] + 1) % ncl ; /* atualiza round - robin */

  • data = EQV_input ( clauses [ rridx [ id ]]); /* l ê dado */ return clauses [ rridx [ id ]]; /* id do processador atendido */ }

4.2 Implementação das operações do semáforo no modelo CSP

Para emular as operações do semáforo utilizando os comandos CSP, são necessários dois processos: PROC_P e PROC_V. Os processos que utilizam o semáforo devem executar C2_output(PROC_P, DONTCARE) e C2_output(PROC_V, DONTCARE) para realizar as operações P() e V() respectivamente. O valor do output é descartado, por isso o valor enviado aos pro- cessos (DONTCARE) é irrelevante. O processo PROC_P é responsável por manter o valor do semáforo. Seja o vetor de inteiros de 32 bits procs[] de tamanho np o vetor de alternativas contendo os IDs dos processos que utilizam o semáforo. Após inicializar o semáforo, o processo executa as seguintes ações infinitamente: (i) caso valor do semáforo seja maior que 0, seleciona um processo que executou a operação P() e decrementa o valor; e (ii) caso valor do semáforo igual a 0, aguarda o processo PROC_V para incrementar o valor. O pseudocódigo no Programa 20 indica a implementação.

Espaço em branco proposital.

Programa 20: Implementação da operação P() no modelo CSP

PROC_P ( initval , * procs , np ) { int val ; int ret , ign ;

val = initval ; /* inicializa sem á foro */

for (;;) { if ( val > 0) { ret = C2_alt ( procs , np , & ign ); /* seleciona processo */ -- val ; /* decrementa sem á foro */ } else { C2_input ( PROC_V ); /* aguarda incremento */ ++ val ; /* incrementa sem á foro */ } } }

O processo PROC_V é responsável por indicar o incremento do valor do semáforo. A execução do processo consiste em atender uma solicitação de algum dos processos que executaram a operação V() e indicar o incremento ao processo PROC_P. O pseudocódigo no Programa 21 indica a implementação.

Programa 21: Implementação da operação V() no modelo CSP

PROC_V (* procs , np ) { int ret , ign ;

for (;;) { ret = C2_alt ( procs , np , & ign ); /* seleciona processo */ C2_output ( PROC_P , INCREMENT ); /* incremento do sem á foro */ } }

5 Código dos experimentos

Foram implementados 6 programas na linguagem C, cada um deles sendo uma solução para os problemas clássicos que nos interessam. Como o simulador representa um sistema altamente idealizado, para aproximar a simulação de um sistema realista, há um laço de espera ocupada, no qual os processos efetuam trabalho computacional, para além das operações de comunicação e sincronização.

5.1 Produtor-consumidor

O produtor e o consumidor se comunicam através de um vetor de números inteiros de 32 bits que implementa uma fila circular com MAXELEM = 16 elementos. O processo produtor produz um conjunto de inteiros representados em 32 bits, na faixa de 0 a MAXVAL - 1, MAXVAL = 1024. Cada inteiro é inserido no buffer pelo produtor e após produzir todos os valores, o processo produtor finaliza.