Warum müssen Engines für neue Prozessoren derselben Architektur optimiert werden?

39

Bei der Veröffentlichung einer neuen Prozessorgeneration geben die meisten Websites an, dass Game-Engines und -Programme für die neue Hardware optimiert werden müssen. Ich verstehe nicht ganz warum. Ein Prozessor hat normalerweise eine Architektur, die definiert, welche Art von Befehlssatz er verwendet. Die, die wir heutzutage alle verwenden, ist amd_x86_64. Warum müsste ein Programm oder ein Compiler aktualisiert werden, wenn alle Prozessoren dieselbe Architektur verwenden? Sicher gibt es Funktionen IN der Pipeline des neuen Prozessors, die die Ausführung von Maschinencode optimieren. Warum müsste der Maschinencode selbst geändert werden, wenn die Architektur dies nicht tun würde?

salbeira
quelle
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Josh
14
„Not“ ist eine falsche Formulierung, und mehr Marketing als Wahrheit, viel in der gleichen Weise wie zB Windows benötigt eine gewisse neue CPU - Generation zu unterstützen (oder nicht, wie im Fall von Windows 7, die im Prinzip funktionieren würden perfekt gut mit zB Ryzen, außer dass 3-4% mehr Energie verbraucht wird als nötig). Bei dieser Optimierung geht es nur darum, etwas mehr aus der CPU herauszuholen und dem Maximum näher zu kommen. Realistisch gesehen kann es sein, dass Sie in ungeklärten Beispielen aufgrund unterschiedlicher Zeitplanung und Verwendung einiger neuer Anweisungen insgesamt 1-2% gewinnen können.
Damon
2
Nur weil zwei Prozessoren die gleichen Vorgänge ausführen können, bedeutet das nicht, dass die Vorgänge auf beiden Prozessoren die gleiche Leistung haben ...
Mehrdad
Eine verwandte Frage von mir zu Stack Overflow: Wie funktioniert mtune tatsächlich?
Marc. 2377

Antworten:

54

Weil verschiedene Generationen derselben Architektur unterschiedliche Befehlssätze haben können .

Beispielsweise sind Streaming-SIMD-Erweiterungen wahrscheinlich der bekannteste x86-Befehlssatz, und obwohl es nur eine x86-Architektur gibt, gibt es SSE, SSE2, SSE3 und SSE4.

Jede dieser Generationen kann neue Anweisungen enthalten, mit denen bestimmte Vorgänge schneller ausgeführt werden können. Ein Beispiel, das für Spiele relevant wäre, könnten Produktanweisungen sein.

Wenn eine Game Engine für eine frühere Generation einer Architektur kompiliert wurde, werden diese neueren Anweisungen nicht unterstützt. Ebenso kann es erforderlich sein, den Motor für neuere Anweisungen zu optimieren. Beispielsweise unterstützt SSE4 Skalarproduktanweisungen, die mit Array-of-Structs-Daten arbeiten. Eine Optimierung, die diese neueren Anweisungen nutzen könnte, besteht darin, Ihr Datenlayout in ein Array von Strukturen zu ändern.

Maximus Minimus
quelle
1
@Panzercrisis - danke für den Bearbeitungsvorschlag. Um es klar auszudrücken: Die ursprüngliche Frage betraf nicht Ihren eigenen Code, sondern den Engine-Code. "Optimieren Sie Ihren eigenen Code" ist also kein guter Bearbeitungsvorschlag. Es zeigte sich jedoch, dass ich klarstellen musste, dass ich, als ich "optimieren" sagte, "den Motorcode optimieren" meinte, also habe ich das bearbeitet, um es aufzunehmen.
Maximus Minimus
37

Maximus 'Antwort ist richtig, ich möchte nur einen weiteren Teil der Geschichte erzählen:

Die Hardware selbst ändert sich auf eine Art und Weise, die Sie ändern müssen, unabhängig von den neu eingeführten Anweisungen.

  • Erhöhte oder verringerte Cache-Mengen bedeuten, dass Sie sich weniger oder mehr Gedanken über Probleme bei der Cache-Optimierung / -Unwirksamkeit machen müssen. Mehr Cache bedeutet, dass Sie sich bei kleinen Daten weniger darauf konzentrieren können, sicherzustellen, dass die Daten zusammenhängend sind, ohne dass Leistungsprobleme auftreten. Weniger Cache bedeutet, dass dies ein Problem sein könnte, und sehr wenig Cache bedeutet, dass es bei einigen großen Datenstrukturen in keiner Weise wichtig ist.

  • Neue Cache-Ebenen bedeuten, dass Sie sich mehr Gedanken darüber machen müssen, wie Sie noch größere Datenmengen organisieren (L1, L2, L3, L4).

  • Mehr Kerne bedeuten, dass Sie überlegen müssen, wie Sie Multithread-Anwendungen besser nutzen und wie sich Ihre Anwendung in einer Umgebung mit mehreren Prozessen skalieren lässt.

  • Schnellerer Takt bedeutet, dass Sie sich mehr Gedanken über die Speicherlatenz machen müssen als über die Geschwindigkeit der CPU-Berechnung als einen Engpass Ihres Systems.

  • Die Anzahl der FPUs auf einem System stimmt möglicherweise nicht mehr mit der Anzahl der ganzzahligen ALUs pro Kern überein (AMD hatte / hat Architekturen wie diese).

  • Die Anzahl der Taktzyklen, die zur Berechnung einer Operation benötigt werden, ist gesunken oder gestiegen.

  • Die Anzahl der verfügbaren Register wurde geändert.

