• Nenhum resultado encontrado

UNIVERSIDADE DO VALE DO ITAJAÍ CENTRO DE CIÊNCIAS TECNOLÓGICAS DA TERRA E DO MAR CURSO DE CIÊNCIA DA COMPUTAÇÃO

N/A
N/A
Protected

Academic year: 2021

Share "UNIVERSIDADE DO VALE DO ITAJAÍ CENTRO DE CIÊNCIAS TECNOLÓGICAS DA TERRA E DO MAR CURSO DE CIÊNCIA DA COMPUTAÇÃO"

Copied!
67
0
0

Texto

(1)

UNIVERSIDADE DO VALE DO ITAJAÍ

CENTRO DE CIÊNCIAS TECNOLÓGICAS DA TERRA E DO MAR

CURSO DE CIÊNCIA DA COMPUTAÇÃO

AVALIAÇÃO DE TÉCNICAS DE ENGENHARIA DE SOFTWARE PARA

MODELAGEM DE PROGRAMAS PARALELOS EM AMBIENTES

MULTICORE

Área de

Engenharia de Software

por

David GraminhoVictor

Marcello Thiry Comicholi da Costa, Dr.

Orientador

(2)

UNIVERSIDADE DO VALE DO ITAJAÍ

CENTRO DE CIÊNCIAS TECNOLÓGICAS DA TERRA E DO MAR

CURSO DE CIÊNCIA DA COMPUTAÇÃO

AVALIAÇÃO DE TÉCNICAS DE ENGENHARIA DE SOFTWARE PARA

MODELAGEM DE PROGRAMAS PARALELOS EM AMBIENTES

MULTICORE

Área de

Engenharia de Software

por

David Graminho Victor

Relatório apresentado à Banca Examinadora do Trabalho de Conclusão do Curso de Ciência da Computação para análise e aprovação.

Orientador: Marcello Thiry Comicholi da Costa, Dr

(3)

DEDICATÓRIA

Dedicado àquela que considero a responsável por toda e qualquer conquista em minha vida: minha mãe!

(4)

AGRADECIMENTOS

Agradeço ao meu orientador Marcello Thiry pelo empenho, profissionalismo e pontualidade nos momentos que se fizeram necessários. Sem dúvida alguma, a realização desse trabalho não seria possível sem seu apoio.

Agradeço aos meus familiares e amigos que de alguma forma me incentivaram direta e indiretamente.

E agradeço à minha mãe, Araceli Graminho...por tudo! Não consigo me lembrar de nada que eu não deva agradecê-la!

(5)

SUMÁRIO

LISTA DE ABREVIATURAS...v

LISTA DE FIGURAS...vi

LISTA DE TABELAS ...vii

RESUMO...viii

ABSTRACT...ix

1. INTRODUÇÃO ...10

1.1 PROBLEMATIZAÇÃO... 13

1.1.1 Formulação do Problema ... 13

1.1.2 Solução Proposta ... 15

1.2 OBJETIVOS ... 16

1.2.1 Objetivo Geral... 16

1.2.2 Objetivos Específicos... 16

1.3 METODOLOGIA... 17

1.3.1 Caracterização da metodologia ... 17

1.3.2 Metodologia de desenvolvimento... 17

1.4 ESTRUTURA DO TRABALHO ... 17

2. FUNDAMENTAÇÃO TEÓRICA ...19

2.1 VANTAGENS DA ARQUITETURA MULTICORE... 21

2.1.1 Paralelismo implícito versus paralelismo explícito... 22

2.2 A ARQUITETURA MULTICORE... 24

2.2.1 Taxonomia de computadores paralelos... 24

2.2.2 Multiprocessadores ... 25

2.3 DESEMPENHO DE COMPUTADORES MULTIPROCESSADOS... 30

2.4 MODELOS DE PROGRAMAÇÃO PARALELA... 32

2.4.1 Estrutura básica de software paralelo ... 33

2.4.2 Linguagens para programação paralela ... 37

2.5 MODELAGEM DE SOFTWARE PARALELO ... 38

2.5.1 UML... 39

2.5.2 Redes de Petri... 43

3. MODELAGEM DO ESTUDO DE CASO...45

3.1 ESTUDO DE CASO ... 45

3.1.1 Modelagem (Paradigma seqüencial) ... 46

3.1.2 Modelagem (Paradigma paralelo)... 49

4. AVALIAÇÃO E TRABALHOS FUTUROS...58

(6)

LISTA DE ABREVIATURAS

CFD Computational Fluid Dynamics CPU Central Processing Unit

HTM Hardware Transactional Memory

JVM Java Virtual Machine

MIMD Multiple Instruction Multiple Data MISD Multiple Instruction Single Data

NUMA NonUniform Memory Access

RPC Remote Procedure Call

SIMD Single Instruction Multiple Data SISD Single Instruction Single Data SMP Symmetric Multiprocessors SPMD Single Program Multiple Data STM Software Transactional Memory TCC Trabalho de Conclusão de Curso

UMA Uniform Memory Access

UML Unified Modeling Language

(7)

LISTA DE FIGURAS

Figura 1. Transistores ...11

Figura 2. Velocidade do processador e da memória ...12

Figura 3.Arquitetura interna de processador multicore da Intel ...12

Figura 4. Método Java compartilhado por dois processos paralelos...14

Figura 5. Planos da Intel para produção de processadores multicore ...19

Figura 6. Arquitetura multicore de barramento único...28

Figura 7. Componente seqüencial e seções paralelizáveis ...31

Figura 8. Ganho perfeito em relação ao ganho real de desempenho de sistema paralelos...32

Figura 9. Classe ativa ...40

Figura 10. Processos paralelos em Redes de Petri: (a) paralelização; (b) sincronização ...44

Figura 11. Código Java para processos paralelos...50

Figura 12. Classe Task...51

Figura 13. Grafo para o problema do caixeiro viajante ... ...53

(8)

LISTA DE TABELAS

Tabela 1. Taxonomia de Flynn ...25

Tabela 2. Protocolo write-invalidate ...30

Tabela 3. Protocolo write-update... 30

Tabela 4. Aplicação de métricas (paradigma seqüencial) ...61

(9)

RESUMO

VICTOR, David Graminho. Avaliação de técnicas de engenharia de software para modelagem

de programas paralelos em ambientes multicore. São José, 2008. 55 f. Trabalho de Conclusão de

Curso (Graduação em Ciência da Computação)–Centro de Ciências Tecnológicas da Terra e do Mar, Universidade do Vale do Itajaí, São José, 2008.

Com o advento da tecnologia multicore os problemas provenientes das limitações relacionadas à miniaturização, como o calor dissipado pelos componentes e a interferência, são atenuados. Além disso, outra vantagem surge com a tecnologia multicore: ao dividir o processamento em vários núcleos, há a possibilidade de executar as instruções das aplicações de forma paralela ao invés da execução serial, aumentando a escalabilidade e desempenho dessas aplicações. A arquitetura multicore possui, dentro de um único chip de processador, dois ou mais núcleos de execução e possibilita – com o software apropriado – a efetiva execução paralela de múltiplas threads. No entanto, para haver uma efetiva paralelização é necessário que o software também seja paralelizado de alguma forma. No modelo atual de desenvolvimento de software, o projetista utiliza uma abstração de alto nível do hardware no qual o software irá executar, sem se preocupar com detalhes de restrições físicas. Porém, para o modelo paralelo, os aspectos nos níveis mais baixos de abstração devem ser considerados. Novos paradigmas deverão ser buscados em diferentes linhas de pesquisa. Na Engenharia de Software, por exemplo, será necessário buscar abstrações adequadas à necessidade de otimização no nível físico. É justamente esse o foco principal do presente trabalho: avaliar técnicas atuais de modelagem de software paralelo que possibilitem sua implementação em nível adequado de abstração sem descuidar da escalabilidade e de maior aproveitamento da tecnologia multicore, visando garantir o nível de concorrência adequado.

