C# AsParalelo

Resumo : neste tutorial, você aprenderá como usar o AsParallel()método C# para executar consultas LINQ em paralelo.

Introdução ao C# Paralelo LINQ

Quando você tem um grande conjunto de dados, leva tempo para processá-los. Para acelerar o cálculo, você pode dividir o grande conjunto de dados em partes menores e processá-los simultaneamente.

Para fazer isso, você usa o LINQ paralelo ou PLINQ, resumidamente. PLINQ permite executar consultas LINQ em paralelo em vários núcleos de CPU.

O PLINQ divide automaticamente os dados grandes em pedaços menores, distribui-os por vários núcleos de CPU e agrega o resultado novamente em um único conjunto de resultados.

PLINQ é útil para operações vinculadas à CPU que exigem processamento de dados em grande escala ou cálculos complexos. Mas pode não ser adequado para operações vinculadas a E/S, como leitura de arquivos ou acesso a dados via API.

Como o PLINQ executa as consultas em paralelo, você precisa garantir que seu código seja thread-safe e não introduza condições de corrida.

Criando uma consulta paralela usando o método C# AsParallel()

Para criar uma consulta paralela, siga estas etapas:

Primeiro, comece com uma consulta LINQ padrão que opera em uma fonte de dados IEnumrable<T>ou IQueryable<T>:

IEnumerable<T> source = ...Linguagem de código:  C#  ( cs )

Segundo, chame o AsParallel()método de extensão na fonte de dados para criar uma consulta paralela:

ParallelQuery<T> query = source.AsParallel();Linguagem de código:  C#  ( cs )

O AsParallel()método retorna uma consulta paralela.

Terceiro, chame um operador para executar a consulta e obter o resultado. Por exemplo:

var result = query.Sum()Linguagem de código:  C#  ( cs )

O programa a seguir demonstra como criar uma consulta paralela simples que retorna a soma de uma sequência de inteiros:

using static System.Console;
using System.Diagnostics;

static void Measure(Func<int> f, string name)
{
    var stopwatch = Stopwatch.StartNew();
    stopwatch.Start();

    f();

    stopwatch.Stop();
    WriteLine($"The method {name} took {stopwatch.ElapsedMilliseconds} ms to run");
}

static int SequentialSum()
{
    return Enumerable.Range(0, 101)
        .Select(x => {
            Thread.Sleep(10);
            return x;
        })
        .Sum();
}

static int ParallelSum()
{
    return Enumerable.Range(0, 101).AsParallel()
        .Select(x => {
            Thread.Sleep(10);
            return x;
         })
        .Sum();
}

Measure(SequentialSum, "SequentialSum");
Measure(ParallelSum, "ParallelSum");Linguagem de código:  C#  ( cs )

Como funciona.

Primeiro, defina o Measuremétodo que recebe dois parâmetros: o parâmetro fdo tipo Func<int>e o nome do parâmetro do tipo string.

O Func<int>é um tipo delegado que representa uma função que não aceita argumentos e retorna um valor inteiro. O nameparâmetro representa o nome do método.

O Measure()método mede o tempo de execução de uma função fusando o StopWatchobjeto:

static void Measure(Func<int> f, string name)
{
    var stopwatch = Stopwatch.StartNew();
    stopwatch.Start();

    f();

    stopwatch.Stop();

    WriteLine($"The method {name} took {stopwatch.ElapsedMilliseconds} ms to run");
}Linguagem de código:  C#  ( cs )

Segundo, defina o SequentialSum()método que calcula a soma dos números sequencialmente de 0 a 10001:

static int SequentialSum()
{
    return Enumerable.Range(0, 101)
        .Select(x => {
            Thread.Sleep(10);
            return x;
        })
        .Sum();
}Linguagem de código:  C#  ( cs )

Observe que usamos o Thread.Sleep(10)para simular operações demoradas.

Terceiro, defina o ParallelSum()método que calcula a soma dos números de 0 a 10001 em paralelo:

static int ParallelSum()
{
    return Enumerable.Range(0, 101).AsParallel()
        .Select(x => {
            Thread.Sleep(10);
            return x;
         })
        .Sum();
}Linguagem de código:  C#  ( cs )

O ParallelSum()usa o AsParallel()método para converter uma consulta em uma consulta paralela e retornar a soma dos números na sequência.

Por fim, execute ambos SequentialSum()os ParallelSum()métodos e meça o tempo que levaram:

Measure(SequentialSum, "SequentialSum");
Measure(ParallelSum, "ParallelSum");Linguagem de código:  C#  ( cs )

Saída:

The method SequentialSum took 1578 ms to run
The method ParallelSum took 293 ms to runLinguagem de código:  C#  ( cs )

A saída mostra que executar a consulta LINQ em paralelo é mais rápido em comparação com executá-la sequencialmente neste caso.

Notas importantes do PLINQ

Conforme mencionado anteriormente, as consultas LINQ paralelas são executadas apenas mais rapidamente do que as consultas LINQ normais com um grande conjunto de dados ou cálculos complexos.

Se você tiver um pequeno conjunto de dados com cálculos simples, as consultas paralelas poderão ser executadas mais lentamente do que as consultas LINQ normais porque o PLINQ apresenta algumas sobrecargas.

Por exemplo, se você remover a Thread.Sleep(10)expressão lambda do Select()método, verá que o SequentialSum()método é executado mais rápido que ParallelSum()o método:

using static System.Console;
using System.Diagnostics;

static void Measure(Func<int> f, string name)
{
    var stopwatch = Stopwatch.StartNew();
    stopwatch.Start();

    f();

    stopwatch.Stop();
    
    WriteLine($"The method {name} took {stopwatch.ElapsedMilliseconds} ms to run");
}

static int SequentialSum()
{
    return Enumerable.Range(0, 101)
        .Select(x => {
            //Thread.Sleep(10);
            return x;
        })
        .Sum();
}

static int ParallelSum()
{
    return Enumerable.Range(0, 101).AsParallel()
        .Select(x => {
            //Thread.Sleep(10);
            return x;
         })
        .Sum();
}

Measure(SequentialSum, "SequentialSum");
Measure(ParallelSum, "ParallelSum");Linguagem de código:  C#  ( cs )

Saída:

The method SequentialSum took 2 ms to run
The method ParallelSum took 74 ms to runLinguagem de código:  C#  ( cs )

Resumo

  • PLINQ permite executar consultas LINQ em paralelo em vários núcleos de CPU.
  • Use AsParallel()o método para executar consultas LINQ paralelas.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *