Lenguaje

Procedimiento para acelerar cuerpos de bucle pequeños

Cuando un Parallel.For bucle tiene un cuerpo pequeño, puede funcionar más lentamente que el bucle secuencial equivalente, como el bucle for en C# y el bucle For en Visual Basic. El rendimiento más lento se debe a la sobrecarga implicada en la creación de particiones de los datos y el costo de invocar un delegado en cada iteración de bucle. Para abordar estos escenarios, la Partitioner clase proporciona el Partitioner.Create método , que permite proporcionar un bucle secuencial para el cuerpo del delegado, de modo que el delegado se invoque solo una vez por partición, en lugar de una vez por iteración. Para obtener más información, consulte Particionadores personalizados para PLINQ y TPL.

Ejemplo

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {

        // Source must be array or IList.
        var source = Enumerable.Range(0, 100000).ToArray();

        // Partition the entire source array.
        var rangePartitioner = Partitioner.Create(0, source.Length);

        double[] results = new double[source.Length];

        // Loop over the partitions in parallel.
        Parallel.ForEach(rangePartitioner, (range, loopState) =>
        {
            // Loop over each range element without a delegate invocation.
            for (int i = range.Item1; i < range.Item2; i++)
            {
                results[i] = source[i] * Math.PI;
            }
        });

        Console.WriteLine("Operation complete. Print results? y/n");
        char input = Console.ReadKey().KeyChar;
        if (input == 'y' || input == 'Y')
        {
            foreach(double d in results)
            {
                Console.Write("{0} ", d);
            }
        }
    }
}
Imports System.Threading.Tasks
Imports System.Collections.Concurrent

Module PartitionDemo

    Sub Main()
        ' Source must be array or IList.
        Dim source = Enumerable.Range(0, 100000).ToArray()

        ' Partition the entire source array. 
        ' Let the partitioner size the ranges.
        Dim rangePartitioner = Partitioner.Create(0, source.Length)

        Dim results(source.Length - 1) As Double

        ' Loop over the partitions in parallel. The Sub is invoked
        ' once per partition.
        Parallel.ForEach(rangePartitioner, Sub(range, loopState)

                                               ' Loop over each range element without a delegate invocation.
                                               For i As Integer = range.Item1 To range.Item2 - 1
                                                   results(i) = source(i) * Math.PI
                                               Next
                                           End Sub)
        Console.WriteLine("Operation complete. Print results? y/n")
        Dim input As Char = Console.ReadKey().KeyChar
        If input = "y"c Or input = "Y"c Then
            For Each d As Double In results
                Console.Write("{0} ", d)
            Next
        End If

    End Sub
End Module

El enfoque que se muestra en este ejemplo es útil cuando el bucle realiza una cantidad mínima de trabajo. A medida que el trabajo se vuelve más costoso computacionalmente, probablemente obtendrá el mismo o mejor rendimiento mediante un bucle For o ForEach con el particionador predeterminado.

Consulte también