
\section{A Exploração do Paralelismo: Níveis de Abstração e Modelos}
\label{exploraçãodoparalelismo}
\cutname{exploracaodoparalelismo.html}

O objetivo deste capítulo é caracterizar a potencialidade para exploração do paralelismo em diferentes modelos de programação.

A classificação utilizada tem como critério o nível de abstração com que a exploração pode ser feita. Estes níveis de abstração estão sugeridos em SKILLICORN \cite{SKI98} e BAL \cite{BAL89}. A partir de textos específicos sobre os modelos, foram feitas considerações, e selecionados exemplos de sistemas paralelos.

\subsection{Modelos nos quais o Paralelismo é Explorado de Forma Totalmente Implícita}
\label{MPEFTI}
Nestes modelos, o programador descreve somente o propósito da computação, o que o programa deve fazer, e não como deve ocorrer o processamento para que o programa atinja seu propósito.

O desenvolvimento de \textit{software} não precisa levar em consideração se o programa irá executar em paralelo ou não. Em função disto, estes modelos trabalham em um nível de abstração elevado, e os programas desenvolvidos pensando no paralelismo não são necessariamente mais complexos que os elaborados para execução seqüencial.

Como exemplos característicos deste nível de abstração na exploração do paralelismo, destacam-se a programação funcional e a programação em lógica.

\subsubsection{Programação Funcional}

A programação funcional é uma alternativa elegante de programação declarativa, na qual o programa é definido por um conjunto de equações e funções, cujo resultado da computação é a solução das mesmas.

Os dois aspectos que tornam este paradigma atrativo é tanto o nível de abstração em que pode ser feito o desenvolvimento de \textit{software}, como o fato do mesmo ser suscetível de uma avaliação formal.

A entidade central da programação funcional é a função. Deste modo, funções podem devolver como resultados outras funções, as quais podem ser passadas como argumentos. A semântica de um programa funcional puro garante a ausência de efeitos colaterais (\textit{side}\textit{-}\textit{effects}) entre as sub-expressões de uma função; isto faculta uma análise paralela das mesmas, sem riscos de dependências de dados e/ou controle, JONES \cite{JON93}.

Um clássico programa que ilustra a possibilidade de avaliação paralela de sub-expressões é o algoritmo recursivo de Fibonacci:


\begin{figure}
	\centering
	\insimg{1}{imagens/numero-de-fibonacci-em-programacao-funcional.jpg}

	\caption{Números de Fibonacci em Programação Funcional}
	\label{fig:fibonacci}
\end{figure}


Como as duas chamadas recursivas de fibonacci são independentes, podem ser executadas em paralelo.

{\bf Fonte Implícita de Paralelismo}

A técnica utilizada para linguagens funcionais puras (de alta ordem e sem anotações) é denominada redução de grafo (\textit{Graph} \textit{Reduction}), PEYTON-JONE \cite{PEY92}. As funções são expressas como árvores, com sub-árvores comuns (para representar as sub-funções compartilhadas). A computação consiste em selecionar subestruturas do grafo, reduzí-las a formas mais simples e atualizar o grafo com as mesmas. Quando não existirem reduções a serem feitas, o grafo que resta é o resultado da computação.

Utilizando regras de exclusão para evitar sobreposições, múltiplos processadores podem pesquisar simultaneamente diferentes regiões do grafo correspondente ao programa. As reduções das sub-árvores poderiam, então, ser realizadas em paralelo.

% \paragraph{Exemplo de Exploração do Paralelismo}
% 
% Como exemplo de modelos que exploram o paralelismo a partir de linguagens funcionais temos o OBJ (GOGUEN \cite{GOG94}) e MAUDE (\cite{LEC95}). Ambos utilizam uma variante do método de grafo chamada \textit{Concurrent} \textit{Rewriting}. Nesta variante, as regras de redução são aplicadas concorrentemente pelas tarefas paralelas a determinados sub-termos, de forma repetida, até que se esgote a possibilidade de faze-lo.

{\bf Potencialidade de Exploração do Paralelismo}

