Partager via


Comment : accélérer les petits corps de boucles

Les boucles For() avec un petit corps peuvent s'exécuter plus lentement que les boucles séquentielles équivalentes. Le ralentissement des performances peut être provoqué par les surcharges impliquées dans le partitionnement des données et le coût de l'appel d'un délégué sur chaque itération de boucle. Pour résoudre les scénarios de ce type, la classe Partitioner propose la méthode Create, qui vous permet de fournir une boucle séquentielle au corps du délégué, afin que le délégué soit appelé une seule fois par partition, au lieu d'une fois par itération. Pour plus d'informations, consultez Partitionneurs personnalisés pour PLINQ et la bibliothèque parallèle de tâches (TPL).

Exemple

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

La méthode illustrée dans cet exemple est utile lorsque la boucle exécute un volume de travail minime. Lorsque le travail devient plus gourmand en ressources informatiques, vous obtiendrez des performances identiques ou meilleures en utilisant une boucle For ou ForEach avec le partitionneur par défaut.

Voir aussi

Référence

Itérateurs (Guide de programmation C#)

Concepts

Parallélisme de données (bibliothèque parallèle de tâches)

Partitionneurs personnalisés pour PLINQ et la bibliothèque parallèle de tâches (TPL)

Expressions lambda en PLINQ et dans la bibliothèque parallèle de tâches