2014-11-04 3 views
8

Die Intel Optimization Reference, unter Abschnitt 3.5.1, rät:Wie kann ich die Mikro-Ops finden, auf denen die Anweisungen auf Intels x86-CPUs dekodieren?

"Favor-Single-Mikro-Operation Anweisungen."

"Vermeiden Sie die Verwendung komplexer Anweisungen (z. B. Eingabe, Verlassen oder Schleife), die mehr als 4 Mikrobefehle haben und mehrere Zyklen zum Decodieren erfordern. Verwenden Sie stattdessen Sequenzen einfacher Anweisungen."

Obwohl Intel selbst Compiler-Schreibern befiehlt, Anweisungen zu verwenden, die zu wenigen Mikro-Ops dekodieren, kann ich in keinem ihrer Handbücher etwas finden, das erklärt, wie viele Mikro-Ops jeder ASM-Befehl dekodiert! Sind diese Informationen überall verfügbar? (Natürlich erwarte ich, dass die Antworten für verschiedene Generationen von CPUs unterschiedlich sein werden.)

+4

Sie können nicht, sie sind ändern. Aktuelle Tendenz um sie wieder CISC-like zu machen durch * fusion * Agner Fogs [Anweisungstabellen Dokument] (http://www.agner.org/optimize/) ist eine sehr anständige Ressource. –

+0

@HansPassant, sieht aus wie die Instr Tabellen, mit denen Sie verbunden sind, sind die beste verfügbare Referenz. Willst du das beantworten, damit ich es annehmen kann? –

+0

mögliches Duplikat von [Warum versteckt Intel den internen RISC-Kern in seinen Prozessoren?] (Http://stackoverflow.com/questions/5806589/why-does-intel-hide-internal-risc-core-in-the-processors) –

Antwort

8

Agner Fogs PDF document auf x86 Anweisungen (von der Hauptseite Hans zitiert zitiert) ist die einzige Referenz, die ich auf Anweisung gefunden habe Timings und Mikro-Ops. Ich habe noch nie ein Intel-Dokument zum Mikro-Ausfall gesehen.

1

Agner Fogs Tabellen zeigen, auf welchen Port Mikro-Ops laufen, was für die Leistung von entscheidender Bedeutung ist. Es zeigt nicht genau, was jeder UOP macht, denn das kann man nicht rückentwickeln. (d. h. welche Ausführungseinheit es an diesem Port verwendet).

In einigen Fällen ist es jedoch leicht zu erraten: haddps auf Haswell ist 1 Up für Port und 2 Ups für Port 5. Das ist ziemlich offensichtlich 2 Shuffle (Port 5) und ein FP-Add (Port 1). Es gibt viele andere Ausführungseinheiten an Port 5, z. Vektor boolean, SIMD Integer add, und viele skalare Integer-Sachen, aber vorausgesetzt, dass haddps mehrere Uups überhaupt braucht, ist es ziemlich offensichtlich, dass Intel es mit Shuffle und einem regulären "vertikalen" addieren UOP implementiert.

Es könnte möglich sein, etwas über die Abhängigkeitsbeziehung zwischen diesen uops herauszufinden (z. B. sind es 2 Shuffs-Style-Shuffle, die ein FP-Adding füttern, oder ist es shuffle-add-shuffle?). Wir sind uns auch nicht sicher, ob die Shuffles voneinander unabhängig sind oder nicht: Haswell hat nur einen Shuffle-Port, so dass der Ressourcenkonflikt eine Gesamtlatenz von 5c ergibt, weil die Shuffles nicht parallel laufen könnten, selbst wenn sie unabhängig wären.

Beide Shuffle-Ups benötigen wahrscheinlich beide Eingänge, so dass selbst wenn sie unabhängig voneinander sind, ein Eingang früher als der andere die Latenz für den kritischen Pfad nicht verbessert (vom langsameren Eingang zum Ausgang)).

Wenn es möglich wäre, HADDPS mit zwei unabhängigen Shuffle mit einem Eingang zu implementieren, würde das bedeuten, dass HADDPS xmm0, xmm1 in einer Schleife, in der xmm1 eine Konstante ist, der dep-Kette von xmm0 nur 4c Latenz hinzufügen würde. Ich habe es nicht gemessen, aber ich denke, es ist unwahrscheinlich; Mit Sicherheit sind es zwei unabhängige 2-Input-Shuffle, um einen ADDPS-Up zu füttern.

3

Es wurde bereits darauf hingewiesen, dass Agner Fog's optimization manuals eine ausgezeichnete Ressource sind, und insbesondere seine Instruction Tables, die fast für alle x86-Mikroarchitekturen von Interesse sind.

Aber Sie haben eine andere Option: Intel's Architecture Code Analyzer (IACA). Es gibt eine Beschreibung, wie man es benutzt here on Stack Overflow, aber es ist ziemlich einfach zu gehen (obwohl ein bisschen langweilig für einmalige Analyse). Sie laden einfach die ausführbare Datei herunter, geben einen Prolog- und Epilog-Code um den zu analysierenden Anweisungsblock herum (zu diesem Zweck enthält sie einen C-Header (iacaMarks.h), der mit verschiedenen Compilern zusammenarbeitet, oder Sie weisen Ihren Assembler an, den geeignete Bytes), und führen Sie dann Ihre Binärdatei durch iaca.exe. Die aktuelle Version (v2.2) unterstützt nur 64-Bit-Binärdateien, aber das ist keine große Einschränkung, da die Analyse auf Befehlsebene für 32-Bit- und 64-Bit-Modi nicht wesentlich unterschiedlich ist. Die aktuelle Version unterstützt auch alle modernen Intel-Mikroarchitekturen, die für einen professionellen Softwareentwickler von Nehalem bis Broadwell interessant sein könnten.

Die Ausgabe, die Sie von diesem Tool erhalten, zeigt Ihnen an, auf welchen Ports eine bestimmte Anweisung ausgeführt werden kann und wie viele μops diese Anweisung in der angegebenen Mikroarchitektur zerlegt wird.

Das ist so nah wie Sie zu einer direkten Antwort auf Ihre Frage erhalten, da als , die genauen μops, dass jeder Befehl zerlegt wird, von Intel absichtlich geheim gehalten werden. Sie sind nicht nur ein proprietäres Geschäftsgeheimnis, sondern Intel will frei sein, wie es von einer Mikroarchitektur zur anderen funktioniert. In der Tat jedoch wie viele μops eine Anweisung zerlegt, ist alles, was Sie jemals wissen möchten, wenn Sie Code optimieren. Es spielt keine Rolle welche μops die Anweisung zerlegt zu.

Aber ich würde noch einmal einen Teil von Peter Cordes's answer wiederholen: "Es ist leicht zu erraten in einigen Fällen, obwohl". Wenn Sie diese Art von detaillierten Informationen für jede Anweisung, die Sie in Betracht ziehen, nachschlagen müssen, werden Sie eine Menge Zeit verschwenden. Sie werden sich auch wahnsinnig machen, denn wie Sie bereits wissen, variiert es von Mikroarchitektur zu Mikroarchitektur. Der eigentliche Trick besteht darin, ein intuitives Gefühl dafür zu bekommen, welche Anweisungen in der x86-ISA "einfach" und welche "komplex" sind. Es sollte ziemlich offensichtlich sein, wenn Sie die Dokumentation lesen, und dieses intuitive Gefühl ist wirklich alles, was die Optimierungsempfehlungen von Intel Ihnen aufzeigen. Vermeiden Sie "komplexe" (alte CISC-artige) Anweisungen wie LOOP, ENTER, LEAVE und so weiter. Zum Beispiel bevorzugen Sie DEC + JNZ über LOOP. Relativ gesehen gibt es nur eine kleine Minderheit von "klassischen" x86-Anweisungen, die zu mehr als ein oder zwei μops dekodieren. * Das Studieren der Ausgabe eines guten optimierenden Compilers wird Sie auch in die richtige Richtung führen, da Sie nie sehen werden, dass Compiler diese "komplexen" Anweisungen verwenden.

Etwas contra Peters Antwort, bin ich jedoch ziemlich sicher, dass der zitierte Abschnitt der Optimierung Handbücher Intel nicht Bezug auf die SIMD-Befehle sind. Sie sprechen über die CISC-Anweisungen der alten Schule, die in Mikrocode implementiert sind und die sie bereits gelöscht hätten, wenn sie sie nicht aus Gründen der Abwärtskompatibilität unterstützen müssten. Wenn Sie das Verhalten von SSE3's HADDPS benötigen, dann sind Sie wahrscheinlich besser dran mit HADDPS anstatt zu versuchen, es in "einfachere" Komponenten zu zerlegen. (Es sei denn natürlich können Sie Zeitplan diese Vorgänge besser, indem sie in keinem Zusammenhang Code Verschachtelung. Aber das ist furchtbar schwer in der Praxis zu tun.)


* Um ganz genau zu sein, da sie sicher sind, scheinbar einfache Anweisungen, die tatsächlich unter Verwendung von Mikrocode implementiert werden und zu mehreren μops zerlegen. Eine 64-Bit-Division (DIV) ist ein Beispiel. Wenn ich mich richtig erinnere, ist dies mikrocodiert mit etwa 30-40 μops (variabel). Dies ist jedoch keine Anweisung, die Sie vermeiden sollten. Dies zeigt, dass die Handbücher von Intel hier sehr allgemein gehalten sind. Wenn Sie eine Division durchführen müssen, verwenden Sie DIV. Natürlich bevorzugen nicht Divisionen bei der Optimierung für die Geschwindigkeit zu tun, aber versuchen Sie nicht, Ihre eigenen Division Algorithmus zu schreiben, nur um die mikrocodierte DIV zu vermeiden, entweder!

Die andere große Ausnahme sind hier die string instructions. Der Performance-Kalkül für diese ist etwas komplizierter als "vermeiden, weil sie zu mehreren μops dekodieren".

, Glück eine Sache ist einfach: nie die Zeichenfolge Anweisungen ohne ein REP Präfix verwenden. Das ist einfach nicht sinnvoll, und Sie erhalten eine wesentlich bessere Leistung, wenn Sie die Anweisung selbst in die einfacheren Komponentenanweisungen zerlegen - zum Beispiel MOVSB + INC/DEC ESI + INC/DEC EDI.

Wo es etwas schwieriger zu entscheiden ist, wenn Sie beginnen, die Vorteile des REP Präfix zu nutzen. Obwohl dies dazu führt, dass die Anweisung in viele μops dekodiert, ist es manchmal noch effizienter, die wiederholten String-Anweisungen zu verwenden, als die Schleife manuell selbst zu codieren. Aber nicht immer. Es gab viele Diskussionen zu diesem Thema bereits auf Stack Overflow und anderswo; siehe zum Beispiel this question.

Eine detaillierte Analyse geht über den Rahmen dieser Antwort wirklich, aber meine schnelle Faustregel ist, dass Sie über REP LOADS vergessen, REP SCAS und REP CMPS vollständig. Auf der anderen Seite sind REP MOVS und REP STOS nützlich when you need to repeat a reasonably large number of times. Verwenden Sie immer die größte mögliche Wortgröße: DWORD auf 32-Bit, QWORD auf 64-Bit (aber beachten Sie, dass on modern processors, you may be better off using MOVSB/STOSB, since they can move larger quantities internally. Und auch wenn alle diese Bedingungen erfüllt sind, wenn Ihr Ziel Vector Anweisungen zur Verfügung hat, möchten Sie wahrscheinlich überprüfen, dass es würde nicht schneller sein, den Zug/Speicher mit Vektor bewegt. ein Geschäftsgeheimnis zu implementieren und für jede Mikroarchitektur

Siehe auch Agner Fog's general advice on page 150.

Verwandte Themen