Uma vez que o grafo correspondente ao programa funcional sem nenhum tipo de anotação varia de forma muito dinâmica durante sua redução, é bastante difícil gerenciar a distribuição de carga entre processadores, o total de novas tarefas criadas e o volume de comunicações. Segundo HANUS \cite{HAN94}, a exploração totalmente implícita do paralelismo na programação funcional resulta em um grande número de tarefas paralelas de pequena granulosidade.

Os modelos atualmente existentes para exploração totalmente implícita do paralelismo com programação funcional, tem obtido desempenho razoável em equipamentos com memória compartilhada, não conseguindo boa eficiência com equipamentos de memória distribuída (HUDAK \cite{HUD92}).

\subsubsection{Programação em Lógica}

Uma importante característica das linguagens de programação em lógica é que as mesmas são de atribuição única. Assim, de forma diferente das linguagens imperativas, elas não permitem atribuições destrutivas às variáveis, nem controle explícito do fluxo de execução do programa. Isto confere às linguagens de programação em lógica, uma semântica clara (declarativa) e uma decorrente construção de programas elegantes, compactos e menos sujeitos a erros oriundos de aspectos de implementação (STERLING \cite{STE94}).

Este forte aspecto declarativo permite que a avaliação de um programa em lógica possa ser feita por diferentes estratégias de controle de fluxo de execução. Isto implica que a ausência de efeitos colaterais decorrentes da semântica declarativa faculta que muitas das operações em um programa em lógica possam ser executadas em qualquer ordem (não determinismo), sem que com isto seja afetada a consistência de execução do mesmo.

{\bf Fontes Implícitas de Paralelismo}


Existem duas principais fontes de paralelismo implícito na programação em lógica (KERGOMMEAUX \cite{KER94}):

\textbf{Paralelismo OU}: este tipo de paralelismo refere-se a uma estratégia de busca paralela na árvore de metas do programa lógico. Assim, quando o processo de busca encontra uma ramificação na árvore de metas, ele pode iniciar processos paralelos de busca em cada ramo descendente (vide figura \ref{fig:fontesdeparalelismo}). O nome paralelismo OU deriva do fato que, em programas lógicos não determinísticos,  existem várias respostas (vários caminhos) que satisfazem o objetivo.

\textbf{Paralelismo E}: em termos da árvore de metas (vide figura \ref{fig:fontesdeparalelismo}), o paralelismo E corresponde à construção paralela de uma ramificação. Neste caso, quando o processo de busca reconhece que um número de passos deve ser efetuado para completar um ramo, ele pode iniciar processos paralelos para avaliar estes passos.

Outra fonte de paralelismo implícito na programação em lógica é o paralelismo de unificação. O paralelismo disponibilizado é de baixa granulosidade e, para ser explorado com eficiência, exige \textit{hardware} especializado.

O paralelismo E, por sua vez, pode ser explorado entre literais independentes (sem possibilidade de conflito na atribuição de valores a variáveis), ou entre quaisquer literais, às custas de um mecanismo mais complexo de detecção e gerência do paralelismo.

{\bf Exemplo de Exploração do Paralelismo}

A grande maioria dos modelos que exploram paralelismo na programação em lógica, implementam a linguagem Prolog (STERLING \cite{STE94}), e utilizam a WAM (Warren Abstract \textit{Machine} - WARREN \cite{WAR83}, AIT-KACI \cite{AIT90}) como estratégia de compilação.

O OPERA (BRIAT \cite{BRI91}, GEYER \cite{GEY91}) é um exemplo de modelo que explora de forma implícita o paralelismo OU. Neste modelo, o paralelismo é explorado de forma multiseqüencial, no qual cada processador ativo está, em determinado momento, trabalhando de forma independente, sobre um ramo específico da árvore de busca.


\begin{figure}[!htb]
	\centering
	\insimg{1}{imagens/fontes-de-paralelismo-na-programacao-em-logica.jpg}

	\caption{Fontes de Paralelismo na Programação em Lógica}
	\label{fig:fontesdeparalelismo}
\end{figure}



O modelo \&-Prolog (HERMENEGILDO \cite{HER90}) é um dos mais maduros modelos explorando paralelismo $E$ independente. Ele combina uma detecção de independência a nível de compilação com um eficiente ambiente de execução implementado em multiprocessadores com memória compartilhada. Sua proposta é fundamentada no Paralelismo $E$ Restrito (DEGROOT \cite{DEG84}).