(10)

ABSTRACT

With the advent of the multicore technology the problems proceeding from the limitations related to the miniaturization, as the heat wasted for the components and the interference, are attenuated. Beyond this solution having brought excellent benefits, another great advantage appears with the multicore technology: when dividing the processing in some cores, has the possibility to execute the instructions of the applications of parallel form instead of the serial execution being increased the scalability and performance of these applications. The multicore architecture inside possess of only chip of processor two or more cores of execution and makes possible - with appropriate software - the effective parallel multiple execution threads. However, to have an effective parallelization it is necessary that software also is paralleled of some form. In the current model of software development, the designer uses an abstraction of high level of the hardware in which software will go to execute, without if worrying about details of physical restrictions. However, for the parallel model, the aspects in the abstraction levels lowest must be considered. New paradigms will have to be searched in different lines of research. In the Engineering of Software, for example, it will be necessary to search adequate abstractions to the necessity of optimization in the physical level. And is exactly this the main focus of gift work. Thus, objective to evaluate current techniques of modeling of parallel software that make possible its implementation in an adequate level of abstraction without neglecting of the scalability and a bigger exploitation of the multicore technology aiming at to guarantee the adequate level of competition.

(11)

1. INTRODUÇÃO

Há mais de três décadas o engenheiro Gordon Moore previu que a densidade1 de transistores em um chip duplicaria a cada 18 meses. Essa taxa de crescimento é conhecida popularmente como lei de Moore (FAZZIO, 2004). Essa lei baseou-se na verificação de Moore ao observar que a dimensão linear do transistor se reduziria a cada ciclo de uma nova tecnologia, geralmente impulsionada pela necessidade de obter melhores índices de desempenho. O aumento na velocidade com que um computador executa uma operação está diretamente relacionado com a densidade de transistores em um chip (FAZZIO, 2004). Com isso, consegue-se aumentar continuamente a freqüência de operação dos processadores. A solução aplicada visando alcançar esse objetivo foi a fabricação de transistores em camadas de silício cada vez menos espessas a fim de reduzir o seu tamanho (CARDOSO, ROSA & FERNANDES, 2006).

A Figura 1 apresenta o tamanho das camadas de silícios e das portas de alguns transistores existentes e de alguns protótipos. A fabricante de processadores Intel prevê, para o ano de 2011 aproximadamente, que a tecnologia a ser empregada possibilitará produzir transistores com drenos de 22 nm (nanomilímetros) e portas de 10 nm (apresentado na Figura 1 como L). Esse cenário é preocupante uma vez que, quanto menor for a largura da porta, mais próximas ficarão as regiões da fonte e dreno do transistor. Segundo Cardoso, Rosa e Fernandes (2006) quando a largura da porta chegar a 5 nm, fonte e dreno ficarão separadas por um trecho de silício tão pequeno que não conseguirá isolá-los completamente, gerando uma probabilidade de 50% de que a corrente flua mesmo quando não houver tensão aplicada à porta. Quando isso ocorre o transistor deixa de ser confiável como dispositivo de processamento de dados. Assim, a miniaturização alcançará os limites impostos pelas leis da física (FAZZIO, 2004).

Há ainda outro problema: a dissipação de energia. Como o espaço dentro de um processador é limitado e os componentes exigem energia, a intensa junção desses aumenta a densidade provocando interferências e uma produção excessiva de calor devido a dissipação de energia causada pela corrente elétrica que circula nos transistores. Se essa energia não for rapidamente removida do circuito e transferida para o ambiente, o chip atingirá temperaturas tão elevadas que, literalmente, derreterá (CARDOSO, ROSA & FERNANDES, 2006).

(12)

Figura 1. Transistores

Fonte: adaptado de Cardoso, Rosa e Fernandes (2006) e Fazzio (2004)

Segundo Paolo Gargine (apud CARDOSO, ROSA & FERNANDES, 2006), Diretor de Tecnologia da Intel, mesmo que se conseguisse contornar o limite da largura da porta, não haveria como remover dele o calor com a mesma rapidez com que seria produzido. O chip se autodestruiria. Há ainda outras limitações impostas pela arquitetura de um único núcleo como a estreita banda de dados, que faz com que 75% do tempo do processador (em média) seja gasto esperando por resultados dos acessos à memória devido à grande diferença entre a velocidade do processador e a da memória (CARDOSO, ROSA & FERNANDES,2006). A Figura 2 ilustra essa diferença (gap) de velocidade. Por último, existe o custo financeiro relacionado aos processos de miniaturização. No contexto apresentado, pode ocorrer o aumento do preço do transistor e não a diminuição, como acontece atualmente. Em síntese, dentro do paradigma da atual arquitetura, os chips em breve não mais evoluirão e a lei de Moore será menos relevante (FAZZIO, 2004)

(13)

Figura 2. Velocidade do processador e da memória Fonte: MA (2006)

O cenário apresentado impõe limites para a miniaturização e para o número de componentes em um chip. A tecnologia multicore é uma resposta aos problemas causados pela miniaturização dos componentes no núcleo de um processador. Sinteticamente, a arquitetura multicore consiste em agregar dentro de um único chip de processador dois ou mais núcleos de execução, possibilitando – com o software apropriado – a efetiva execução paralela de múltiplas threads. O sistema operacional trata cada um desses núcleos de execução como processadores diferentes, com todos os seus recursos de execução associados. Cada núcleo de execução possui seu próprio cache e pode processar várias instruções simultaneamente. A Figura 3 ilustra essa arquitetura

Figura 3.Arquitetura interna de processador multicore da Intel

Com o advento da tecnologia multicore as limitações relacionadas à miniaturização, como o calor dissipado pelos componentes e a interferência, são atenuadas. Há outra vantagem relacionada

(14)

à tecnologia multicore: ao dividir o processamento em vários núcleos, há a possibilidade de executar as instruções das aplicações de forma paralela ao invés da execução serial (TANENBAUM, 2001), aumentando a escalabilidade e desempenho dessas aplicações. Com a adoção de dois ou mais núcleos, é possível obter o paralelismo real entre as threads de uma aplicação (PATTERSON & HENNESSY, 2000). O conceito de escalabilidade será apresentado na Subseção 0

No entanto, para a efetiva execução paralela de múltiplas threads é necessário que haja um software apropriado. Um processador multicore que não possui tal software para ser executado tem pouca ou nenhuma utilidade (TANENBAUM, 2001). Assim, para haver uma efetiva paralelização é necessário que o software também seja paralelizado de alguma forma.

No modelo atual de desenvolvimento de software, o projetista utiliza uma abstração de alto nível do hardware no qual o software irá executar, sem se preocupar com detalhes de restrições físicas. Porém, para o modelo paralelo, os aspectos nos níveis mais baixos de abstração devem ser considerados. Novos paradigmas deverão ser buscados em diferentes linhas de pesquisa. Na Engenharia de Software, por exemplo, será necessário buscar abstrações adequadas à necessidade de otimização no nível físico. E é justamente esse o foco principal do presente TCC (Trabalho de Conclusão de Curso). Assim, objetiva-se avaliar técnicas atuais de modelagem de software paralelo que possibilitem sua implementação em um nível adequado de abstração sem descuidar da escalabilidade e de um maior aproveitamento da tecnologia multicore, visando garantir o nível de concorrência2 adequado.

1.1 Problematização

1.1.1

Formulação do Problema

Existem vários fatores que causam a subutilização de sistemas multicore. Aparentemente, o software é o principal obstáculo para o uso difundido de processadores paralelos (PATTERSON & HENNESSY, 2000). Essa constatação deve-se aos problemas inerentes à programação paralela relacionadas à comunicação e sincronização de processos. Como a arquitetura dos processadores multicore caracteriza-se pelo compartilhamento de recursos a programação paralela para esses ambientes requer algum mecanismo de coordenação. O exemplo da Figura 4 demonstra os