Alle diese Faktoren wirken sich sehr effektiv auf die Leistung von Programmen aus, bei denen Annahmen über die zugrunde liegende Architektur früherer Hardware mit demselben ISA (positiv oder negativ) getroffen wurden.

opa
quelle
"Erhöhte oder verringerte Cache-Ebenen bedeuten, dass Sie sich weniger Gedanken über die Cache-Kohärenz machen müssen." - Praktisch jede CPU ist cachekohärent. Meinst du falsches Teilen? Selbst als praktisch jede CPU $ -Linie ist fast immer 64 B ...
Maciej Piechotka
1
Maciej hat gerade Ihre Aussage zur Cache-Kohärenz getroffen :) Sie meinten wahrscheinlich "Cache-Optimierung" oder so. Cache-Kohärenz ist die Fähigkeit eines Systems, eine konsistente Speicheransicht für die Software transparent zu halten, selbst wenn N unabhängige Caches vorhanden sind. Dies ist völlig orthogonal zur Größe. TBH die Aussage ist nicht wirklich relevant, aber Ihre Antwort (insbesondere die Punkte 5 und 6) befasst sich besser mit der Frage als die akzeptierte IMO :) Vielleicht wird sie durch Hervorheben des Unterschieds zwischen Architektur und U-Architektur mehr hervorgehoben.
Margaret Bloom
4
"Wie bei der Multiplikation, die mehr Zeit benötigt als die Addition, wo sie wie heute bei modernen Intel- und AMD-CPUS-Systemen die gleiche Zeit benötigt." Das ist nicht alles. In Pipeline-Architekturen muss zwischen Latenz (wenn das Ergebnis fertig ist) und Durchsatz (wie viele können Sie pro Zyklus ausführen) unterschieden werden. Int auf modernen Intel-Prozessoren hat einen Durchsatz von 4 und eine Latenz von 1. Multiplizieren Sie hat Durchsatz 1 und Latenz 3 (oder 4). Dies sind die Dinge, die sich mit jeder Architektur ändern und optimiert werden müssen. ZB pdepdauert 1 Zyklus auf Intel, aber 6 auf Ryzen, so kann es sein, dass Sie es nicht auf Ryzen verwenden möchten.
Christoph
2
@Clearer Ich weiß, wir reden hier über CPUs, aber Sie haben noch nie für GPUs programmiert, oder? Derselbe Code führt zu so unterschiedlichen Ergebnissen bei der Leistung, dass Sie häufig gezwungen sind, die Hardwarefunktionen in CUDA zu berücksichtigen . Deshalb muss die Cachegröße (gemeinsam genutzter Speicher, verwalteter L1-Cache) tatsächlich berücksichtigt werden, wie Sie etwas in CUDA codieren.
Opa
2
@Christoph ist richtig. Der Benchmark, den Sie verknüpfen, bezieht sich auf eine Schleife über ein Array c[i] = a[i] OP b[i](dh 2 Ladevorgänge und 1 Speicherplatz pro Operation), sodass die Zeiten aufgrund der sehr geringen Rechenintensität von der Speicherbandbreite dominiert werden. Die Array-Größe wird nicht angezeigt, daher IDK, wenn sie in L1D passt. ( gcc4.9 -OfastDiese Schleifen werden höchstwahrscheinlich automatisch vektorisiert, sodass Sie nicht einmal die Kosten für normale skalare Operationen als Teil eines komplexen Ganzzahlcodes messen). Die erste Zeile dieser Seite ist WICHTIG: Nützliche Rückmeldungen haben ergeben, dass einige dieser Maßnahmen schwerwiegende Fehler aufweisen. Ein großes Update ist in Vorbereitung .
Peter Cordes
2

Selbst über grobe Änderungen hinaus, wie die Unterstützung neuer Anweisungen, ändern die Mikroprozessorhersteller ständig ihre Entwürfe, um die Leistung zu verbessern, und jeder neue Entwurf kann für jede Anweisung oder Technik eine andere relative Leistung aufweisen. Vielleicht haben Sie sorgfältig optimierten branchless Code für das Modell X geschrieben, aber das Modell Y verfügt über einen verbesserten Branch-Predictor, der die Strafe für die falsche Vorhersage für die nicht-branchless Version des Codes reduziert (wodurch auch ein Register frei wird, das woanders verwendet werden kann). . Möglicherweise unterstützt das Modell Y eine größere Parallelität eines bestimmten Befehls mit hoher Latenz, so dass Sie mit einer entrollten Schleife dieses Befehls einen besseren Durchsatz erzielen, während beim Modell X eine kürzere Sequenz besser war.

Jedes Problem kann auf vielfältige Weise gelöst werden, und jedes Programm ist eine ineinandergreifende Sammlung von Kompromissen und Ressourcenzuweisungen unter dem Gesichtspunkt der Optimierung. Selbst kleine Änderungen der Verfügbarkeit dieser Ressourcen oder der Kosten eines bestimmten Codeteils in Bezug auf diese Ressourcen können einen Kaskadeneffekt haben, der dem einen oder anderen Codeteil einen erheblichen Leistungsvorteil verleiht. Selbst wenn ein aufgerüsteter Chip "mehr von allem" hat, wie viel mehr von jedem Ding kann die Waage schwingen.

hobbs
quelle