O paralelismo pode ser explorado automaticamente, a partir de um programa em Prolog padrão (opcionalmente o programador pode fazer anotações). O compilador do modelo executa a transformação de programas Prolog para \&-Prolog. A análise estática do compilador detecta a independência entre literais, mesmo na presença de predicados com \textit{side}\textit{-}\textit{effects} (MUTHUKUMAR \cite{MUT89} e \cite{MUT89a}).

Os programas \&-Prolog são compilados em uma extensão da WAM denominada PWAM, cuja principal diferença em relação a WAM é a adição de uma \textit{pilha de literais} \textit{paralelizáveis}, onde processadores inativos retiram trabalho.

{\bf Potencialidade de Exploração do Paralelismo}


As alternativas de exploração do paralelismo na programação em lógica são fortemente ortogonais entre si; isto significa que podem ser explorados simultaneamente, sem que um comprometa o outro.

O paralelismo OU, presente em programas com não determinismo, caracteriza-se por uma granulosidade mais elevada, o que faculta sua exploração em máquinas de memória distribuída. Por sua vez, o paralelismo E, passível de ser explorado praticamente em qualquer programa em lógica, apresenta uma granulosidade pequena, daí a maioria dos modelos existentes serem orientados a equipamentos de memória compartilhada (baixo custo de comunicação) (KERGOMMEAUX \cite{KER94}).

Os modelos para exploração do paralelismo na programação em lógica de forma totalmente implícita, apresentam um elevado nível de abstração. Este aspecto, que lhes confere elegância, portabilidade e possibilidade de aproveitamento de toda cultura da programação seqüencial disponível, também dificulta a garantia de desempenho destes modelos frente à diversidade das aplicações reais. Porém, é importante ter presente que situações de bom ganho de desempenho foram registradas. Por sua vez a elevada dinâmica da programação em lógica dificulta sobremaneira qualquer medida de custo.

A exploração do paralelismo na programação em lógica se mostra promissora. A maioria dos modelos implementados explora somente um tipo de paralelismo, uns poucos exploram dois tipos, e nenhum explora efetivamente todas as alternativas de paralelismo possíveis.

O custo-benefício da exploração implícita do paralelismo ainda não atingiu patamares satisfatórios (KERGOMMEAUX \cite{KER94}).


\subsection{Modelos com Assinalamento do Paralelismo Explícito}

Nestes modelos, o programador deve estar ciente da natureza do paralelismo que será explorado, e deve expressar todo o potencial para o mesmo no código, porém não precisa se envolver como este será efetivamente tratado pelo ambiente de execução. Portanto, fica a cargo do modelo como irá ocorrer a decomposição, o mapeamento, a comunicação e a sincronização das tarefas paralelas.

Assim, o programa deve expressar o máximo paralelismo inerente ao algoritmo. A implementação do mesmo (compilação e execução), por sua vez, reduzirá este nível de paralelismo ao possível de ser explorado em função da arquitetura destino e dos decorrentes custos de escalonamento, comunicação e sincronização.

Como exemplo de paralelismo neste nível de abstração temos exploração do paralelismo de dados.

\subsubsection{Paralelismo de dados - exploração de laços}

O paralelismo de dados tem uma origem histórica no uso de processadores pipeline. Neste caso, a exploração de paralelismo ocorria com a aplicação de forma independente e repetida de uma mesma operação sobre dados diferentes. Tal situação permitia o uso de processadores com registradores vetoriais, com os quais o paralelismo era explorado, associado a um custo reduzido de controle das repetições.

Com o desenvolvimento das arquiteturas matriciais (SIMD), as mesmas se tornaram ótimas candidatas para processar em paralelo o código vetorizado. Por sua vez, o código para arquiteturas matriciais pode ser eficientemente executado em multiprocessadores.

Deste modo, o paralelismo de dados, inicialmente destinado a pipelines vetoriais, pode atualmente ser explorado em diversas arquiteturas paralelas.

