Programming Strategies for Multicore Processing: Pipelining

Visão geral

This document is part of the
Multicore Programming Fundamentals Whitepaper Series

Multicore Programming Fundamentals Whitepaper Series

Multicore Programming Fundamentals Whitepaper Series

When programming multicore applications, special considerations must be made to harness the power of today's processors. This paper discusses pipelining, which is a technique that can be used to gain a performance increase (on a multicore CPU) when running an inherently serial task.

Contents

Sumário

Atualmente, com processadores multicore e aplicações multithread, os programadores necessitam pensar constantemente sobre qual melhor maneira de aproveitar ao máximo as CPUs quando estão desenvolvendo suas aplicações. Embora estruturar um código em paralelo em uma linguagem tradicional baseada em texto pode ser difícil de se programar e visualizar, ambientes de desenvolvimento gráfico como LabVIEW da National Instruments permite cada vez mais aos engenheiros e cientistas diminuir o tempo de desenvolvimento e executar rapidamente suas idéias.

O fato de o NI LabVIEW ter paralelismo inerente (baseada em fluxo de dados), programar aplicações multithread é tipicamente uma tarefa muito simples. Tarefas independentes do diagrama de blocos executam paralelamente sem nenhum trabalho extra necessário por parte do programador. Mas e as partes do código que não são independentes? Quando implementamos uma programação sequencial inerente, o que fazer para aproveitar o poder dos processadores multicore?

Introdução ao Pipelining

Uma técnica muito utilizada para aumentar o desempenho de tarefas sequenciais de software é o Pipelining. Pode se dizer que Pipelining é o processo que divide tarefas sequenciais em estágios distintos que podem ser executados no modelo de linha estruturada.

Considere o seguinte exemplo: suponha que você está produzindo carros em uma linha de produção automatizada. Sua tarefa final é construir um carro completo, mas você pode separar isso em três estágios distintos: construção da armação, instalação de peças internas (como o motor), e pintura do carro quando terminado.

Levando em conta que a contrução da armação, instalação das peças, e pintura levem uma hora cada parte serem concluídas, então, se você construir apenas um carro de cada vez, cada carro levará três horas para ficar pronto (veja a Figura 1 abaixo).

Figura 1. Nesse exemplo (sem Pipeline), a contrução do carro leva 3 horas para ser concluída.

Como esse processo pode ser melhorado? Se fosse criado uma estação para a construção da armação, outra para a instalação das peças, e uma terceira pra pintura, agora quando um carro estiver sendo pintado, o segundo carro pode estar instalando as peças, e o terceiro carro pode estar na construção da armação.

Como o Pipelining Melhora o Desempenho

Embora cada carro ainda leve três horas para ser finalizado usando o novo processo, nós podemos agora produzir um carro a cada hora, melhor que um a cada três horas -  uma melhoria de 3x no processo de produção do carro. Note que este exemplo foi bem simplificado para propósitos de demosntração; veja a seção de Considerações Importantes abaixo para mais detalhes sobre Pipelining.

Figura 2. Pipelining pode aumentar muito o rendimento da sua aplicação

Pipelining Básico em LabVIEW

O mesmo conceito de pipelining como visto no exemplo do carro, pode ser usado em várias aplicações em LabVIEW, onde você estará executando uma tarefa sequencial. A seguinte ilustração mostra como uma aplicação pipelining pode funcionar em diferentes núcleos (cores):  

Figura 3. Carta dos tempos para aplicação pipelining funcionando em cores diferentes

Considerações Importantes

Quando são criadas aplicações multicore usando pipelining, o programador deve levar em conta diversos parâmetros importantes. Em especial, equillibrar os estágios em  pipelining e minimizar a transferência de memória entre os cores  são fatores importantes para aumentar o desempenho com o pipelining.

Balanceando Estágios

Em ambos os processos citados acima, na produção do carro e em outros exemplos do LabVIEW, cada estágio pipelining assumiu o mesmo montante de tempo de execução; e  podemos dizer que este exemplo de estágios de pipeline estavam equilibrados. Entretando, em aplicações reais isso raramente acontece. Considere o diagrama a seguir: se o Estágio 1 toma três vezes o tempo do Estágio 2, então os dois estágios produzem um aumento pequeno de desempenho.

Sem Pipeline (tempo total = 4s):

Com Pipeline (tempo total = 3s):

 

Obs: Aumento de desempenho = 1.33X (caso não ideal para pipelining)

Para melhorar essa situação, o programador deve mover as tarefas para o Estágio 1 e o Estágio 2 até que ambos os estágios demorem aproximadamente o mesmo tempo de execução. Com um grande números de estágios no pipeline, essa pode ser uma tarefa difícil.

No LabVIEW, é útil testar cada estágio do pipeline e assegurar que o pipeline está bem balanceado. Isso pode facilmente ser feito usando uma estrutura de sequência em conjunto com a função Tick Count (ms) mostrado na Figura 4.

Figura 4. Testando se os estágios do pipeline estão bem balanceados.

Tranferência de Dados Entre os Cores

É bom evitar a transferência de grandes quantidades de dados entre os estágios pipeline sempre que possível. Uma vez que os estágios do pipeline podem rodar em processadores separados, qualquer transferência de dados entre estágios individuais pode resultar na transferência de memória entre os processadores. No caso de dois processadores não dividirem o cache (ou de a transferência de memória exceder o tamanho do cache), no fim da aplicação o usuário pode ver como diminui a utilidade do pipelining.

Conclusão

Resumindo, pipelining é uma técnica que os programadores podem usar para aumentar o desempenho de uma aplicação sequencial inerente (em máquinas multicore). A indústria de CPUs tende a aumentar a quantidade de cores nos mesmos, isso significa que as estratégias, como o pipelining, se tornarão essenciais no desenvolvimento de aplicações no futuro.

Para obter o melhor desempenho de ganho do pipelining, os estágios individuais devem ser cuidadosamente equilibrados de modo que nenhum estágio demore mais tempo que o outro para finalizar. Assim, toda a transferência de dados entre os estágios de pipeline deve ser diminuída para evitar a perda de desempenho devido ao acesso na memória dos multiplos cores.

Was this information helpful?

Yes

No