Compartir a través de


Cómo: Acelerar cuerpos de bucle pequeños

Cuando un bucle For() tiene un cuerpo pequeño, puede registrar un rendimiento más lento que el del bucle secuencial equivalente. Este rendimiento más lento es consecuencia de la sobrecarga en la participación de los datos y el costo de invocar un delegado en cada iteración del bucle. Para hacer frente a estos escenarios, la clase Partitioner proporciona el método Create, que permite proporcionar un bucle secuencial para el cuerpo de delegado de modo que el delegado solo se invoque una vez por partición, en lugar de una vez por iteración. Para obtener más información, vea Particionadores personalizados para PLINQ y TPL.

Ejemplo

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
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);
            }           
        }
    }
}

El enfoque mostrado en este ejemplo es útil cuando el bucle realiza una cantidad de trabajo mínima. Cuando el trabajo se vuelve más costoso en los cálculos, obtendrá probablemente un rendimiento igual o mejor si usa un bucle For o ForEach con el particionador predeterminado.

Vea también

Referencia

Iteradores (Guía de programación de C#)

Conceptos

Paralelismo de datos (Task Parallel Library)

Particionadores personalizados para PLINQ y TPL

Expresiones lambda en PLINQ y TPL