Warum ist der loop-Anweisung langsam? Konnte nicht auf Intel umgesetzt haben, ist es effizient?

LOOP (Intel ref manuelle Eingabe)
dekrementiert ecx /rcx, und dann springt, falls nicht null. Es ist langsam, aber Sie konnte nicht Intel preiswert schnell? dec/jnz bereits makro-sicherungen in einem einzigen Upstream-Provider auf Sandybridge-Familie; der einzige Unterschied ist, dass das setzt flags.

loop auf verschiedenen microarchitectures, von Agner Fog-Anweisung Tabellen:

  • K8/K10: 7 m-ops
  • Bulldozer-Familie/Ryzen: 1 m-op (gleiche Kosten wie makro-fused test-und-Zweig, oder jecxz)

  • P4: 4 uops (gleiche wie jecxz)

  • P6 (PII/PIII): 8 uops
  • Pentium M, Core2: 11 uops
  • Nehalem: 6 uops. (11 für loope /loopne). Durchsatz = 4c (loop) oder 7c (loope/ne).
  • SnB-Familie: 7 uops. (11 für loope /loopne). Durchsatz = eine pro 5 Zyklen, wie viel von einem Engpass, als wenn Ihr den loop-Zähler im Speicher! jecxz ist nur 2 uops mit dem gleichen Durchsatz wie die regelmäßige jcc
  • Silvermont: 7 uops
  • AMD Jaguar (low-power): 8 uops, 5c Durchsatz
  • Über Nano3000: 2 uops

Konnte nicht die Decoder nur decodieren das gleiche wie lea rcx, [rcx-1] /jrcxz? Das wäre 3 uops. Zumindest wäre das der Fall mit keine Adresse-Präfix Größe, da es sonst zu verwenden hat ecx und abschneiden RIP zu EIP wenn der Sprung genommen wird; vielleicht ist die seltsame Wahl der Adresse-Größe, die Steuerung der Breite der Dekrement erklärt die vielen uops?

Oder besser, nur Sie entschlüsseln, wie eines fusionierten, dec-und-Zweig, der nicht gesetzten flags? dec ecx /jnz auf SnB dekodiert, um eine einzelne uop (die nicht gesetzten flags).

Ich weiß, dass real-code nicht verwenden (da es schon langsam seit mindestens P5 oder sowas), aber AMD beschlossen, es war es Wert, um es schnell für Bulldozer. Wahrscheinlich, weil es einfach war.


  • Würde es leicht sein, für SnB-Familie uarch die schnelle loop? Wenn dem so ist, warum nicht? Wenn nicht, warum ist es schwer? Viele decoder mit transistoren? Oder extra-bits in eine verschmolzen Dez&branch Upstream-Provider zu erfassen, dass es nicht gesetzten flags? Was könnten diese 7 uops tun? Es ist eine wirklich einfache Anleitung.

  • Was ist das Besondere an der Bulldozer, der einen schnellen loop einfache /lohnt sich das? Oder hat AMD Müll ein Haufen transistoren auf machen loop schnell? Wenn dem so ist, vermutlich jemand dachte, es war eine gute Idee.


Wenn loop war schnell, es wäre perfekt für BigInteger beliebige Präzision adc - Schleifen, um zu vermeiden, Teil-flag-Stände /slowdowns (siehe meine Kommentare auf meine Antwort), oder jedem anderen Fall, wo Sie wollen, um die Schleife, ohne diese zu berühren Fahnen. Es hat auch eine kleinere code-Größe Vorteil gegenüber dec/jnz. (Und dec/jnz nur makro-sicherungen auf SnB-Familie).

Auf modernen CPUs, wo dec/jnz ok ist, in einen ADC-Schleife loop wäre noch schön für ADCX /ADOX Schleifen (zu bewahren).

Wenn loop hatte, wurden schnell, Compiler würde schon werden Sie es als eine peephole-Optimierung für code-Größe + Geschwindigkeit auf CPUs ohne makro-fusion.


