2012-03-31 4 views
29

Ich schreibe einen JIT-Compiler mit einem x86-Backend und lernen x86 Assembler und Maschinencode, wie ich gehe. Ich habe ARM Assembler vor etwa 20 Jahren benutzt und bin überrascht über den Unterschied in den Kostenmodellen zwischen diesen Architekturen.Moderne x86 Kostenmodell

Insbesondere Speicherzugriffe und Verzweigungen sind auf ARM teuer, aber die äquivalenten Stapeloperationen und Sprünge sind auf x86 billig. Ich glaube, dass moderne x86-CPUs viel mehr dynamische Optimierungen durchführen als ARM-Cores, und ich finde es schwierig, ihre Auswirkungen vorherzusehen.

Was ist ein gutes Kostenmodell beim Schreiben von x86-Assembler? Welche Kombinationen von Anweisungen sind billig und welche sind teuer?

Zum Beispiel wäre mein Compiler einfacher, wenn er immer die lange Form zum Laden von ganzen Zahlen oder zum Sprung zu Offsets erzeugt hätte, selbst wenn die ganzen Zahlen klein waren oder die Offsets schließen, aber würde dies die Performance beeinträchtigen?

Ich habe noch keinen Floating Point gemacht, aber ich würde gerne bald darauf kommen. Ist die Interaktion zwischen normalem und float-Code nicht offensichtlich?

Ich weiß, es gibt viele Referenzen (z. B. Michael Abrash) auf x86-Optimierung, aber ich habe eine Ahnung als alles andere als ein paar Jahre alt wird nicht für moderne x86-CPUs gelten, weil sie in letzter Zeit so viel geändert haben. Hab ich recht?

+1

An welcher x86-Implementierung sind Sie interessiert? – harold

+0

@harold Alles, was Sie heute in einem Laptop, Desktop oder Server finden würden. Also ich denke, SSE3 ist eine Selbstverständlichkeit. Ich hätte gerne generische Ratschläge zur Optimierung für alle von ihnen sowie Details über Überraschungen, die ich finden könnte, z. eine Anweisung, die auf dem Atom 10x langsamer ist. –

+1

Conroe und es Derivate (Nehalem, Sandy Bridge) sind so verschieden von Atom wie sie sich von ARM unterscheiden. Die Optimierungsgrundsätze sind dieselben wie beim P6, daher sind einige ältere Texte gültig. –

Antwort

33

Die beste Referenz ist die Intel Optimization Manual, die ziemlich detaillierte Informationen über Architekturgefahren und Befehlswartezeiten für alle aktuellen Intel-Kerne sowie eine gute Anzahl von Optimierungsbeispielen bietet.

Eine weitere hervorragende Referenz ist Agner Fog's optimization resources, die den Vorteil haben, auch AMD-Kerne abzudecken.

Beachten Sie, dass spezifische Kostenmodelle von Natur aus mikroarchitekturspezifisch sind. Es gibt kein "x86-Kostenmodell", das irgendeine echte Gültigkeit hat. Auf der Anweisungsebene unterscheiden sich die Leistungsmerkmale von Atom stark von i7.

Ich würde auch beachten, dass Speicherzugriffe und Verzweigungen nicht wirklich "billig" auf x86-Kernen sind - es ist nur, dass das Out-Of-Order-Ausführungsmodell so hoch entwickelt ist, dass es die Kosten von ihnen in vielen verbergen kann einfache Szenarien.

+1

Danke! "Die Leistungsmerkmale von Atom unterscheiden sich stark von i7". Kannst du etwas mit mehr Informationen dazu zitieren? –

+0

@JonHarrop mehr Informationen als in Agner Fog Microarchitectures Dokument? Ich würde mich wundern, wenn mehr Informationen veröffentlicht worden wären: – harold

+5

@JonHarrop: Ein moderner i7-Kern ist außer Betrieb und kann 4 Befehle pro Zyklus zurücknehmen. Ein Atom-Kern ist streng in Ordnung und kann unter idealen Umständen 2 Anweisungen pro Zyklus zurücknehmen, aber die Verwendung einiger Anweisungen beschränkt ihn auf nur 1 IPC. Dies alles ist sowohl in Intels Dokument als auch in Agners Notizen ausführlich beschrieben. Auf einer sehr hohen architektonischen Ebene ist Atom einem ARM Cortex-A8 ähnlicher als anderen modernen x86-Kernen. –