Assim, genericamente, o paralelismo de dados é uma estratégia na qual as rotinas paralelas são composições de operações aplicadas a dados de um determinado tipo, e que produzem
resultados do mesmo tipo.

\textbf{Fonte de paralelismo}

No caso mais usual, o paralelismo de dados envolve estruturas de dados organizadas na forma de matrizes de dimensões variadas (estruturas regulares), e o paralelismo se viabiliza quando estes dados são passíveis de manipulação independente. Esta manipulação independente é comum na álgebra matricial.


{\bf Exemplo de Exploração do Paralelismo}

Como exemplos típicos de modelos que exploram paralelismo de dados surgem os diversos dialetos FORTRAN. O HPF (\textit{High}\textit{ Performance }\textit{Fortran} - THAKUR \cite{THA96}, MEHROTRA \cite{MEH93}), particularmente, potencializa a exploração do paralelismo de dados inerente aos laços, disponibilizando construtores que devem ser utilizados nos programas para determinar como as estruturas de dados devem ser alocadas aos processadores.

Para fazer isto o programador precisa ter clareza da relação entre os dados.

% Por exemplo, a diretiva Align no HPF especifica que determinados dados devem ser distribuídos da mesma maneira. Por exemplo:
% 
% \begin{center}
% \begin{framed}
% \begin{verbatim}
% 
% 	!HPF$  Align A ( : ; : )  with Y  ( : ; : )
% 	
% \end{verbatim}
% \end{framed}
% \end{center}
% 
% 
% A presença da diretiva Align alinha a matriz A com a Y, isto é, garante que os elementos de A e Y com os mesmos índices serão alocados ao mesmo processador.
% A diretiva Independent, por sua vez, informa ao compilador que as operações em um determinado laço podem ser executadas independentemente. Por exemplo, no código a seguir fica determinado que Z e K não compartilham o mesmo espaço de memória e podem, deste modo, ser executados de forma independente (portanto em paralelo).
% 
% \begin{center}
% \begin{framed}
% \begin{verbatim}
% 
% !HPF$ Independent
% 	Do I = 1, 1000
% 	A(I) = B(I)
% 	End Do
% 
% \end{verbatim}
% \end{framed}
% \end{center}


{\bf Potencialidade de Exploração do Paralelismo}


O código para exploração do paralelismo de dados é simples de ser escrito e depurado. Isto decorre do paralelismo ser explicitamente trabalhado pelos sincronismo e fluxo de controle inerentes aos equipamentos matriciais.

Os programas para paralelismo de dados necessitam, para sua execução, de uma pré-distribuição dos conjuntos de dados que serão manipulados. Deste modo, a organização das estruturas de dados utilizadas neste tipo de paralelismo é determinante para o seu desempenho.

Portanto, este paralelismo é centrado em computações locais e operações de manipulação de dados (replicações, permutações, reduções, etc.). Pode ser explorado com sucesso mesmo em problemas de baixa granulosidade, desde que estes utilizem conjuntos de dados com estruturas multidimensionais regulares.

Dependendo da granulosidade inerente ao problema e do algoritmo adotado, o paralelismo de dados pode também ser explorado em multiprocessadores tanto de memória compartilhada como distribuída.

Porém, seu emprego é mais facilmente potencializado nas arquiteturas síncronas, as quais conseguem explorar eficientemente o paralelismo a nível de instrução.

O paralelismo de dados, tem sido utilizado com ótimos desempenhos em diversas arquiteturas. A simplicidade do seu código facilita a codificação e  eventuais recodificações quando de migrações entre arquiteturas diferentes. Faculta uma previsão de desempenho e também medida de custo. Porém, é possível sua exploração com desempenho somente quando os dados tiverem as características de independência e regularidade mencionadas.

\subsection{Modelos com Assinalamento e Decomposição do Paralelismo Explícitos}

Estes modelos delegam para o programador a decisão de como será decomposto o trabalho paralelo. As implicações desta decisão, no entanto, serão tratadas de forma transparente pelo modelo.

Uma vez divido o problema, a atribuição das partes aos processadores e a forma como estas vão se comunicar e sincronizar não precisam ser explicitadas no programa.