Es würde nicht aufhören, mich immer genervt auf all die Fragen, die mit schlechten 16bit-code, der verwendet loop für jeden loop, auch wenn Sie müssen auch ein weiterer Zähler innerhalb der Schleife. Aber zumindest wäre es nicht als schlecht.

  • Es ist schon komisch, dass AMD selbst empfiehlt die Vermeidung der LOOP Anweisung, wenn die Optimierung für den Bulldozer.
  • Vielleicht ist es nicht Zweig-Vorhersagen, die auf dieselbe Weise? IDK. Ich fand einige Spekulationen und plausible Theorien über groups.google.com/d/msg/comp.arch/5RN6EegUxE0/KETMqmKWVN4J. (Link zu einer von Paul Clayton ' s post Mitte Weg, obwohl. Scrollen Sie bis zum Beginn des Threads, das war eine exakte Kopie von meiner Frage). hurr durr google Ihre Fragen >.<
  • Einer der anderen Antworten, die sagt: "SCHLEIFE wurde langsam auf einige der frühesten Maschinen (circa 486), wenn erhebliche pipelining begann zu geschehen, und die laufen alle, aber die einfachste Anweisung über die pipeline effizient war technisch nicht praktikabel. So SCHLEIFE war zu langsam für eine Anzahl von Generationen. Also niemand benutzt es. Also, wenn es möglich wurde, um ihn zu beschleunigen, gab es keinen wirklichen Anreiz, dies zu tun, da niemand tatsächlich. "Also, wenn der Compiler aufgehört haben, dem Unterricht, warum die Mühe, es jetzt zu verbessern? Es würde nicht verbessern den Grundstein für eine neue CPU...
  • "es lohnt sich nicht beschleunigt, 'Ursache, die niemand nutzt, weil es langsam?" ist das genial 🙂
  • Hätte es effizient wieder auf P6, Compiler würde schon werden Sie es verwenden, und speichern Sie ein paar code-bytes. (Und bevor makro-fused dec-und-Zweig, speichern uops, auch wenn es einzelne-uop). Dies gilt nur für die seltenen Fälle, in denen ein compiler transformieren kann den Schleifenzähler in ein count-down, da die meisten Programmierer schreiben Ihre loops zu zählen. Auch ohne loop auf asm-Ebene, die einen Countdown auf null ist etwas effizienter, da das Dekrementieren wird das zero-flag, ohne dass ein vergleichen Sie. Ich in der Regel immer noch schreiben, dass meine C-Schleifen von 0..n, zur besseren Lesbarkeit aber.
  • Schleife über einen Puffer nach vorn in eine Schleife, dann rückwärts in die nächste Schleife, ist wahrscheinlich die Idee, den Fall für die Zwischenspeicherung, wenn. In der Theorie erhalten Sie immer eine komplette cache-Größe, block-cache-Treffer bei der turn-around-Ende des Puffers, anstatt sich zu keinem Treffer, wenn das array ist etwas zu groß (und der Anfang ist vertrieben durch die Zeit, die Sie am Ende bekommen). Hardware Prefetcher erkennen vorwärts-und rückwärts-streams, so dass Sie nicht verpassen diese (habe ich geprüft, und das ist wahr für mindestens SnB-Familie. HW Prefetcher vielleicht weniger nach hinten Ablagefächer auf ältere CPUs habe ich vergessen.)
  • Ich arbeitete bei Nexgen, für eine kurze Weile, dann bei AMD auf der K6, K6-2 und Athlon Prozessoren. Ein problem, das ich erinnere mich mit der LOOP Anweisung ist, dass schnelle Implementierungen, die Sie verursachen würden bestimmte bestehende software (mehr als ein Programm), um Fehlfunktionen, die verwendet werden LOOP für delay-Schleifen zu implementieren Mikro-Verzögerungen, z.B. in der Treiber-software. Soweit ich mich erinnere (aber meine Erinnerung ist verschwommen und ich habe nicht die Zeit zu finden, Verweise), die beide Nexgen und Cyrix fiel in die Falle, ca. 1995. Smart-CPU-Architekten nur machen Sie den gleichen Fehler einmal, so dass spätere CPUs gehalten LOOP langsam auf Zweck.
  • Ah, daran hatte ich nicht gedacht Richtigkeit Probleme mit Treibern. Timing-Probleme sind erwähnt worden, wie einer der Gründe, aber ich hatte darüber nachgedacht, Spiele, oder etwas, das würde zu schnell laufen, und variable CPU Geschwindigkeit macht das obsolet. Aber wenn der Fahrer Verzögerungen kann kürzer sein, auf schnellere CPUs, das macht Sinn. (Oder, wenn Sie Kalibrieren den delay Schleifen beim Start, wenn schnell loop die erforderlichen Graf überlauf?) Da AMD hat wieder einmal versucht, das Schicksal mit fast loop ich denke, es ist sicher anzunehmen, dass die Art des delay-Schleife ist komplett tot, im Alter von DVFS Energiespar-/turbo-CPU-clocks.
  • Nexgen ist Nx586 hatte patchbare microcode, gespeichert in der SBIOS, also die Befestigung das Problem mit dem schnellen LOOPAnweisung erforderlich ist nichts mehr als ein BIOS-update, soweit ich mich erinnere. Ich bin unter dem Eindruck, dass patchbare microcode ist eine standard-Funktion auf x86-Prozessoren in diesen Tagen, so dass es nicht nehmen viel Mut, um zu versuchen eine schnelle LOOP. Diese delay-loops starb wahrscheinlich mit DOS-und Win16-aber für den Athlon-Prozessor stecken wir mit einem langsamen LOOP Umsetzung zu vermeiden, unnötige Risiken: software hat die Tendenz, länger zu Leben als die hardware.
  • IDK, wenn Planierraupe loop Anweisung kann geändert werden, mit microcode. Ja, Intel und AMD haben patchbare microcode (und ja, es gibt tatsächliche bugfixes in den updates für Skylake, zum Beispiel!). Aber nicht alles ist nicht microcoded. Ich vermute loop sein könnte hard-wired. Im AMD-Terminologie, ist es ein "DirectPath Single" Unterricht, decodeable von jedem der 4-Decoder in einem einzigen makro-op. Nur VectorPath Anweisungen (mehr als 2 m-ops) bekommen uops aus einer ucode ROM. (superuser.com/q/360456/20798). (Intel ist ähnlich, 4 uops und weniger decodiert werden direkt).
  • Ich vermute, NX586 ist LOOP war mehrere uops und kam aus ROM sowieso, so dass Sie könnte leicht machen es langsamer? Microcode-updates können oft nur die Dinge beheben durch ausschalten der ganzen features. z.B. Skylake hat einen bug mit Teil-register umbenennen und Zusammenführen uops, und das update zu Update, das deaktiviert die loop-Puffer komplett (also auch kleine loops zu Holen uops aus der L0 uop cache, anstelle von recycling der Puffer, dass die feeds das Problem stage). Zum Glück Skylake nur aufgepeppt die front-end, so dass es nicht zu einem Engpass prob. nur eine kleine power-Strafe.
  • Nx586 ist LOOP Unterricht wurde microcoded, somit die Leichtigkeit der Verlangsamung. DirectPath ist AMD Terminologie für eine Anweisung implementiert, die direkt in hardware, während VectorPath bezieht sich auf microcoded Anweisungen (ich war ein microcoder für den Athlon-Prozessor, wo das gleiche Terminologie verwendet wurde, die vor zwanzig Jahren). Ob DirectPath Anweisungen auf modernen AMD-Prozessoren werden könnte, re-vektorielle Mikrocode für bug-fixing Zwecke, ich weiß nicht; im Allgemeinen ist es sicherlich technisch möglich, design-in-solch eine Funktion (für eine kleine Anzahl von Anweisungen).
  • in Bezug auf das update, um dass deaktiviert die loop-buffer-ganz - hast du eine Referenz für diese Behauptung? Es wäre eine große Sache, aber ich sehe keine Bestätigung noch. Update: ich fand dieser.
  • perf Counter auf meinem desktop. Ich meinte zu erwähnen, dass in einem update zu meinem SKL teilweise-regs Antwort. Alles, was ich habe profilierten da eigentlich aktivieren von Arch Linux zu aktualisieren, die ucode gezeigt hat, genau 0 zählt für lsd.uops. Auch nicht-microbench Dinge (wie ocperf.py -p some-PID) haben niemals eine zählt. Entweder, dass die perf counter ist jetzt gebrochen, oder Sie deaktiviert die LSD. Ich habe gelesen, dass die SKL-X nicht verwenden, LSD, und diese Entdeckung erklärt, warum: es versendet mit neuen genug ucode zum deaktivieren des LSD. (update: finden Sie den gleichen link hast du auf wikichip).
  • IMO ist das eine große Sache.
  • Ja, es ist, aber ich denke, der Effekt ist klein bis nicht existent, die meisten der Zeit. Das LSD nur gearbeitet, uops, die enthalten sind in der uop-cache, und die SKL hat ausgezeichnete uop-cache lese-Bandbreite. Es sei denn, dein code passt sehr schlecht in die uop-cache und sonst sustain-4 uops pro Takt, es ist nicht ein echtes Nadelöhr.
  • richtig, ich würde vermuten, dass performance-Weise, ist es eigentlich ein pessimization mehr als oft ein Vorteil, aber es ist da, um Energie zu sparen, richtig? Es scheint wie eine nicht-triviale Menge an Komplexität-und Validierungs-Aufwand, so dass ich davon ausgehen muss es eine angemessene Leistung profitieren. Mit sehr hoher Wahrscheinlichkeit die meisten Menschen werden nie in diesen Fehler (aufgrund der speziellen high-reg nutzen, das es auslöst), so bezahlen jeden Preis ist sowas von bedauerlich.
  • Ja, ich denke, dass der Hauptvorteil in der SKL war macht. Auf HSW, es könnte einem manchmal perf Schub, denke ich. Ich habe noch nicht getestet, Wann genau uop-cache gelesen werden können, einen Engpass auf NHM (z.B. mit 5 uops pro Zeile?), also, welche Art von Puffer-es gibt vor, dass "4 uops pro Takt aus dem DSB" die Grenze an der HSW. Sie hielt die LSD von NHM, wo es war definitiv ein großer Schub (kein uop-cache), aber wahrscheinlich eine Menge es musste neu implementiert für die SnB. Noch, IDK, wenn Sie würde haben ihn entworfen von Grund auf für die SnB, wenn Sie nicht bereits haben es aus dem NHM.
  • Als der KBY (Kaby Lake) und APL (Apollo See) nichts verändert zu haben scheint: uops.info/html-instr/LOOP-786.html

InformationsquelleAutor Peter Cordes | 2016-03-02
Schreibe einen Kommentar