5

Torbjörn Granlunds Instruction latencies and throughput for AMD and Intel x86 processors ist auch gut.

bearbeiten

Granlund des Dokuments betrifft Befehlsdurchsatz im Zusammenhang mit, wie viele Anweisungen eines bestimmten Typs kann pro Taktzyklus ausgegeben werden, (i in parallell durchgeführt E). Er behauptet auch, dass die Dokumentation von Intel nicht immer genau ist.

3

Für das, was es wert ist, es verwendet, um ein erstaunlichen Buch "Inner Loops" by Rick Booth genannt werden, die im Detail beschrieben, wie manuell auf IA-86 Assembler-Code für Intels 80486, Pentium, Pentium Pro und Pentium MMX-Prozessoren Mikro-Optimierung, mit vielen nützlichen realen Code-Beispielen (Hashing, Verschieben von Speicher, Zufallszahlenerzeugung, Huffman- und JPEG-Komprimierung, Matrixmultiplikation).

Leider wurde das Buch seit seiner ersten Veröffentlichung im Jahr 1997 für neuere Prozessoren und CPU-Architekturen nicht aktualisiert.Trotzdem würde ich es immer noch als eine sanfte Einführung in die Themen empfehlen wie:

  • die Anweisungen im Allgemeinen sehr billig sind, oder billig, und welche nicht
  • die Register sind die vielseitigste (dh sie haben keine besondere Bedeutung/ist nicht das Standardregister einiger Anweisungen)
  • wie Befehle paaren, so dass sie parallel ausgeführt werden, ohne ein Abwürgen Pipeline
  • verschiedene Arten von Ständen
  • Verzweigungs-Vorhersage
  • Was Sie in Bezug auf Prozessor-Caches beachten sollten
+0

* wie man Anweisungen koppelt, so dass sie parallel ausgeführt werden, ohne eine Pipeline zu blockieren *: Die Optimierung für P5 in der richtigen Reihenfolge macht keinen Sinn für Code, der auf Out-of-Order-Kernen ausgeführt wird. Wenn Sie für Atom tunen, ist es vielleicht etwas ähnlich zu P5, wenn einige Anweisungen pairable und einige nicht sind. –

+0

Es wird jetzt so gefährlich, da sich die Dinge seit diesen Tagen so massiv verändert haben. Völlig neue Dinge wie Loop-Buffer und Micro-Op-Caching (IIRC), die noch nie zuvor existierten, sind Game-Changer. Plus SIMD und sehr, sehr leistungsfähiges ILP. –

+0

@CecilWard: Ich nehme deinen Standpunkt. Ich bin definitiv nicht mehr auf dem Laufenden über diese Dinge. (Aber das Buch * war * erstaunlich. ;-) – stakx

1

Es lohnt sich, sich die Backends bestehender Open-Source-Compiler wie GCC und LLVM anzusehen. Diese haben Modelle für Unterrichtskosten und auch anständige (aber idealisierte) Maschinenmodelle (zB Heftbreite, Cache-Größen, etc.).

1

Natürlich sind die Berichte von Agner Fog und das Intel® 64 und IA-32 Architectures Optimization Reference Manual notwendige und ausgezeichnete Referenzen. AMD hat auch eine Optimierung Handbuch:

  • Software-Optimierungs-Leitfaden für AMD Familie 15h Prozessoren

jedoch zwei Tools Intel in Verständnis wesentlich sind Codesequenzen:

  • Intel® Architektur Code-Analysator
  • Intel® VTune ™

IACA ist Ihr Kostenmodell. Ich verwende es unter OSX, aber VTune läuft nur unter Windows und Linux.

Sie können auch graben in die Intel Patentliteratur und verschiedene Papiere Intel besser zu verstehen, wie die Dinge funktionieren:

  • Die Next-Generation Intel Core Mikroarchitektur
  • Haswell: Die vierte Generation Intel Core-Prozessor
  • Mikrooperation Cache: eine Strom bewusst Frontend für ISA variable Befehlslänge
+0