(15)

problemas que podem ocorrer com o paralelismo. Para essa abordagem, devem-se desconsiderar momentaneamente os aspectos relacionados à paralelização automática (ou implícita), às políticas transacionais ou mecanismos de controle intrínsecos a uma determinada tecnologia. A intenção nesse exemplo é demonstrar os problemas que o paralelismo pode causar quando não há qualquer tipo de controle entre os processos paralelos de uma aplicação.

O exemplo da Figura 4 contém um método em Java denominado saque. Esse método é responsável por deferir o saque baseado no saldo disponível e na quantia a ser sacada. Se houver saldo, o saque é autorizado. Caso contrário, o saque é indeferido.

Figura 4. Método Java compartilhado por dois processos paralelos.

Considera-se, para o exemplo, um saldo inicial de R$1.000,00 e dois processos executando em paralelo, denominados processos A e B. Ao iniciar a execução da aplicação, o seguinte cenário pode ocorrer:

• Processo A realiza uma chamada ao método saque, passando como parâmetro o valor 700, representando a quantia a ser sacada;

• Simultaneamente, o processo B realiza uma outra chamada ao método saque passando como parâmetro o valor 500, representando a quantia a ser sacada;

• O processo A verifica na linha 10 que saldo é maior que quantia e continua sua execução;

(16)

• Por se tratar de tarefas paralelas, é possível que o processo B verifique a condição da linha 10 momentos antes do processo A efetuar a operação de subtração da linha 11. Como ainda não foi efetuada a operação de subtração, a aplicação interpreta como verdadeira a condição e permite que o processo B continue sua execução. Nesse ponto, há um erro de controle;

• O processo A efetua a subtração. Agora, saldo possui R$300,00; e

• O processo B também efetua a subtração. Agora, saldo possui R$200,00 negativos. Nesse ponto o erro se consolidou.

Esse tipo de problema, natural ao paralelismo, admite a interferência entre processos paralelos que acessem recursos compartilhados, podendo causar a inconsistência de dados. Portanto, os problemas inerentes ao paralelismo devem ser tratados.

1.1.2

Solução Proposta

Há duas abordagens para o paralelismo: implícito e explícito. Ambas, à sua maneira, dispõe de mecanismos que visam resolver o problema citado na Subseção 1.1.1 . O paralelismo implícito abstrai os problemas inerentes à programação paralela, ou seja, toda a paralelização ocorre de forma automática e transparente para o desenvolvedor de software. É possível que a responsabilidade sobre tal paralelização fique a cargo de um compilador que receba como entrada um programa seqüencial escrito em uma predeterminada linguagem e produza um programa paralelo eficiente. O grande problema nessa abordagem resume-se no grau de dificuldade em construir tal compilador (GRUNE et al., 2001).

A abordagem alternativa ao paralelismo implícito é o paralelismo explícito. Nesse caso, a paralelização do software – e os problemas inerentes à programação paralela - fica a cargo do desenvolvedor. Por esse motivo, geralmente, essa abordagem é menos desejada que a anterior (paralelismo implícito). É possível tratar os problemas inerentes à programação paralela, como aqueles relacionados à sincronização entre processos, por meio de primitivas de sincronização. Há, inclusive, mecanismos que se propõe a facilitar a programação paralela abstraindo a própria sincronização entre os processos por meio de memórias transacionais como o STM (Software Transactional Memory) ou HTM (Hardware Transactional Memory) (ADL-TABATAI, KOZYRAKIS & SAHA, 2006). De qualquer forma, como a responsabilidade é do desenvolvedor de software decidir parcialmente ou totalmente as questões referentes ao paralelismo, a fase de

(17)

modelagem do software deve contemplar os aspectos paralelos. Existem técnicas de modelagem com suporte ao paralelismo em um nível relativamente alto de abstração. Um exemplo é a UML, com a qual é possível modelar processos paralelos abstraindo parcialmente os aspectos dos níveis mais baixos da arquitetura de computadores.

O presente TCC se propôs a avaliar técnicas atuais de Engenharia de Software por meio da identificação e aplicação dessas técnicas para a modelagem de softwares paralelos. Especificamente, foi modelada uma aplicação onde a adoção de processadores multicore e a conseqüente paralelização do software é justificada pela necessidade de melhores índices de desempenho. A aplicação em questão visa tratar os aspectos paralelos que podem ser aplicados sobre o problema do caixeiro-viajante. A modelagem considerou tratar os problemas inerentes à programação paralela sem descuidar dos requisitos da aplicação. Para a avaliação propriamente dita, foram adotadas técnicas presentes na Engenharia de software capazes de mensurar aproximadamente o esforço e tempo despendidos no processo de modelagem do estudo de caso.

A proposta desse trabalho visa contribuir com o desenvolvimento de software paralelo por meio da compilação de técnicas constantes na Engenharia de Software que contemplem o paralelismo. Porém, é válido ressaltar que o presente TCC não se propõe a desenvolver uma nova metodologia para modelagem de softwares paralelos tampouco qualificar o desempenho entre programas seqüenciais e paralelos.

Multicomputadores, clusters e compiladores de paralelização automática estão fora do escopo desse trabalho. O presente TCC restringe-se ao processamento paralelo baseado na tecnologia multicore.

1.2 Objetivos

1.2.1

Objetivo Geral

Avaliar técnicas atuais de Engenharia de Software para a modelagem de programas paralelos em ambientes multicore.

1.2.2

Objetivos Específicos

1. Identificar técnicas atuais da Engenharia de Software que contemplem a concorrência; 2. Analisar as técnicas identificadas no item 1 a fim de selecionar aquelas que atendam aos

(18)

3. Aplicar as técnicas selecionadas no item 2 visando implementar os aspectos paralelos de uma aplicação; e

4. Avaliar os resultados obtidos baseando-se em métricas estruturais orientada a objetos.

1.3 Metodologia

1.3.1

Caracterização da metodologia

O presente trabalho é de natureza aplicada, pois a proposta consiste em avaliar as técnicas de Engenharia de Software para a modelagem de programas paralelos em ambientes multicore pela identificação, análise e aplicação daquelas técnicas em um software específico. A abordagem será qualitativa, uma vez que o trabalho culmina em uma avaliação sem utilizar técnicas estatísticas. Do ponto de vista dos objetivos, a pesquisa será basicamente exploratória, pois a avaliação envolvida possibilita a construção de hipóteses. Vinculado aos objetivos, verifica-se que os procedimentos técnicos serão baseados principalmente em pesquisa bibliográfica, consistindo inclusive de material obtido na internet.

1.3.2

Metodologia de desenvolvimento

O TCC é dividido em 3 fases: fundamentação teórica, modelagem, implementação e avaliação. O TCC I contempla totalmente a fase de fundamentação teórica e parcialmente a fase de modelagem e implementação, deixando a cargo do TCC II o restante. Assim, neste trabalho houve a análise da arquitetura multicore, dos modelos de programação paralela e das técnicas para modelagem de softwares paralelos por meio de pesquisa bibliográfica. Também foi determinado um estudo de caso onde adoção de processadores multicore e a conseqüente paralelização do software seja justificada pela necessidade de melhores índices de desempenho.

1.4 Estrutura do trabalho

Este documento está estruturado em quatro capítulos. O Capítulo 1, Introdução, apresenta um breve histórico sobre os motivos que culminaram no desenvolvimento de processadores multicore. Foram abordados os impactos provocados por tais processadores no desenvolvimento de software paralelo. No Capítulo 2, Fundamentação Teórica, é apresentada a arquitetura dos processadores multicore, as vantagens que esses processadores proporcionam e um comparativo

(19)

