Programação paralela no .NET Framework 4 – Parte II

Olá pessoal, tudo bem?

Este post é uma continuação da série iniciada neste outro post, sobre programação paralela.
Meu objetivo hoje é apresentar o PLINQ, algo que poderá ser utilizado imediatamente nos projetos de vocês.

Parallel LINQ (PLINQ)

PLINQ nada mais é que uma implementação de programação paralela ao nosso famoso LINQ, através de métodos de extensão.

O LINQ foi lançado com a versão 3.0 na plataforma .NET, apresentando uma maneira muito mais fácil e segura de manipular coleções IEnumerable ou IEnumerable<T>. O que veremos hoje é a “alteração” do LINQ to Objects, que é direcionado a coleções de objetos em memória.

A principal diferença entre o LINQ to Objects “normal” e o paralelo é que na segunda opção o processamento é realizado tentando utilizar todos os recursos disponíveis para tal, obtendo uma melhora significante de performance.

CUIDADO: Nem todas as operações ficam mais rápidas utilizando recursos de paralelismo. Não deixe de ler a seção “Performance” abaixo.

ParallelEnumerable

Tudo que a gente precisa para este post está organizado na classe ParallelEnumerable. Esta classe contém os métodos que iremos utilizar neste post, e muito mais:

  • AsParallel
  • AsSequential
  • AsOrdered
  • AsUnordered
  • WithCancellation
  • WithDegreeOfParallelism
  • WithMergeOptions
  • WithExecutionMode
  • ForAll

O exemplo mais básico de como executar um código PLINQ é utilizando o métodos AsParallel, como o exemplo:

var source = Enumerable.Range(1, 10000);

var evenNums = from num in source.AsParallel()
               where Compute(num) > 0
               select num;

Algo tão interessante quanto esta facilidade é que o PLINQ não executa sempre de forma paralela. Dependendo da situação e da análise de alguns itens no cenário de execução, talvez seja mais adequado executar o código de forma sequencial – e nativamente o próprio PLINQ faz esta escolha.  É possível forçar a execução para sempre utilizar o paralelismo, caso seja necessário. Utilize o método WithExecutionMode no seu código PLINQ.

Um teste muito simples onde podemos visualizar a diferença é demonstrado abaixo:

static void Main(string[] args)
        {
            IEnumerable<int> numbers = Enumerable.Range(1, 1000);

            IEnumerable<int> results = from n in numbers.AsParallel()
                                       where IsDivisibleByFive(n)
                                       select n;

            Stopwatch sw = Stopwatch.StartNew();
            IList<int> resultsList = results.ToList();
            Console.WriteLine("{0} itens", resultsList.Count());
            sw.Stop();

            Console.WriteLine("Tempo de execução: {0} ms", sw.ElapsedMilliseconds);

            Console.WriteLine("Fim...");
            Console.ReadKey(true);
        }

        static bool IsDivisibleByFive(int i)
        {
            Thread.SpinWait(2000000);

            return i % 5 == 0;
        }

 

Basta remover o AsParallel da instrução LINQ que você terá uma noção prática da diferença de performance.

image

1. Instrução utilizando AsParallel

image 

2. Instrução sem utilizar paralelismo

Performance

Apesar de todos os benefícios, não podemos utilizar PLINQ sem conhecer todos os seus detalhes. Lembre-se de fazer as perguntas básicas:

  1. Eu tenho trabalho suficiente que justifique utilizar paralelismo?
  2. Mesmo com o overhead do PLINQ, vamos ter algum benefício?

Por este motivo, visite este link e conheça todos os aspectos, antes de utilizar os recursos disponíveis.

Conclusão

Utilizar recursos de paralelismo é ótimo, aumenta a performance, utiliza o investimento realizado em hardware – tudo isso sem custo de produtividade. Porém, não podemos usufruir de qualquer tipo de tecnologia sem conhece-la a fundo antes. Portanto, faça bom uso, mas não esqueça de manter o conhecimento a frente da empolgação.

Abraços.

1 Comment

Comments have been disabled for this content.