Nach 26 Iterationen fährt Linux die CPU auf die maximale Taktrate hoch, da Ihr Prozess ein paar Mal hintereinander seine volle Zeitscheibe verwendet.
Wenn Sie mit Leistungszählern anstelle der Uhrzeit nachsehen, würden Sie sehen, dass die Kerntaktzyklen pro Verzögerungsschleife konstant blieben, was bestätigt, dass dies nur ein Effekt von DVFS ist (das alle modernen CPUs verwenden, um mit mehr Energie zu laufen). effiziente Frequenz und Spannung die meiste Zeit).
Wenn Sie auf einem Skylake mit Kernel-Unterstützung für den neuen Energieverwaltungsmodus (bei dem die Hardware die volle Kontrolle über die Taktrate übernimmt) getestet haben, würde das Hochfahren viel schneller erfolgen.
Wenn Sie es für eine Weile auf einer Intel-CPU mit Turbo laufen lassen, werden Sie wahrscheinlich feststellen, dass die Zeit pro Iteration wieder leicht ansteigt, sobald die thermischen Grenzen erfordern, dass die Taktfrequenz wieder auf die maximal anhaltende Frequenz reduziert wird. (Siehe Warum kann meine CPU die Spitzenleistung in HPC nicht aufrechterhalten, um mehr darüber zu erfahren, wie Turbo die CPU schneller laufen lässt, als sie für Hochleistungs-Workloads aufrechterhalten kann.)
Einführung von 01
verhindert, dass der CPU-Frequenzregler von Linux die Taktrate erhöht, da der Prozess selbst bei minimaler Frequenz keine 100%ige Last erzeugt. (Das heißt, die Heuristik des Kernels entscheidet, dass die CPU schnell genug für die darauf ausgeführte Arbeitslast läuft.)
Kommentare zu anderen Theorien :
Betreff:Davids Theorie, dass ein möglicher Kontextwechsel von 10
könnte Caches verschmutzen:Das ist im Allgemeinen keine schlechte Idee, aber es hilft nicht, diesen Code zu erklären.
Cache-/TLB-Verschmutzung ist für dieses Experiment überhaupt nicht wichtig . Es gibt im Grunde nichts innerhalb des Zeitfensters, das den Speicher außer dem Ende des Stapels berührt. Die meiste Zeit wird in einer winzigen Schleife (1 Zeile Anweisungs-Cache) verbracht, die nur einen 28
berührt des Stapelspeichers. Jede potenzielle Cache-Verschmutzung während 32
ist ein winziger Bruchteil der Zeit für diesen Code (echter Code wird anders sein)!
Ausführlicher für x86:
Der Aufruf von 40
selbst kann einen Cache-Miss verursachen, aber ein Code-Fetch-Cache-Miss verzögert die Messung der Startzeit, anstatt Teil dessen zu sein, was gemessen wird. Der zweite Aufruf von 58
wird fast nie verzögert, da es immer noch heiß im Cache sein sollte.
Die 61
Die Funktion befindet sich möglicherweise in einer anderen Cache-Zeile als 73
(da gcc 80
markiert als "kalt", also wird es weniger optimiert und mit anderen kalten Funktionen/Daten platziert). Wir können mit ein oder zwei Befehls-Cache-Fehlschlägen rechnen. Sie befinden sich jedoch wahrscheinlich immer noch auf derselben 4k-Seite, also 90
wird den potenziellen TLB-Fehlschlag ausgelöst haben, bevor er in den zeitgesteuerten Bereich des Programms eingetreten ist.
gcc -O0 kompiliert den OP-Code in etwa so (Godbolt Compiler Explorer):Behält den Schleifenzähler im Speicher auf dem Stack.
Die leere Schleife hält den Schleifenzähler im Stapelspeicher, sodass die Schleife auf einer typischen Intel x86-CPU mit einer Iteration pro ~6 Zyklen auf der IvyBridge-CPU des OP ausgeführt wird, dank der Speicherweiterleitungslatenz, die Teil von 107 mit einem Speicherziel (Lesen-Ändern-Schreiben).
115
beträgt 600.000 Zyklen, was den Beitrag von höchstens ein paar Cache-Fehlschlägen dominiert (jeweils ~200 Zyklen für Code-Fetch-Fehlschläge, die verhindern, dass weitere Anweisungen ausgegeben werden, bis sie aufgelöst sind).
Out-of-Order-Ausführung und Store-Forwarding sollten den potenziellen Cache-Miss beim Zugriff auf den Stack größtenteils verbergen (als Teil der 120
Anleitung).
Selbst wenn der Loop-Counter in einem Register gehalten wird, sind 100.000 Zyklen viel.
Ein Anruf bei 134
kann zu einem Kontextwechsel führen oder auch nicht. Wenn dies der Fall ist, dauert es länger, als wenn dies nicht der Fall ist.