Ich habe diese einfache Schleife:
%Vor%Ich habe seine Leistung mit seiner C ++ - Version verglichen. Ich denke allerdings, dass die Performance in etwa gleich sein sollte, da es sich um einen sehr einfachen Code handelt und die Range-Checks in diesem Fall entfallen. Aber es stellte sich heraus, dass die C ++ Version fast dreimal schneller ist. Also habe ich C # unsicher Version implementiert, aber die Leistung war noch schlimmer. Resharper schlägt vor, die Schleife wie folgt in linq Ausdruck umzuwandeln:
%Vor%Dieser Code ist viel langsamer als die ursprüngliche Schleife in C #
Könnte mir jemand sagen, ob es noch etwas gibt, was ich tun kann, um die Leistung dieser Schleife zu verbessern (ohne sie in 64-Bit-Version zu kompilieren - was zwei mal schneller ist).
Alle Tests wurden mit 32-Bit-Version durchgeführt und ohne Debugger ausgeführt.
Bearbeiten: Kleine Korrektur. 64-Bit-Version ist doppelt so schnell mit Doppel, nicht ints
Wickeln Sie die Schleife 2-8 Mal aus. Messen Sie, welches ist das Beste. Das .NET JIT optimiert schlecht, also müssen Sie etwas von seiner Arbeit tun.
Wahrscheinlich müssen Sie auch unsafe
hinzufügen, da der JIT die Überprüfung der Array-Grenzen jetzt nicht optimieren kann.
Sie können auch versuchen, in mehrere Summenvariablen zu aggregieren:
%Vor% Das könnte die Parallelität auf Befehlsebene erhöhen, weil alle Anweisungen add
jetzt unabhängig sind.
Das i+0
wird automatisch auf i
optimiert.
Ich habe es getestet und es hat ungefähr 30% abgeschabt.
Die Zeiten sind stabil, wenn sie wiederholt werden. Code:
%Vor%Weiter herumspielen, es stellt sich heraus, dass mehrere Aggregationsvariablen nichts tun. Das Abwickeln der Schleife hat jedoch eine wesentliche Verbesserung bewirkt. Unsicher hat nichts gemacht (außer im abgewickelten Fall, wo es ziemlich benötigt wird). 2 mal abrollen ist so gut wie 4.
Wird auf einem Core i7 ausgeführt.
Linq Parallel scheint zumindest auf meiner Maschine gefastet zu haben.
Das Festlegen der Länge spielt keine große Rolle, verbessert aber ~ 10%
Es gibt nicht viel, was Sie tatsächlich tun könnten, unmanaged C-Code wird dafür immer schneller sein.
Ergebnisse auf meinem PC sind:
%Vor%Zuerst ein paar allgemeine Bemerkungen über Mikro-Benchmarks wie folgt:
ForEach
-Schleife einen anonymen Delegaten enthält, der beim ersten Aufruf nur JIT-aktiv ist und daher die JIT-Zeit in das Timing einbezogen wird, wenn der Benchmark zum ersten Mal ausgeführt wird. Es gibt vier grundlegende Techniken, um den Code zu beschleunigen (wenn wir ihn rein CLR behalten):
Hier ist der parallele Code:
%Vor% Die Partitioner
-Klasse lebt im Namensraum System.Collections.Concurrent
.
Auf meinem Rechner (i7 950, 8 logische Kerne) waren die folgenden Timings:
%Vor%Es gab keinen signifikanten Unterschied zwischen 32-Bit- und 64-Bit-Code.
Ich habe zu @ Elas 'Antwort folgendes hinzugefügt:
%Vor%und dann die Ergebnisse gedruckt, so dass Sie Referenzwerte erhalten:
%Vor% Wie Sie sehen können, waaay schneller :)
Für Stepsize
probierte ich arr.Length / 8
, arr.Length / 16
, arr.Length / 32
(ich habe i7 cpu (4 Kerne * 2 Threads = 8 Threads gleichzeitig)) und sie waren alle ziemlich gleich, also ist es deine Entscheidung
Edit: Ich habe auch stepsize = arr.length / 100
versucht, was irgendwo bei 250ms ist, also ein bisschen langsamer.
Die Verwendung von unmittelbaren Operanden verbessert die Leistung in gewissem Maße,
%Vor%Der obige Code muss auf zwei Speicherplätze zugreifen, d. h. int i und array.length;
Verwenden Sie stattdessen
%Vor%Unsicherer und paralleler Code sollte ebenfalls die Leistung erhöhen. Überprüfen Sie diesen Artikel, um mehr zu erfahren.
Eine einfache und manchmal signifikante C # for
-Schleifenoptimierung, die oft übersehen wird, ist das Wechseln des Schleifenzähler-Variablentyps von int
nach uint
. Dies führt zu einer durchschnittlichen Beschleunigung von ca. 12% für Ihre standardmäßigen i++
( Inkrement ) Schleifen mit Millionen von Iterationen. Wenn Ihre Schleife weniger als iteriert, wird die Leistung wahrscheinlich nicht viel ändern.
Beachten Sie, dass Arrays mit uint
indiziert werden können, ohne dass sie auf int
umgesetzt werden, damit Sie bei der Indexierung innerhalb der Schleife keinen der Vorteile verlieren. Die einzigen allgemeinen Gründe, diese Technik nicht zu verwenden, sind, wenn Sie negative Schleifenzählerwerte benötigen oder wenn die Schleifenzählervariable für andere Funktionsaufrufe usw. innerhalb der Schleife in int
umgewandelt werden muss. Sobald du Cast machen musst, ist es das wahrscheinlich nicht wert.
Tags und Links c# performance for-loop