Existem poucas propostas neste nível de abstração (somente duas), e sua implementação ocorre na forma de bibliotecas utilizadas a partir das linguagens C e FORTRAN. Como exemplo deste nível de abstração, surge a exploração do paralelismo com restrição de comunicações e sincronização.

\subsubsection{Exemplo de Exploração do Paralelismo}

Um modelo que explora o paralelismo neste nível é o BSP (\textit{Bulk} \textit{Synchronous} \textit{Parallelism} \textit{model} - SKILLICORN \cite{SKI94}, VALIANTE \cite{VAL90}). As computações em BSP são divididas em fases, alternando comunicações globais e computações locais.

Cada fase inicia com a ativação das operações de comunicação. O processamento nas tarefas paralelas ocorre utilizando somente referências a dados locais. Enquanto isto, de forma independente, a rede de interconexões realiza as trocas de dados necessárias entre os processadores. Ao final de cada fase, chamadas de \textit{superstep}, todas as comunicações são entendidas como prontas (para garantir isto é utilizada uma barreira de sincronização) e todas as atribuições feitas à memória global (que dizem respeito à tarefa paralela do processador) são reproduzidas localmente. Do ponto de vista do programador, as solicitações de dados à memória global remota serão feitas pelo BSP no \textit{superstep} anterior àquele no qual eles serão utilizados.

Uma característica que se destaca no BSP é o fato do mesmo expressar as propriedades da rede de interconexão utilizada na arquitetura paralela a partir de uns poucos parâmetros. A máquina abstrata do BSP consiste de uma coleção de \textit{p} processadores, cada qual com sua memória local, todos conectados através da rede de interconexão. Os parâmetros da rede de interconexão utilizados são: \textit{l} tempo necessário para a realização da barreira de sincronização, e \textit{g} - a razão na qual dados locais com endereço aleatório podem ser liberados/recebidos. Estes parâmetros são determinados experimentalmente para cada computador paralelo.

Deste modo, se o tempo consumido com computações locais exigir um total \textit{w} e o volume de valores recebidos e enviados pelo processador for \textit{h}, então o tempo total gasto em um \textit{superstep} é:


\begin{center}
\begin{framed}


t = w + hg + l


\end{framed}
\end{center}


Considerando que a computação seqüencial tem diversas métricas de complexidade disponíveis, e trabalhando com o maior valor previsto para as variáveis anteriores, o tempo máximo que um \textit{superstep} irá depender pode ser facilmente obtido.

O outro modelo que trabalha neste nível de abstração é o logP (CULLER \cite{CUL93}). Também neste modelo são utilizadas \textit{threads} com contextos locais e com atualizações de dados por comunicações globais.

\subsubsection{Potencialidade de Exploração do Paralelismo}


O nível de abstração dos programas em BSP é bastante elevado.

Apesar de sua decomposição em \textit{threads} ser feita pelo programador, a alocação das mesmas e toda comunicação/sincronização é feita de forma transparente para o mesmo.

Podem ser obtidos bons desempenhos com o modelo do BSP, porém sua previsibilidade é influenciada pelos seguintes fatores:

\textbf{Comunicações}: a otimização das comunicações depende de como ocorre a alocação (automática) das \textit{threads} aos processadores, tendo em vista as características da rede de interconexão (topologia, largura de banda, etc.);

\textbf{Sincronização}: o custo total introduzido pelas barreiras de sincronização entre cada fase de execução, depende da natureza do problema e do algoritmo utilizado (sua granulosidade, possibilidade de ser particionado em partes homogêneas, etc.).

Por outro lado, um ponto forte do modelo BSP é a existência de medida de custo, através da qual é possível obter o custo real de execução de um programa para qualquer arquitetura, desde que sejam conhecidos os parâmetros \textit{l} e \textit{g }para a mesma.

A versão corrente do BSP trabalha com uma biblioteca SPMD (\textit{Simple} \textit{Program} \textit{Multiple}\textit{ Data}), que fornece operações para colocar dados na memória remota de outro processo, para receber dados de um processo remoto e para sincronização (pode ser utilizada a partir de C ou FORTRAN).