entre o paralelismo implícito e o paralelismo explícito. Serão apresentados ainda os impactos que a tecnologia multicore e a programação paralela provocam no desenvolvimento de software e as técnicas de Engenharia de Software para a modelagem de software paralelo. O Capítulo 3 apresenta o estudo de caso a ser modelado segundo as técnicas de modelagem para software paralelo identificadas no decorrer desse trabalho. Concluindo, o Capítulo 4 apresenta as considerações finais a respeito da avaliação das técnicas de Engenharia de Software para a modelagem de softwares paralelos em ambientes multicore.

(20)

2. FUNDAMENTAÇÃO TEÓRICA

Caso as previsões dos fabricantes de processadores do mercado tornem-se realidade, os processadores multicore provavelmente serão a plataforma comum para servidores, dispositivos móveis e desktops. Assim, os processadores multicore se tornarão o padrão industrial e não a exceção (SHIEL, 2006). Conforme ilustrado na Figura 5. Planos, os planos da fabricante de processadores Intel consistem em aumentar gradativamente a produção de seus processadores multicore até o ponto em que esses se tornem a maioria disponível no mercado. Essa tendência pode provocar uma demanda por softwares paralelos além deinvestimentos em processos e metodologias para desenvolvimento.

Figura 5. Planos da Intel para produção de processadores multicore Fonte: Intel (2005)

A opção por desenvolver software paralelo depende das necessidades específicas de cada aplicação. Se o desempenho for elemento crítico, despender esforços para desenvolver software paralelo pode ser uma solução viável ao considerar os possíveis ganhos que o desempenho venha

(21)

agregar à aplicação. Sistemas de tempo real além das aplicações que demandam complexas simulações 3D, bancos de dados maiores, interfaces de usuário mais sofisticadas e níveis adicionais de segurança são exemplos onde o desempenho é crítico e requerem maior poder de processamento (CARDOSO, ROSA & FERNANDES, 2006). Para esses, os ganhos conseguidos por meio dos sistemas multicore e a conseqüente paralelização do software podem ser significativos. Assim, a adoção do hardware paralelo está diretamente ligada à necessidade em prover mais recursos computacionais de forma sustentável para as aplicações. Segundo Shiel (2006) há dois motivos relacionados à adoção de hardware capaz de executar software em paralelo:

• pela necessidade de uma aplicação executar mais rapidamente determinado conjunto de dados: múltiplos processadores permitem a execução de uma mesma tarefa por vários processadores simultaneamente. Esse é o conceito generalizado de processamento paralelo. O ganho de desempenho depende da organização dos processadores, da linguagem de programação utilizada e do grau de paralelismo possível na aplicação (MACHADO & MAIA, 2002). O processamento paralelo e os modelos associados são abordados na Seção 0.

• pela necessidade de uma aplicação suportar mais usuários finais ou maior conjunto de dados: múltiplos processadores permitem a execução simultânea de diversas tarefas independentes, aumentando o throughput3 do sistema. Servidores de banco de dados e servidores web são exemplos desses ambientes, onde o aumento no número de processadores permite atender número maior de usuários simultaneamente (MACHADO & MAIA, 2002)

Para as duas abordagens, há duas possíveis soluções: (i) incrementar o poder de processamento dos recursos que compõe um sistema e/ou (ii) adicionar mais recursos ao sistema. A primeira solução consiste em redimensionar um sistema verticalmente, como por exemplo, substituindo um

determinado processador de 1Ghz por outro com pinagem compatível de 2Ghz. A segunda solução consiste em redimensionar o sistema horizontalmente adicionando uma nova unidade de

(22)

2.1 Vantagens da arquitetura multicore

Machado e Maia (2002) descrevem vários benefícios provenientes dos sistemas com múltiplos processadores.

• desempenho: a princípio, sempre que novos processadores são adicionados à arquitetura de uma máquina, melhor é o desempenho do sistema. No entanto, o ganho real de desempenho não é linear ao número de processadores adicionados. Essa questão é abordada com mais detalhes na Seção 0;

• escalabilidade: é a proporção entre o aumento de desempenho de um sistema com o acréscimo de hardware e a capacidade acrescida. Ou seja, um sistema ao qual se pode adicionar mais processadores e aumentar de modo correspondente a sua potência computacional é dito escalável (TANENBAUM, 2001). Ao ampliar a capacidade de computação apenas adicionando-se novos processadores é possível reduzir os custos em relação à aquisição de um outro sistema com maior desempenho;

• relação custo/desempenho: sistemas com múltiplos processadores permitem utilizar CPUs convencionais de baixo custo, interligadas às unidades funcionais por meio de mecanismos de interconexão. Dessa forma, é possível oferecer sistemas de alto desempenho com custo aceitável;

• tolerância a falhas e disponibilidade: a tolerância a falhas é a capacidade de manter o sistema em operação mesmo em casos de falhas de algum componente. Nessa situação, mesmo se um dos processadores falhar, os demais podem assumir suas funções de maneira transparente aos usuários e suas aplicações, embora com menos capacidade computacional. A disponibilidade é medida em número de minutos por ano que o sistema permanece em funcionamento de forma ininterrupta, incluindo possíveis falhas de hardware ou software, manutenções preventivas e corretivas; e

• balanceamento de carga: é a distribuição do processamento entre os diversos componentes da configuração do sistema, mantendo todos os processadores ocupados o maior tempo possível. As tarefas são movidas dos processadores sobrecarregados para

(23)

processadores com menor carga de processamento. Servidores de banco de dados que oferecem esse tipo de facilidade, permitem que as solicitações dos diversos usuários sejam distribuídas entre os vários processadores disponíveis.

Apesar das vantagens, os sistemas com múltiplos processadores também apresentam, dadas suas características, desvantagens. As principais são aquelas relacionadas aos problemas de comunicação e sincronização entre os processadores que estejam acessando as mesmas posições de memória. Nesse ponto, há um fator crítico a ser considerado: as soluções para os problemas causados pela concorrência podem ser abordados de forma implícita ou explícita. A Subseção 0 aborda com mais detalhes essa questão.

2.1.1 Paralelismo implícito versus paralelismo explícito

A programação paralela geralmente consiste em dividir o problema a ser processado em tarefas, alocando e sincronizando essas tarefas nos processadores. Há duas abordagens para essa atividade:

• paralelismo implícito, onde o sistema (compiladores de paralelização automática ou outro programa capaz de realizar algum tipo de paralelização automática) particiona o programa e aloca tarefas aos processadores automaticamente; e

• paralelismo explícito, onde o particionamento do programa em tarefas e conseqüente alocação de recursos fica sob o controle do programador.

Paralelismo implícito

Para Grune et al. (2001), o ápice da paralelização seria a construção de um compilador que receba como entrada um programa seqüencial escrito em uma predeterminada linguagem e produza um programa paralelo eficiente. O multiprocessamento seria transparente aos analistas, ficando sob sua responsabilidade apenas inserir no projeto os requisitos necessários ao processamento paralelo. O problema nessa abordagem é saber se esse compilador pode ser implementado. Segundo Grune et al. (2001), ainda não existe compilador desse tipo e é difícil afirmar se alguém conseguirá criá-lo, apesar de haver um progresso substancial com a paralelização automática.

Há ainda a possibilidade de delegar ao sistema operacional funções de paralelização automática. No entanto, uma tendência recente é mover funcionalidades do sistema operacional para o ambiente de tempo de execução. As vantagens para essa abordagem constituem-se em

(24)

poupar interações dispendiosas com o sistema operacional e delegar ao desenvolvedor do compilador um controle maior sobre a implementação das funcionalidades. Percebe-se aqui que, geralmente, o limite entre o ambiente de tempo de execução e o sistema operacional é vago. Assim, há de se analisar cuidadosamente onde exatamente ocorrerá o paralelismo nessa abordagem.

