Warum ist das so und warum ist es für clang und gcc genau gleich?
Das Limit für die gleiche Schleife, wenn Sie ersetzen float mit double ist 479. Dies gilt wieder für gcc und clang.
Aktualisierung 1
Es stellt sich heraus, dass sich gcc 7 (snapshot) und clang (trunk) sehr unterschiedlich verhalten. Clang optimiert die Loops für alle Limits unter 960, soweit ich das beurteilen kann. gcc hingegen ist empfindlich gegenüber dem genauen Wert und hat keine Obergrenze. Zum Beispiel es nicht Optimieren Sie die Schleife, wenn das Limit 200 (sowie viele andere Werte) ist, aber es tut wenn die Grenze 202 und 20002 (sowie viele andere Werte) ist.
Was Sulthan wahrscheinlich meint, ist, dass 1) der Compiler die Schleife entrollt und 2) sobald sie entrollt ist, sieht, dass die Summenoperationen zu einer gruppiert werden können. Wenn die Schleife nicht ausgerollt wird, können die Operationen nicht gruppiert werden.
– Jean-Francois Fabre ♦
10. Februar 2017 um 12:39 Uhr
Eine ungerade Anzahl von Schleifen macht das Abrollen komplizierter, die letzten Iterationen müssen speziell durchgeführt werden. Das könnte durchaus ausreichen, um den Optimierer in einen Modus zu versetzen, in dem er die Verknüpfung nicht mehr erkennen kann. Es ist ziemlich wahrscheinlich, dass es den Code für den Spezialfall zuerst hinzufügen und dann wieder entfernen müsste. Den Optimierer zwischen den Ohren zu verwenden ist immer am besten 🙂
– Hans Passant
10. Februar 2017 um 13:02 Uhr
@HansPassant Es ist auch für jede Zahl kleiner als 959 optimiert.
– Graff
10. Februar 2017 um 13:05 Uhr
Würde dies nicht normalerweise mit der Eliminierung der Induktionsvariablen erfolgen, anstatt eine wahnsinnige Menge abzurollen? Das Abrollen um den Faktor 959 ist verrückt.
– Harald
10. Februar 2017 um 13:06 Uhr
@eleanora Ich habe mit diesem Compilre-Explorer gespielt und das Folgende scheint zu gelten (nur über den gcc-Snapshot zu sprechen): Wenn die Schleifenanzahl ein Vielfaches von 4 und mindestens 72 ist, dann ist die Schleife nicht abgerollt (oder besser gesagt um den Faktor 4 abgerollt); Andernfalls wird die gesamte Schleife durch eine Konstante ersetzt – auch wenn die Schleifenanzahl 2000000001 beträgt. Meine Vermutung: vorzeitige Optimierung (wie in einem verfrühten “Hey, ein Vielfaches von 4, das ist gut zum Ausrollen”, das eine weitere Optimierung blockiert, im Vergleich zu einem gründlicheren “Was hat es mit dieser Schleife überhaupt auf sich?”)
– Hagen von Eitzen
11. Februar 2017 um 22:10 Uhr
Grzegorz Szpetkowski
TL;DR
Standardmäßig verhält sich der aktuelle Snapshot GCC 7 inkonsistent, während frühere Versionen aufgrund von Standardlimits eingeschränkt sind PARAM_MAX_COMPLETELY_PEEL_TIMESdas ist 16. Es kann von der Befehlszeile aus überschrieben werden.
Der Grund für die Begrenzung besteht darin, ein zu aggressives Abrollen der Schleife zu verhindern, was ein zweischneidiges Schwert sein kann.
GCC-Version <= 6.3.0
Die relevante Optimierungsoption für GCC ist -fpeel-loopsdas indirekt zusammen mit flag aktiviert wird -Ofast (Hervorhebung von mir):
Schält Loops, für die genügend Informationen vorliegen, dass sie nicht viel rollen (aus Profil-Feedback bzw statische Analyse). Es schaltet auch das vollständige Schleifen-Peeling ein (dh vollständige Entfernung von Schleifen mit kleiner konstanter Anzahl von Iterationen).
Aktiviert mit -O3 und/oder -fprofile-use.
Weitere Details erhalten Sie durch Hinzufügen -fdump-tree-cunroll:
$ head test.c.151t.cunroll
;; Function f (f, funcdef_no=0, decl_uid=1919, cgraph_uid=0, symbol_order=0)
Not peeling: upper bound is known so can unroll completely
if (maxiter >= 0 && maxiter <= npeel)
{
if (dump_file)
fprintf (dump_file, "Not peeling: upper bound is known so can "
"unroll completely\n");
return false;
}
Eine ausführlichere Ausgabe kann mit erreicht werden -fdump-tree-cunroll-details:
Loop 1 iterates 959 times.
Loop 1 iterates at most 959 times.
Not unrolling loop 1 (--param max-completely-peeled-times limit reached).
Not peeling: upper bound is known so can unroll completely
Es ist möglich, die Grenzen zu optimieren, indem Sie mit klicken max-completely-peeled-insns=n und max-completely-peel-times=n Parameter:
max-completely-peeled-insns
Die maximale Anzahl von Insns einer vollständig abgezogenen Schleife.
max-completely-peel-times
Die maximale Anzahl von Iterationen einer Schleife, die für ein vollständiges Peeling geeignet ist.
f:
vmovss xmm0, DWORD PTR .LC0[rip]
ret
.LC0:
.long 1148207104
Klirren
Ich bin mir nicht sicher, was Clang tatsächlich tut und wie man seine Grenzen optimiert, aber wie ich beobachtet habe, könnten Sie es zwingen, den endgültigen Wert auszuwerten, indem Sie die Schleife mit markieren Pragma ausrollenund es wird es vollständig entfernen:
Vielen Dank für diese sehr nette Antwort. Wie andere angemerkt haben, scheint gcc empfindlich auf die genaue Grenzgröße zu reagieren. Beispielsweise wird die Schleife für 912 nicht eliminiert godbolt.org/g/EQJHvT . Was sagt fdump-tree-cunroll-details in diesem Fall?
– Graff
10. Februar 2017 um 21:41 Uhr
Tatsächlich hat sogar 200 dieses Problem. Dies ist alles in einem Schnappschuss von gcc 7, den Godbolt bereitstellt. godbolt.org/g/Vg3SVs Dies gilt überhaupt nicht für Clang.
– Graff
10. Februar 2017 um 21:56 Uhr
Sie erklären die Mechanik des Peelings, aber nicht, was die Relevanz von 960 ist oder warum es überhaupt eine Grenze gibt
– MM
10. Februar 2017 um 23:06 Uhr
@MM: Das Peeling-Verhalten ist zwischen GCC 6.3.0 und dem neuesten Snaphost völlig unterschiedlich. Bei ersterem vermute ich stark, dass das fest codierte Limit durchgesetzt wird PARAM_MAX_COMPLETELY_PEEL_TIMES param, das ist definiert in /gcc/params.def:321 mit dem Wert 16.
– Grzegorz Szpetkowski
10. Februar 2017 um 23:16 Uhr
Vielleicht möchten Sie erwähnen warum GCC beschränkt sich bewusst auf diese Weise. Insbesondere wenn Sie Ihre Schleifen zu aggressiv entrollen, wird die Binärdatei größer und Sie passen weniger wahrscheinlich in den L1-Cache. Cache-Misses sind potenziell ziemlich teuer relativ zum Speichern einiger bedingter Sprünge unter der Annahme einer guten Verzweigungsvorhersage (die Sie für eine typische Schleife haben werden).
– Kevin
11. Februar 2017 um 5:58 Uhr
Jean-Francois Fabre
Nachdem ich Sulthans Kommentar gelesen habe, denke ich, dass:
Der Compiler rollt die Schleife vollständig aus, wenn der Schleifenzähler konstant (und nicht zu hoch) ist.
Sobald es entrollt ist, sieht der Compiler, dass die Summenoperationen zu einer gruppiert werden können.
Wenn die Schleife aus irgendeinem Grund nicht ausgerollt wird (hier: es würde zu viele Anweisungen mit generieren 1000), können die Vorgänge nicht gruppiert werden.
Der Compiler könnte Beachten Sie, dass das Aufrollen von 1000 Anweisungen auf eine einzelne Addition hinausläuft, aber die oben beschriebenen Schritte 1 und 2 sind zwei separate Optimierungen, sodass Sie nicht das “Risiko” des Aufrollens eingehen können, ohne zu wissen, ob die Operationen gruppiert werden können (Beispiel: ein Funktionsaufruf kann nicht gruppiert werden).
Hinweis: Dies ist ein Sonderfall: Wer verwendet eine Schleife, um dasselbe noch einmal hinzuzufügen? Verlassen Sie sich in diesem Fall nicht auf das mögliche Entrollen/Optimieren des Compilers; Schreiben Sie die richtige Operation direkt in eine Anweisung.
dann kannst du dich darauf konzentrieren not too high Teil? Ich meine, warum ist das Risiko in dem Fall nicht da 100 ? Ich habe etwas vermutet … in meinem Kommentar oben … kann es der Grund dafür sein?
– Benutzer2736738
10. Februar 2017 um 12:49 Uhr
Ich denke, dass der Compiler sich der Ungenauigkeit der Gleitkommazahl, die er auslösen könnte, nicht bewusst ist. Ich denke, es ist nur eine Begrenzung der Befehlsgröße. Du hast max-unrolled-insns neben max-unrolled-times
– Jean-Francois Fabre ♦
10. Februar 2017 um 13:03 Uhr
Ah, es war irgendwie mein Gedanke oder meine Vermutung … ich möchte eine klarere Argumentation bekommen.
– Benutzer2736738
10. Februar 2017 um 13:06 Uhr
Interessanterweise ändert man das float zu einem intist der gcc-Compiler aufgrund seiner Induktionsvariablenoptimierungen in der Lage, die Schleife unabhängig von der Anzahl der Iterationen zu reduzieren (-fivopts). Aber die scheinen nicht zu funktionieren floats.
– Tavian Barnes
10. Februar 2017 um 17:29 Uhr
@CortAmmon Richtig, und ich erinnere mich, dass ich einige Leute gelesen habe, die überrascht und verärgert waren, dass GCC MPFR verwendet, um sehr große Zahlen genau zu berechnen, was ziemlich andere Ergebnisse liefert als die entsprechenden Gleitkommaoperationen, die Fehler und Genauigkeitsverlust angesammelt hätten. Zeigt, dass viele Leute Gleitkommazahlen falsch berechnen.
– Zan Luchs
10. Februar 2017 um 22:31 Uhr
Sehr gute Frage!
Sie scheinen eine Grenze für die Anzahl der Iterationen oder Operationen erreicht zu haben, die der Compiler beim Vereinfachen des Codes einzubetten versucht. Wie von Grzegorz Szpetkowski dokumentiert, gibt es Compiler-spezifische Möglichkeiten, diese Grenzen mit Pragmas oder Befehlszeilenoptionen zu optimieren.
Sie können auch mit spielen Godbolts Compiler Explorer um zu vergleichen, wie sich verschiedene Compiler und Optionen auf den generierten Code auswirken: gcc 6.2 und icc 17 immer noch den Code für 960 inline, während clang 3.9 nicht (mit der standardmäßigen Godbolt-Konfiguration stoppt das Inlining tatsächlich bei 73).
Ich habe die Frage bearbeitet, um die Versionen von gcc und clang zu verdeutlichen, die ich verwendet habe. Sehen godbolt.org/g/FfwWjL . Ich verwende zum Beispiel -Ofast.
– Graff
10. Februar 2017 um 16:36 Uhr
14232900cookie-checkWarum wird eine einfache Schleife optimiert, wenn das Limit 959, aber nicht 960 ist?yes
Was Sulthan wahrscheinlich meint, ist, dass 1) der Compiler die Schleife entrollt und 2) sobald sie entrollt ist, sieht, dass die Summenoperationen zu einer gruppiert werden können. Wenn die Schleife nicht ausgerollt wird, können die Operationen nicht gruppiert werden.
– Jean-Francois Fabre
♦
10. Februar 2017 um 12:39 Uhr
Eine ungerade Anzahl von Schleifen macht das Abrollen komplizierter, die letzten Iterationen müssen speziell durchgeführt werden. Das könnte durchaus ausreichen, um den Optimierer in einen Modus zu versetzen, in dem er die Verknüpfung nicht mehr erkennen kann. Es ist ziemlich wahrscheinlich, dass es den Code für den Spezialfall zuerst hinzufügen und dann wieder entfernen müsste. Den Optimierer zwischen den Ohren zu verwenden ist immer am besten 🙂
– Hans Passant
10. Februar 2017 um 13:02 Uhr
@HansPassant Es ist auch für jede Zahl kleiner als 959 optimiert.
– Graff
10. Februar 2017 um 13:05 Uhr
Würde dies nicht normalerweise mit der Eliminierung der Induktionsvariablen erfolgen, anstatt eine wahnsinnige Menge abzurollen? Das Abrollen um den Faktor 959 ist verrückt.
– Harald
10. Februar 2017 um 13:06 Uhr
@eleanora Ich habe mit diesem Compilre-Explorer gespielt und das Folgende scheint zu gelten (nur über den gcc-Snapshot zu sprechen): Wenn die Schleifenanzahl ein Vielfaches von 4 und mindestens 72 ist, dann ist die Schleife nicht abgerollt (oder besser gesagt um den Faktor 4 abgerollt); Andernfalls wird die gesamte Schleife durch eine Konstante ersetzt – auch wenn die Schleifenanzahl 2000000001 beträgt. Meine Vermutung: vorzeitige Optimierung (wie in einem verfrühten “Hey, ein Vielfaches von 4, das ist gut zum Ausrollen”, das eine weitere Optimierung blockiert, im Vergleich zu einem gründlicheren “Was hat es mit dieser Schleife überhaupt auf sich?”)
– Hagen von Eitzen
11. Februar 2017 um 22:10 Uhr