Langue

Comment accélérer les corps de petites boucles

Lorsqu’une boucle Parallel.For a un petit corps, elle peut s’exécuter plus lentement que la boucle séquentielle équivalente, telle que la boucle for en C# et la boucle For dans Visual Basic. Les performances plus lentes sont causées par la surcharge liée au partitionnement des données et au coût d’appel d’un délégué sur chaque itération de boucle. Pour résoudre ces scénarios, la Partitioner classe fournit la Partitioner.Create méthode, qui vous permet de fournir une boucle séquentielle pour le corps délégué, afin que le délégué soit appelé une seule fois par partition, au lieu d’une seule fois par itération. Pour plus d’informations, consultez Partitionneurs personnalisés pour PLINQ et TPL.

Exemple

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

L’approche illustrée dans cet exemple est utile lorsque la boucle effectue un travail minimal. À mesure que le travail devient plus coûteux en calcul, vous obtiendrez probablement les mêmes performances ou de meilleures performances à l’aide d’une boucle ForEach ou For avec le partitionneur par défaut.

Voir également :