Tanenbaum (2003) cita que há várias organizações possíveis ao abordar sistemas operacionais para multiprocessadores. Atualmente, o modelo SMP (Symmetric Multiprocessor – Multiprocessador Simétrico) é amplamente utilizado pelos multiprocessadores modernos. Nesse modelo, existe uma cópia do sistema operacional na memória sendo que qualquer CPU pode executá-la. Assim, quando uma chamada ao sistema é efetuada, a CPU local chamada é desviada para o núcleo e a processa. No entanto, mesmo nesse modelo, há problemas críticos relacionados ao desempenho, à gerência de regiões críticas e à sincronização dos núcleos de processamento persistindo, portanto, as dúvidas sobre a viabilidade em delegar completamente o paralelismo ao sistema operacional.

Paralelismo explícito

Segundo Patterson e Hennessy (2003), o paralelismo pode ocorrer no nível de thread. Ao considerar essa afirmação, conclui-se que o papel dos analistas e desenvolvedores de software fica mais evidente na concepção de sistemas que possuam o paralelismo como requisito, uma vez que, geralmente, é delegado a esses a responsabilidade da gerência das threads. O grande problema nessa abordagem é que a programação paralela não é trivial. Patterson e Hennessy (2000) citam alguns motivos pelos quais desenvolver programas para processamento paralelo são muito mais difíceis do que os programas seqüenciais. Entre eles está a obrigatoriedade em obter um bom desempenho e alta eficiência dos programas que executam em um sistema multiprocessador, pois caso contrário, é melhor utilizar um sistema monoprocessado, cuja programação é muito mais simples. Há, no entanto, alguns avanços na área de programação paralela. Por exemplo, a fabricante de processadores Intel publicou pesquisas que visam facilitar a programação paralela em arquitetura multicore e a fabricante de processadores AMD criou um mecanismo denominado LWP (Light-Weight Profiling) que permite ao software executar melhor suas funções nas plataformas multicore.

Mesmo com esses avanços, ainda assim é difícil construir abstrações de modo que funcionem de maneira segura na presença de vários fluxos de controle (BOOCH, RUMBAUGH & JACOBSON, 2006). Pode haver um excesso de engenharia em sua visão de processo (muitos fluxos concorrentes e o sistema acaba falhando) ou uma engenharia escassa (uma concorrência insuficiente não otimiza o tempo de resposta do sistema). Outro fator está relacionado ao desenvolvimento do

(25)

software propriamente dito. Assim, identificar na aplicação a ser desenvolvida frações passíveis de paralelismo, além de gerenciar a comunicação e a sincronização entre os processos, são atividades candidatas à incorporação na análise e projeto de sistemas. Os analistas e desenvolvedores que estiverem inseridos nesse contexto podem se deparar com a necessidade de formalizar novos processos de desenvolvimento a serem integrados a Engenharia de Software.

Um desafio é definir um nível adequado de abstração de forma que seja possível aos projetistas e programadores desenvolverem software que atenda aos requisitos do processamento paralelo sem comprometer significativamente a produtividade. A Seção 0 aborda a modelagem de sistemas com mais detalhes.

2.2 A arquitetura multicore

O que torna o desenvolvimento de programas para processamento paralelo mais difícil do que os programas seqüenciais é que o programador precisa conhecer o funcionamento do hardware. Em um sistema monoprocessado, o programador da linguagem de alto nível abstrai questões sobre a organização do hardware. Essa responsabilidade é delegada ao compilador. No entanto, em máquinas paralelas é fundamental o conhecimento do hardware para que seja possível desenvolver programas aptos a executar em máquinas com vários processadores.

2.2.1 Taxonomia de computadores paralelos

Flynn (1972 apud TANENBAUM, 2001) criou uma taxonomia para classificar os sistemas de computadores em geral. Essa taxonomia baseia-se em dois conceitos: (i) seqüência de instruções e (ii) seqüência de dados. Tanenbaum (2001) cita que para cada seqüência de instruções há um registrador program counter (PC). Assim, um sistema com n processadores tem n registradores program counter e, por conseqüência, n seqüências de instruções. A seqüência de dados é formada por um conjunto de operandos. As seqüências de instruções e de dados são, de certa forma, independentes, acarretando em quatro combinações possíveis dessas seqüências, conforme descrito na

Tabela 1. Taxonomia de Flynn.

As máquinas SISD possuem uma única seqüência de instruções e de dados, não admitindo qualquer tipo de paralelismo. Essas são as máquinas clássicas de Von Neumann. Com relação à categoria MISD, devido as suas características naturais (várias instruções operando sobre um conjunto único de dados), não está claro se alguma máquina existente pode ser classificada nessa

(26)

categoria MISD. As máquinas que pertencem à categoria SIMD têm uma única unidade de controle que trata uma única instrução de cada vez, mas possuem várias UALs (Unidades Aritméticas Lógicas) que podem executar essa instrução em vários conjuntos de dados simultaneamente (TANENBAUM, 1999). As máquinas pertencentes à categoria MIMD são aquelas formadas por um conjunto de processadores independentes onde cada processador executa diferentes instruções. A maioria das máquinas paralelas enquadra-se nessa categoria, incluindo os processadores multicore, sendo, portanto a categoria mais relevante para o presente TCC.

Tabela 1. Taxonomia de Flynn Seqüência

De instruções Seqüência de dados Nome Exemplos

1 1 SISD Máquina clássica de Von Neumann

1 Várias SIMD Computador vetorial, processador matricial

Várias 1 MISD Nenhum exemplo

Várias Várias MIMD Multiprocessador, multicomputador Fonte: Tanenbaum (2001)

A taxonomia de Flynn pode ser estendida. Bell (1985 apud MACHADO & MAIA, 2002) propôs uma outra maneira de subdividir as arquiteturas MIMD, considerando apenas o grau de acoplamento da memória principal. Assim, as máquinas pertencentes à categoria MIMD podem ser subdivididas em dois grupos: (i) multiprocessadores e (ii) multicomputadores. Multicomputadores são máquinas que possuem seu próprio espaço de endereçamento individual e a comunicação entre os sistemas é realizada por meio de mecanismos de troca de mensagens (MACHADO & MAIA, 2002). Por esse motivo, os sistemas MIMD inseridos nesse contexto, são classificados como sistemas fracamente acoplados. Os processadores multicore não pertencem a esse grupo e, por esse motivo, não será mais considerado no presente TCC. A ênfase será sobre o primeiro grupo, ou seja, os multiprocessadores.

2.2.2 Multiprocessadores

Multiprocessadores são máquinas com memória compartilhada. A comunicação entre os diversos processadores é realizada por intermédio de variáveis compartilhadas armazenadas nas memórias, sendo que todos os processadores têm acesso a todos os endereços da memória (PATTERSON & HENNESSY, 2000). Assim, os múltiplos processadores acessam toda memória

(27)

disponível como um espaço de endereço global e são controlados por um único sistema operacional. Por esse motivo, os sistemas MIMD inseridos nesse contexto, são classificados como sistemas fortemente acoplados. A maneira como a memória compartilhada é implementada em cada um deles divide os multiprocessadores em dois grupos baseados principalmente no tempo de acesso à memória: (i) UMA (Uniform Memory Access – Acesso Uniforme à Memória), na qual os tempos de acesso para todas as áreas da memória são iguais (uniformes) e (ii) NUMA (NonUniform Memory Access – Acesso Não-Uniforme à Memória), na qual os tempos de acesso para todas as áreas da memória não são iguais (não-uniformes). Os sistemas formados por multiprocessadores de memória compartilhada do tipo UMA também são conhecidos como sistemas SMP (Symmetric Multiprocessors – Sistemas com Multiprocessadores Simétricos). Machado & Maia (2002) citam que tais sistemas (SMP) são assim chamados por implementarem a simetria dos processadores, ou seja, todos os processadores realizam as mesmas funções, em oposição aos sistemas assimétricos, onde somente um processador, denominado mestre, pode executar serviços do sistema operacional. Os demais processadores, denominados escravos, precisam requisitar serviços ao processador mestre. Para Tanenbaum (1999), a denominação SMP deve-se pelo fato de que todos os processadores têm acesso a todos os módulos de memória, sendo tratados de forma idêntica (simétrica) pelo sistema operacional. Por idêntica, entenda-se como todos os processadores realizando as mesmas funções. Apesar de existirem variações do modelo de classificação abordado, o que foi apresentado é comum à maioria dos modelos sugeridos pelos autores referenciados na bibliografia deste TCC.