\subsection{Modelos com Assinalamento, Decomposição e Mapeamento do Paralelismo Explícitos}


Nestes modelos, o programador, além de dividir o programa em partes, tem a incumbência de avaliar qual o melhor processador para cada parte. Uma vez que a proximidade dos processadores é determinante para o desempenho das comunicações, é indispensável para o programador ter conhecimento das características da rede de interconexões utilizada na arquitetura. Este comprometimento do código com as características do equipamento, trazem repercussões nos custos de migração de \textit{software} entre diferentes arquiteturas.

Por sua vez, o ambiente de execução se responsabiliza pelo gerenciamento das comunicações e das sincronizações entre as tarefas paralelas.

\subsubsection{Exemplo de Exploração do Paralelismo}

Um modelo que trabalha neste nível de abstração é o ``Linda"~ (CARRIERO \cite{CAR92}). Neste conhecido modelo, as comunicações ponto-a-ponto são substituídas por um espaço compartilhado, no qual os dados são colocados pelos processadores e a partir do qual são recuperados associativamente. Este espaço é denominado \textit{espaço de }\textit{tuplas}.

As três operações básicas do modelo de comunicações utilizado pelo Linda são: uma que lê o \textit{espaço de }\textit{tuplas} buscando aquelas que satisfazem os campos informados e a correspondente aridade, uma segunda que faz a leitura porém remove a tupla que sastisfaz a condição do espaço de tuplas e uma terceira que coloca uma tupla no \textit{espaço de }\textit{tuplas}.

As operações de comunicação em Linda desconectam o emissor do receptor, isto é, a \textit{thread} que envia informação, em nenhum momento precisa saber qual vai recebé-la. Não existe conexão nem espacial e nem temporal entre os mesmos.


\subsubsection{Potencialidade de Exploração do Paralelismo}

Como as comunicações entre programas exigem que ambos os lados (emissor e receptor) tenham seus parâmetros devidamente concordantes, e considerando que os programas paralelos usualmente contemplam muitas comunicações, esta tarefa de especificar comunicações introduz um custo elevado no desenvolvimento do \textit{software} paralelo.

Os modelos que operam neste nível de abstração reduzem muito este custo, oferecendo primitivas de alto nível para especificação das trocas de dados. Normalmente esta simplificação é feita separando, nos programas, os aspectos de processamento dos de comunicação. Normalmente é disponibilizada uma linguagem a parte (subconjunto da linguagem) para tratar das comunicações. Esta divisão faz com que a computação e a comunicação fiquem ortogonais entre si, de tal forma que uma particular estratégia para as comunicações possa ser utilizada com diversas linguagens seqüenciais. Este é o caso particular de Linda.

É preciso ter presente que a combinação de decomposição explícita do paralelismo, com um mecanismo de troca de dados que garante um desacoplamento entre as partes comunicantes, é uma fonte de possíveis inconsistências no estado de execução do programa paralelo (\textit{deadlocks}). Por exemplo, uma \textit{thread} pode ficar bloqueada aguardando uma tupla que nunca será inserida no espaço de tuplas. Apesar de ser possível um desacoplamento entre emissor e receptor, o algoritmo introduz necessidades de sincronização (que neste caso é feita através dos dados) que são inerentes à natureza do algoritmo que está sendo computado.

Como a eficiência da implementação das abstrações de alto nível utilizadas na construção e gerência do espaço de tuplas (comunicações) é dependente da rede de interconexão utilizada na arquitetura, não é possível trabalhar com medidas de custo.

\subsection{Modelos com Assinalamento, Decomposição, Mapeamento e Comunicação Explícitos}

Nestes modelos, o programador tem a seu encargo especificar quase todos os detalhes da implementação paralela, exceto os aspectos pertinentes a sincronização.

Para oferecer isto, via de regra, estes modelos empregam uma semântica assíncrona, na qual as mensagens são enviadas, porém o emissor não fica atrelado ao tempo necessário para que elas
atinjam seu destino.

\subsubsection{Exemplo de Exploração do Paralelismo}