IACA und VTune sind Werkzeuge, die man verwenden könnte, während man ein Kostenmodell * abstimmt *, aber tatsächlich die Verwendung von fork/executing IACA, um eine Folge von Anweisungen zu testen, scheint für zu langsam zu sein ein optimierender Compiler, der auf jedem Basisblock ausgeführt wird, es sei denn, er reserviert dies für Hot-Loops. VTune ist hauptsächlich ein Werkzeug zum Lesen von Leistungsindikatoren, was bedeutet, dass Sie die Anweisungen, die Sie gerade erstellen, tatsächlich ausführen müssen. Das funktioniert nur mit "-mtune = nativ"; Tuning für den Host, der das Kompilieren durchführt. –

+0

IACA ist ein statisches Analysewerkzeug. Dein Code läuft nicht einmal.Sie wickeln Code mit einem Präfix und Suffix und führen Sie das Tool IACA -64 -arch HSW -ignore wahr -Analyse LATENZ prog> lst % Makro START_MARKER mov ebx, 111 db 0x64, 0x67, 0x90 % endmacro % Makro END_MARKER mov ebx, 222 db 0x64, 0x67, 0x90 % endmacro – Olsonist

+0

ich habe vor IACA verwendet, auch gepostet SO Antworten einschließlich IACA Ausgang: P. Mein Punkt war, dass die Verwendung von * als * Ihr Kostenmodell bedeuten würde, dass der Compiler tatsächlich IACA für alle möglichen Implementierungen für eine Schleife aufruft. Da IACA Closed Source ist und nur als ausführbare Datei, nicht als Bibliothek, verteilt wird, müssten Sie eine Objektdatei und fork/exec IACA schreiben. (Ja, ich weiß, das ist nicht das, was Sie meinten, und das ist lächerlich. Nehmen Sie einfach Ihre Formulierung wörtlich: P) Upvoted für das Tuning im Allgemeinen, oder für das Tuning * des Kostenmodells eines Compilers. –

0

Ich schreibe einen JIT-Compiler mit einem x86-Backend und lerne x86 Assembler und Maschinencode, wie ich gehe.

Das wesentliche Problem hier ist, dass ein JIT-Compiler es sich nicht leisten kann, viel Zeit mit der Mikrooptimierung zu verbringen. Da das "Optimieren" zur Laufzeit stattfindet, müssen die Kosten für Optimierungen geringer sein als die durch die Optimierungen eingesparte Zeit (andernfalls wird die Optimierung zu einem Nettoverlust in der Leistung).

Für 80x86 gibt es mehrere verschiedene CPUs mit unterschiedlichen Verhalten/Eigenschaften. Wenn man die spezifischen Eigenschaften der CPU berücksichtigt, steigen die Kosten für die Optimierung und man stürzt direkt in eine "Kosten mehr als Gewinn" Barriere. Dies gilt insbesondere für Dinge wie "ideale Unterrichtsplanung".Die meisten (aber nicht alle) modernen 80x86-CPUs haben glücklicherweise verschiedene Funktionen (Out-of-Order, spekulative Ausführung, Hyper-Threading), um (teilweise) die durch "weniger als perfekte" Optimierung verursachten Performance-Kosten zu mindern. Dies führt dazu, dass teure Optimierungen weniger vorteilhaft sind.

Das erste, was Sie tun wollen, ist zu identifizieren, welche Teile des Codes optimiert werden sollten und welche Teile nicht sollten. Dinge, die nicht häufig ausgeführt werden (z. B. der Initialisierungscode "nur einmal ausgeführt"), sollten überhaupt nicht optimiert werden. Es sind nur häufig ausgeführte Stücke (z. B. innere Schleifen usw.), an denen es sich lohnt, zu stören. Sobald Sie ein Stück identifiziert haben, das es wert ist, die Frage zu optimieren, wird "wie viel?".

Als eine grobe Über-Generalisierung; Ich würde erwarten, dass (im Durchschnitt) 90% des Codes überhaupt keine Optimierung wert ist, und für 9% des Codes ist es nur eine generische Optimierung wert. Die verbleibenden 1% (die von einer umfangreichen Optimierung in der Theorie profitieren könnten) werden sich für den JIT-Compiler-Entwickler in der Praxis zu viel Mühe machen (und würde zu einem massiven Komplexitäts-/Prüfbarkeits-Albtraum führen - z. B. "Bugs, die nur existieren, wenn läuft auf einigen CPUs "Szenarien).