Comunicação entre processadores

As unidades funcionais (processadores, memória principal e dispositivos de E/S) de um sistema pertencente à categoria MIMD (em especial os sistemas SMP) são interligadas de duas maneiras básicas a fim de possibilitar a comunicação entre os processadores e as demais unidades funcionais: (i) unidades funcionais conectadas por meio de um único barramento e (ii) unidades funcionais conectados por meio de uma rede (PATTERSON & HENNESSY, 2000). Os sistemas SMP são geralmente interligados por meio de um único barramento. O problema dessa organização é que somente uma unidade funcional pode utilizar o barramento em determinado instante. Isso pode produzir um gargalo (vide Figura 6) quando várias unidades tentam acessar o barramento simultaneamente. Outro problema relacionado ao barramento único é que, caso ocorra algum problema no barramento, todo o sistema fica comprometido. Uma forma de reduzir a latência das

(28)

operações de acesso à memória é por meio do uso de caches. Nesse esquema, cada processador possui seu cache individual, para leitura e escrita de dados. No entanto, essa solução também introduz um problema conhecido como coerência de cache (MACHADO & MAIA, 2002). Na medida em que processadores que operam em paralelo precisam compartilhar dados, independentemente se for para leitura ou escrita, há a necessidade de se estabelecer uma coordenação para tornar possíveis as operações sobre dados compartilhados (PATTERSON & HENNESSY, 2000). Essa coordenação é conhecida como sincronização e é abordada na Subseção 0

Machado e Maia (2002) apresentam um exemplo sobre o processo de leitura e escrita de uma variável e os problemas de sincronização relacionados. Consideram-se, para tal, dois processadores: PA e PB.

• O processador PA lê uma variável X na memória principal e copia o conteúdo para sua respectiva cache.

• O processador PA altera o valor de X. Nesse ponto, o valor de X na memória cache é diferente do valor de X na memória principal. Os valores estão, portanto, inconsistentes.

• O processador PB lê a variável X na memória principal e copia o conteúdo para sua respectiva cache. Logo, o processador PB possui em sua cache um valor de X diferente do valor alterado pelo processador PA.

Uma solução para esse problema é conhecida como write-through, e consiste em atualizar a memória principal sempre que um dado na cache for alterado. No entanto, essa solução degrada o desempenho devido aos constantes acessos à memória a cada operação de escrita, provocando tráfego no barramento. Uma alternativa é o esquema write-back que permite alterar o dado na cache sem alterá-lo imediatamente na memória principal. Esse esquema oferece desempenho melhor que o esquema write-through, porém, não resolve o problema de inconsistência. Há métodos de sincronização de processadores por meio da coerência de caches e protocolos que visam manter a coerência dos vários processadores. Esses, por sua vez, são chamados de protocolos para manutenção da coerência de caches.

(29)

Figura 6. Arquitetura multicore de barramento único Fonte: Ma (2006)

Protocolos para manutenção da coerência de caches

Segundo Patterson e Hennessy (2000), o mais popular protocolo para manutenção da cache é conhecido como monitoramento (snooping). Nesse protocolo, todos os controladores das caches monitoram o barramento para determinar se elas têm ou não uma cópia do bloco compartilhado, ou seja, cada cache verifica se o endereço do dado que trafega no barramento está localmente armazenado. Caso esse dado seja alterado por qualquer processador a técnica de snooping deve garantir que os demais obtenham a cópia mais recente. Os protocolos de monitoramento são de dois tipos:

• write-invalidate (invalidação na escrita): o processador, ao realizar uma operação de escrita e antes de modificar sua própria cópia local, envia um sinal de invalidação pelo barramento visando invalidar todas as cópias, armazenadas em outras caches, do dado a ser escrito (PATTERSON & HENNESSY, 2000). Assim, apenas o processador que realizou a operação de escrita possuirá uma cópia válida do dado na cache. Os demais processadores, ao acessarem novamente o dado em suas respectivas caches, terão uma cópia inválida e o dado deverá ser transferido da memória principal para suas caches (cache miss) (MACHADO & MAIA, 2002). Há nesse esquema muitos leitores mas apenas um escritor. A Tabela 2 exemplifica o protocolo write-invalidate.

(30)

Tabela 2. Protocolo write-invalidate

Processador Barramento Cache do PA Cache do PB Memória

PA lê X Cache miss do PA 0 - 0

PB lê X Cache miss do PB 0 0 0

PA escreve 1 em X Invalidação de X 1 - 1

PB lê X Cachê miss de PB 1 1 1

Fonte: Machado e Maia (2002)

• write-update (atualização na escrita): esse esquema, também conhecido como escrita em broadcast, ao invés de invalidar cada bloco compartilhado, o processador escritor envia o novo dado em broadcast pelo barramento. Assim, todas as cópias são atualizadas com o novo valor (PATTERSON & HENNESSY, 2000). O desempenho dessa técnica é inferior a técnica de write-invalidate, pois além do endereço do dado a ser atualizado também deve informar, no barramento, seu novo valor. Por isso, a técnica de write-invalidate é empregada na maioria dos sistemas SMP (MACHADO & MAIA, 2002). A Tabela 3 exemplifica o protocolo write-update.

Tabela 3. Protocolo write-update

Processador Barramento Cache do PA Cache do PB Memória

PA lê X Cache miss do PA 0 - 0

PB lê X Cache miss do PB 0 0 0

PA escreve 1 em X Atualização de X 1 1 1

PB lê X Cachê miss de PB 1 1 1

Fonte: Machado e Maia (2002)

A maioria dos sistemas comerciais com múltiplos processadores usam caches write-back com o protocolo write-invalidate, a fim de reduzir o tráfego no barramento e permitir que um número maior de processadores possa ser colocado em torno desse barramento.

Segundo Patterson e Hennessy (2000), medidas recentes mostram que dados compartilhados têm localidade espacial e temporal mais baixa que os demais tipos de dados. Portanto, as faltas no

(31)

acesso dominam o padrão de acesso aos dados compartilhados armazenados na cache, mesmo considerando que eles representam de 10% a 40% do total de acessos a dados.

Muitas das soluções apresentadas enfrentam um problema em comum: o gargalo provocado pelo barramento único. Para evitar esse gargalo, a memória principal pode ser dividida em módulos para permitir múltiplos acessos simultâneos, podendo ser compartilhada entre as várias unidades funcionais. As unidades funcionais podem ser conectadas entre si por meio de um barramento cruzado comutado (crossbar switch), criando uma matriz de interconexão (MACHADO & MAIA, 2002). Segundo Tanenbaum (2003), uma das características mais relevantes de um barramento cruzado é que ele é uma rede não-bloqueante, ou seja, a conexão solicitada por um processador nunca é negada caso algum ponto de cruzamento ou linha já esteja ocupado. Nesse esquema, é possível a comunicação simultânea entre diferentes unidades funcionais, ficando a cargo do hardware e do sistema operacional a resolução dos possíveis conflitos de acesso a uma mesma unidade. O problema em uma arquitetura de barramento cruzado, é que para cada n processadores e n módulos de memória são necessários n² comutadores para interligar todos os pontos. Em uma configuração onde n seja muito grande o custo tende a ser, conseqüentemente, muito alto (MACHADO & MAIA, 2002).

2.3 Desempenho de computadores multiprocessados