O mais importante modelo nesta classe é Atores (\textit{Actors} - AGHA \cite{AGH86}). Ele consiste de uma coleção de objetos chamados atores, todos contendo uma pilha de mensagens de entrada. Todo ator executa repetidamente a seqüência: leitura da mensagem disponível na pilha de entrada; envia suas mensagens a todos os outros atores que ele conhece e a partir das mensagens que chegaram define seu novo contexto, o qual determinará suas respostas às próximas mensagens que receber.

As mensagens são enviadas de forma assíncrona e sem preocupação de ordem.

Os nomes dos atores podem ser distribuídos através de mensagens. A exploração de concorrência e distribuição em objetos é amplamente discutida em BRIOT \cite{BRI98}.


\subsubsection{Potencialidade de Exploração do Paralelismo}

Além dos custos de comunicação, outro ponto de estrangulamento (gargalo) do desempenho do modelo de atores é o processamento seqüencial das mensagens na fila de entrada. Para minimizar este aspecto, o modelo ActorSpace (AGHA \cite{AGH93}) propõe estratégias para reduzir o número de mensagens distribuídas.

A comunicação entre processadores no modelo atores e seus derivados é grande, o que aumenta consideravelmente o indeterminismo introduzido pelo desempenho da rede de interconexões da arquitetura. Em função disto, não é possível nestes modelos dar, garantia de desempenho, ou medida de custo.


\subsection{Modelos nos quais o Paralelismo é Explorado de Forma Totalmente Explícita}

Nestes modelos o programador precisa especificar todos os aspectos da implementação. Em função disto, se torna uma tarefa trabalhosa desenvolver \textit{software} empregando tais modelos, porque tanto a sua correção quanto seu desempenho, somente podem ser atingidos pela criteriosa atenção de um grande número de detalhes.

Os primeiros modelos para o paralelismo, na sua grande maioria, atuavam neste nível, normalmente voltados para um particular tipo de arquitetura, e com sua execução paralela gerenciada de forma totalmente explícita.


\subsubsection{Exemplo de Exploração do Paralelismo}

Um conhecido exemplo de exploração de paralelismo neste nível é a linguagem \textit{SR} (\textit{Synchronizing} \textit{Resources} - ANDREWS em \cite{AND81} e \cite{AND82} e ANDREWS e OLSSON em \cite{AND86}, \cite{AND88} e \cite{AND92}). Nesta linguagem, estão presentes as características usuais do paradigma convencional (imperativo), tais como: tipos, variáveis, atribuição destrutiva, comandos de controle de repetição, comandos de  seleção simples e múltipla, procedimentos, etc. Para exploração do paralelismo \textit{SR} fornece mecanismos específicos para gerenciamento da concorrência, comunicação e sincronização.

Em \textit{SR} um programa pode ser formado por diversos espaços de endereçamento (máquinas virtuais), os quais podem estar localizados em múltiplos computadores (máquinas físicas). Os processos residentes em um mesmo espaço de endereçamento podem compartilhar memória. Desta forma, a linguagem \textit{SR} suporta programação em ambientes distribuídos e ambientes com memória compartilhada. SR é baseada no conceito de recurso (\textit{resource}). O recurso é um módulo que pode conter diversos processos.

Um recurso é dinamicamente criado de maneira explicita, pelo comando \textit{create}. Os seus processos comunicam-se utilizando semáforos para sincronização. A comunicação entre processos de recursos remotos pode ser feita através de troca de mensagens assíncronas, chamada remota de procedimentos (RPC) e \textit{rendezvous}.



\subsubsection{Potencialidade de Exploração do Paralelismo}

De forma análoga a outros modelos de sua categoria, a linguagem SR não oferece recursos de abstração para detecção, decomposição, comunicação ou sincronização do paralelismo. A SR, também como outros modelos análogos, costuma oferecer mecanismos alternativos para o programador gerenciar o paralelismo, porém, mesmo assim, é inviável conciliar em um programa independência de arquitetura e máximo desempenho.

Dependendo da arquitetura, problemas de qualquer granulosidade podem ser computados com desempenho em SR. Para uma arquitetura em particular, SR oferece garantia de desempenho e medida de custo, no entanto, pelos detalhes que exige, torna o desenvolvimento de \textit{software} paralelo uma tarefa onerosa.
