Freigeben über


Gewusst wie: Beschleunigen von kurzen Schleifenkörpern

Eine For()-Schleife mit einem kurzen Körper wird möglicherweise langsamer ausgeführt als die entsprechende sequenzielle Schleife. Die geringere Leistung wird durch den Mehraufwand verursacht, der mit der Partitionierung der Daten und dem Aufruf eines Delegaten bei jeder Schleifeniteration verbunden ist. Für solche Szenarien stellt die Partitioner-Klasse die Create-Methode bereit, mit der Sie eine sequenzielle Schleife für den Delegattext angeben können, damit der Delegat nur einmal pro Partition aufgerufen wird, und nicht einmal pro Iteration. Weitere Informationen finden Sie unter Benutzerdefinierte Partitionierer für PLINQ und TPL.

Beispiel

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

Der in diesem Beispiel veranschaulichte Ansatz ist sinnvoll, wenn die Menge der von der Schleife ausgeführten Arbeit minimal ist. Bei rechenintensiverer Arbeit bietet die Verwendung einer For-Schleife oder ForEach-Schleife mit dem Standardpartitionierer wahrscheinlich die gleiche oder eine höhere Leistung.

Siehe auch

Referenz

Iteratoren (C#-Programmierhandbuch)

Konzepte

Datenparallelität (Task Parallel Library)

Benutzerdefinierte Partitionierer für PLINQ und TPL

Lambda-Ausdrücke in PLINQ und TPL