A maneira mais simples de se melhorar o desempenho de um sistema é aumentando o número de processadores disponíveis (TANENBAUM, 2001). Se a relação fosse de 1:1, ou seja, se o uso de n processadores acarretasse em n vezes a execução mais rápida de um programa, haveria um ganho perfeito de desempenho No entanto, o desempenho não melhora linearmente apenas adicionando mais processadores ou mais núcleos. Assim, adicionar n núcleos de execução dentro de um chip não significa que o desempenho de um programa executando nesses núcleos será n vezes maior. Essa virtual impossibilidade de se obter o ganho perfeito deve-se, em parte, ao fato de que todos os programas têm componentes seqüenciais. Esses componentes ocorrem, na maioria das vezes, na fase de inicialização, leitura de dados ou obtenção de resultados desse mesmo programa (TANENBAUM, 2001). A Figura 7. Componente seqüencial e seções paralelizáveis ilustra o componente seqüencial e as seções paralelizáveis de um programa qualquer. De acordo com a Figura 5, F é a fração de tempo executando código eminentemente seqüencial, (1 – F) a fração de tempo executando código potencialmente paralelizável e N é o número de processadores (ou núcleos) adicionados ao sistema.

(32)

Figura 7. Componente seqüencial e seções paralelizáveis Fonte: Adaptado de Charão (2006)

A impossibilidade de se chegar ao ganho perfeito em razão do componente seqüencial é contemplada pela lei de Amdahl. Essa lei determina o ganho real através da utilização de vários processadores paralelos em relação ao uso de apenas um único processador. O ganho real pode ser obtido através da Equação 1:

Equação 1

Assim, caso não existisse o componente seqüencial em dado programa, então F seria igual a 0 (zero). Após algumas simplificações, constatar-se-ia que o ganho seria diretamente proporcional ao número de processadores (ou núcleos) adicionados ao sistema. No entanto, como o componente seqüencial é imperativo (pelo menos atualmente) nos programas, tem-se que F > 0 e, portanto, o ganho linear não é possível. A Figura 8 apresenta esse comportamento.Portanto, um desafio para a programação paralela consiste em diminuir o valor de F o máximo possível.

Segundo Tanenbaum (1999), a lei de Amdahl não é o único motivo responsável pela quase impossibilidade de se obter o ganho perfeito. As latências na comunicação, as bandas passantes finitas e as ineficiências algorítmicas também são fatores que cooperam com a ocorrência dessa

(33)

impossibilidade. No caso específico dos algoritmos, convém observar que, muitas vezes, o melhor algoritmo não pode ser facilmente paralelizado e, portanto, um algoritmo subótimo será usado para a execução em paralelo.

Figura 8. Ganho perfeito em relação ao ganho real de desempenho de sistema paralelos Fonte: adaptado de Amdahl (1967)

2.4 Modelos de programação paralela

Considerando as dificuldades em realizar a paralelização automática, deve-se considerar a escolha por um modelo de programação apropriado que contemple positivamente a execução de tarefas em paralelo. Um modelo de programação que se propõe a executar tarefas em paralelo, é geralmente avaliado pela sua capacidade de expressão e simplicidade, visando aumentar a produtividade de programação.

Um modelo de programação paralela consiste em uma arquitetura computacional que forneça recursos capazes de executar código em paralelo e um software projetado para expressar o paralelismo. Esse software pode ser um compilador, bibliotecas ou outro recurso que habilita a aplicação a usar hardware paralelo. Modelos paralelos são implementados de várias formas: como bibliotecas invocadas por linguagens seqüenciais, como extensão de linguagens que forneçam parcialmente algum recurso passível de paralelização ou como uma linguagem completamente nova capaz de efetuar todo tipo de processamento paralelo. Esses modelos são válidos tanto para os sistemas de memória compartilhada como os de memória distribuída.

(34)

Tanenbaum (2001) classifica a produção de software para máquinas paralelas em quatro metodologias: (i) acrescentando bibliotecas especiais para o tratamento de números às linguagens de programação seqüencial; (ii) incorporando ao ambiente de execução bibliotecas especiais que contêm primitivas de comunicação e controle; (iii) acrescentando à linguagem de programação estruturas especiais capazes de efetuar algum tipo de processamento paralelo como, por exemplo, a possibilidade de criar com facilidade novos processos paralelos ou executar as iterações de um loop em paralelo e (iv) por meio do desenvolvimento de linguagens de programação novas, voltadas para a programação paralela. Sobre essas metodologias é que se deve embasar a escolha por um modelo de programação apropriado.

2.4.1 Estrutura básica de software paralelo

Todas as quatro metodologias apresentadas na Seção 0 possuem vantagens e desvantagens. A escolha deve ser baseada em uma análise criteriosa sobre cinco aspectos que formam a estrutura básica dos softwares para computadores paralelos (TANENBAUM, 2001): (i) modelos de controle; (ii) granularidade do paralelismo; (iii) paradigmas computacionais; (iv) métodos de comunicação e (v) primitivas de sincronização.

Modelos de controle

Há dois modelos básicos de controle que se referem ao suporte a uma ou várias linhas de controle, também conhecidas como threads. Para o primeiro caso, o modelo com suporte a apenas uma linha de controle, há um único programa e um único PC, porém há vários conjuntos de dados. Na medida em que cada instrução é emitida, ela é executada simultaneamente sobre todos os conjuntos de dados, por diferentes elementos de processamento. Para o segundo caso, ou seja, o modelo com suporte para várias threads, cada uma delas tem seu próprio PC, seus registradores e suas variáveis locais. Cada thread executa seu próprio programa, com seus próprios dados, possivelmente se comunicando e sincronizando com outras threads em determinados momentos. As variações em torno dessa idéia básica formam o modelo dominante para o processamento paralelo (TANENBAUM, 2001).

(35)

Threads

Para que seja possível paralelizar o problema, é necessário que de alguma forma seja possível explicitar o paralelismo. Há estruturas denominadas threads que possibilitam a execução em paralelo de vários fluxos de controle. Threads são úteis em sistemas com múltiplos processadores, para os quais o paralelismo real é possível (TANNENBAUM, 2003). As threads podem ser de usuário ou de kernel (também conhecidas como threads nativas). O mapeamento das threads de usuário para as threads de kernel depende diretamente da implementação de cada sistema operacional. Dependendo do modelo adotado, cada thread criado pelo desenvolvedor será diretamente mapeada para uma thread de kernel. Esse modelo é conhecido como “um-para-um” e é usado por alguns sistemas operacionais como o Windows NT. Esses sistemas operacionais executam threads de acordo com o número de núcleos disponíveis no sistema baseado na arquitetura multicore.

O paralelismo conseguido com as threads depende diretamente do sistema operacional e do modelo que o mesmo adota. O controle pelo escalonamento dos processadores fica a cargo do sistema operacional e do modelo que o mesmo adota (ou seja, não é possível controlar as decisões do agendador). Mesmo que algumas funções como “SetThreadIdealProcessor( )” possibilitem especificar determinado processador para a execução de uma thread, isso não garante que esse processador em questão seja escolhido no processo de escalonamento.

No caso dos processadores multicore, pelo fato dos mesmos serem homogêneos e que qualquer núcleo disponível pode ser utilizado para executar um processo(SILBERSCHATZ, 2004), é possível utilizar uma fila de processos separada para cada processador. No entanto, nesse esquema um processador pode ficar ocioso (devido a sua fila de processos prontos estar vazia) enquanto que no mesmo instante outro (núcleo) esteja sobrecarregado (com sua fila de processos prontos cheia). Para evitar essa situação, utiliza-se uma fila de processos comum aos processadores sendo esses, por sua vez, agendados para qualquer processador disponível. Porém, essa abordagem também possui problemas sendo o mais evidente aqueles relacionados ao compartilhamento dos dados, pois é possível que os processadores do sistema acessem e atualizem uma estrutura de dados comum (SILBERSCHATZ, 2004) em determinado segmento de código conhecido como seção crítica e que possam causar a inconsistência de dados. Para tanto, é necessário que sejam adotados métodos que coordenem e sincronizem o acesso à seção crítica. Monitores são, dentre as soluções possíveis, uma estrutura de sincronização de alto nível. Essa estrutura consiste em (i) declarações de variáveis cujos valores definem o estado de uma instância desse tipo e (ii) corpos de procedimentos ou funções que implementam operações desse tipo (SILBERSCHATZ, 2004). A estrutura do tipo

(36)

monitor garante que somente um processo de cada vez pode estar ativo dentro do monitor. Algumas linguagens de programação (como Java) suportam diretamente o conceito de monitores.

Granularidade do paralelismo

O paralelismo pode ocorrer em vários níveis: do nível mais baixo, como as instruções de máquinas, até os níveis mais altos de abstração, pelo uso de vários processos independentes na solução de um único problema. Há vários níveis contidos dentro do intervalo entre os citados níveis (instruções de máquina e os de vários processos). Entre esses, há aqueles que se utilizam de processos leves como as threads. Nesse caso, o paralelismo é evidenciado quando as threads executam de maneira independente uma das outras, possivelmente em processadores diferentes. Em alguns sistemas, o sistema operacional controla todas as threads e é o principal responsável pelo seu escalonamento. Em outros, cada processo de usuário é responsável por escalonar e gerenciar suas threads, sem que o sistema operacional sequer saiba de sua existência. O paralelismo no nível de threads é amplamente utilizado em algumas linguagens de programação como Java, que permite o uso explícito desses recursos.

Paradigmas computacionais

Os paradigmas computacionais responsabilizam-se pela estrutura de trabalho da maioria dos programas paralelos, em especial aqueles que suportam as threads e processos independentes. O SPMD (Single Program Multiple Data – Único Programa Múltiplos Dados), por exemplo, executa o mesmo programa em conjuntos de dados diferentes. Isso ocorre porque há apenas uma linha de controle e várias unidades de execução. Há ainda outros paradigmas como, por exemplo, o pipeline.

Método de comunicação

Processos executados em paralelo precisam se comunicar. Essa comunicação pode ser feita por variáveis compartilhadas ou por troca explícita de mensagens. Na primeira técnica, todos os processos têm acesso a uma memória comum e se comunicam lendo e escrevendo nessa memória. Em um sistema multiprocessador, as variáveis podem ser compartilhadas entre vários processos mapeando a mesma página no espaço de endereçamento de cada um dos processos. Na segunda técnica, os processos usam na comunicação primitivas como send e receive. Um processo executa uma primitiva send, identificando outro processo como destino. Quando esse último processo executar uma primitiva receive, a mensagem é copiada em seu espaço de endereçamento

(37)

(TANENBAUM, 2001). Essa técnica é comumente utilizada em multicomputadores ao passo que, a primeira, é utilizada em sistemas multiprocessados (multicore). Multicomputadores também podem fazer uso de variáveis compartilhadas, mas essa discussão está fora do escopo do TCC.

Primitivas de sincronização

A comunicação entre processos paralelos geralmente exige a sincronização de suas ações. Por exemplo, quando há o compartilhamento de variáveis logicamente é necessário que haja a garantia que, enquanto um processo estiver escrevendo na estrutura de dados compartilhada, nenhum outro poderá ter acesso a essa estrutura. Para evitar que mais de um processo utilize os mesmos dados compartilhados ao mesmo tempo é necessário que haja uma forma de sincronizar o acesso a esses dados. Assim, a sincronização consiste em permitir a requisição ao uso exclusivo de algum recurso compartilhado pela exclusão mútua entre processos. A exclusão mútua pode ser garantida por meio de várias primitivas de comunicação como semáforos, travamentos, mutexes e regiões críticas (TANENBAUM, 2001). Está fora do escopo deste TCC discutir os conceitos dessas primitivas de comunicação.

As metodologias apresentadas fornecem uma base para iniciar a definição de um modelo que contemple o desenvolvimento de software paralelo. Considerando exclusivamente a arquitetura dos sistemas multicore e o contexto deste trabalho, define-se:

• O modelo de controle adotado será aquele com suporte a várias linhas de controle, ou seja, com suporte a várias threads. Esses sistemas são conhecidos como multithread. Essa escolha se baseia no fato de que as variações em torno dessa idéia formam o modelo dominante para o processamento paralelo;

• A granularidade do paralelismo também contemplará as threads, haja vista que os processadores multicore oferecem suporte ao paralelismo nos níveis mais baixos, como o pipeline;

• O paradigma computacional adotado está fortemente acoplado à decisão dos itens anteriores. Assim, o paradigma computacional adotado será aquele relacionado ao suporte às threads e processos independentes;

• Os processadores multicore possuem memória compartilhada. Assim, o método de comunicação adotado será aquele onde as variáveis podem ser compartilhadas entre

(38)

vários processos mapeando a mesma página no espaço de endereçamento de cada um dos processos; e

• A sincronização para sistemas de memória compartilhada consiste em permitir a requisição ao uso exclusivo de algum recurso compartilhado por meio da exclusão mútua entre processos. A exclusão mútua pode ser garantida com várias primitivas de comunicação.

2.4.2 Linguagens para programação paralela

Uma série de linguagens foi projetada para suportar o paralelismo, como a PL/I, ADA 95 e Java (SEBESTA, 2000). As linguagens de programação estão vinculadas diretamente às abordagens apresentadas na Subseção 0consistindo, portanto, em linguagens com paralelismo implícito e linguagens com paralelismo explícito. LabVIEW, HPF, ZPL e SISAL são exemplos de algumas implementações de linguagens com paralelismo implícito. Java, ADA e PVM são exemplos de linguagens de programação com paralelismo explícito. Cada abordagem possui vantagens e desvantagens. No entanto, um fator comum a essas abordagens e importante para o suporte à concorrência é a sincronização. A sincronização visa garantir que apenas um processo poderá acessar certa variável compartilhada em qualquer instante dado (GRUNE et al., 2001). Nos modelos de memória compartilhada, as principais primitivas de sincronização são os semáforos e monitores. Java e C# são linguagens baseadas no modelo de memória compartilhada com o bloqueio sendo fornecido por monitores.

Além do suporte da sincronização, a concorrência também se caracteriza pelo modelo de controle implementado pelas linguagens de programação. Conforme apresentado na Subseção 0o modelo de controle dominante é aquele formado pelo suporte a várias linhas de controle, também conhecidas como threads. A maior parte dos programas baseados em threads é feita em uma linguagem convencional, como C, que foi estendido para ter uma biblioteca de threads. O pacote C Threads e o padrão POSIX Threads, conhecido como pthreads, são exemplos disso. Há linguagens que oferecem suporte direto para threads, como o Java e ADA 95 (COLOURIS, 2007).

Referências

Documentos relacionados

O facto de ter um campo de vento homogéneo (Figura 5), não influi notoriamente nos resultados simulados, pois a grelha de 0.025 o da região dos Açores foi aninhada com as duas

[r]

Verificou-se que mais de 80% dos casos se referiam a conviventes de doentes com exame bacteriol6gico da expectoray3o positivo por exame directo com colorayao de Ziehl-Neelsen

Observando-se a Figura 6.5.1, nota-se que as amostras de 10B(Sr)+5Al tratadas por 24h e 100h apresentam um resultado melhor que as outras duas.. composições, porém para 800h sua

59 Pedro Augusto Ferraz e Silva Teoria de Representações e Físicas Ronaldo Silva Thibes DCEN Itapetinga 60 Dalton José Santos Sant'Ana. Veículo autônomo não tripulado (VANT) –

Cores Conj Eficiência Pontos.. 1°

- (EA-112) SOCIEDADE AMADORA NACIONAL DE ORNITOLOGIA- A/C JOSÉ AP... SERRANAS DE CRIADORES DE CANÁRIOS

A presença do instrutor no voo, em que pese o comportamento conservativo por parte do aeroclube em relação ao piloto recém-formado, não encontrou respaldo para