Zum 3DCenter Forum
Inhalt




NV40-Technik im Detail
Teil 2: Die Ausnutzung der NV40-Möglichkeiten

28. Oktober 2004 / von aths / Seite 4 von 5


   Bessere Ausnutzung

Im letzten Teil ging es darum, wie Nvidia es schaffte, die Pixelshader-Geschwindigkeit einer einzelnen Pipeline deutlich zu steigern, ohne im Vergleich zum NV35 zusätzliche Transistoren zu benötigen: Zwar hat eine Pipeline jetzt weniger Recheneinheiten, aber pro Takt können mehr dieser Einheiten genutzt werden. Das erfordert eine sehr fortschrittliche Crossbar-Logik. Die Realisierung der NV40-Pipeline ist ein Meisterstück der Ingenieurskunst. Natürlich ist das nicht vom Himmel gefallen, solche Hardware wurde nur möglich, weil Nvidia vorher Praxis-Erfahrung sammeln konnte.

Was ist die NV40-Pipeline wirklich? Es sieht so aus, als wäre der NV30-Shadercore in der Mitte aufgespaltet worden. Die TEX-Einheit ist an den ersten Teil (sprich Shader-Einheit 1) angebunden worden, während der andere Teil (Shader-Einheit 2) ein zusätzliches MUL spendiert bekam. Während NV30 noch zwei TEX-Einheiten pro Pipe hat, kommt NV40 mit einer einzelnen dedizierten Textur-Einheit pro Pixelpipe aus.

NV30 unterstützt ein schnelles RSQ_PP (z. B. beim NRM_PP praktisch anwendbar), beim NV40 ist das komplette NRM_PP beschleunigt worden. Normalisierung wird oft benötigt, um gutaussehende Bumpmapping-Effekte zu rendern. Der größte Vorteil gegenüber der NV30-Pipeline ist natürlich die Implementierung von Co- und Dual-Issue. Die zusätzliche FPU vom NV35 wird nicht länger gebraucht, um eine passable Leistung zu gewährleisten.


   Mehr Details zu den NV40-Fähigkeiten

Für PS_3_0 gibt es noch immer keine DIV-Instruktion, eine Division muss "von Hand" ausgeführt werden. Und zwar mit RCP (Reziproke) und MUL (da a/x = 1/x * a = RCP(x) MUL a). Die NV40-Pipeline kann dennoch ein DIV in einem Takt anbieten: Einheit 1 kann sowohl RCP als auch MUL in einem Takt berechnen. Der Optimizer muss beim Auftreten von RCP und MUL die mögliche Bedeutung von "DIV" erkennen, um die Instruktionen in einen Slot zu packen.

Der gleiche Slot kann sogar noch mehr aufnehmen, weil Einheit 2 eine MAD-Operation oder ein Skalarprodukt ausführen kann, so dass sich noch ein volles Vektor4-Skalarprodukt reinpacken lässt. Oder ein weiteres skalares MUL und ein unabhängiges ADD, da auch Einheit 2 Co-Issue-fähig ist. Man sieht schnell, dass der Optimizer eine schwierige Aufgabe hat. Natürlich hat auch der NV40 seine Grenzen.

Normalisierung in voller Genauigkeit (mit DP, RSQ, MUL) benötigt mehrere Zyklen, da RSQ alleine schon zwei Takte beansprucht. Dennoch, während das DP berechnet wird, kann Einheit 1 in der Zwischenzeit anderweitig ausgelastet werden, zum Beispiel eine Multiplikation ausführen oder eine Textur-Operation starten, sowie ein RCP im gleichen Takt zu berechnen.

Man muss allerdings sagen, dass in einigen Fällen alleine der Datenfluss bestimmte Teile einer Shader-Einheit blockieren wird, was beim Optimieren natürlich auch berücksichtigt werden sollte.


   Der Compiler

Um sowohl Co- als auch Dual-Issue bestmöglich auszunutzen, benötigt man einen guten Shader-Compiler, um die Instruktionen so zu sortierteren, dass die Pipeline bestmöglich ausgelastet werden kann. Gewisse Vorarbeit dafür leistet der DirectX Shader-Compiler. Im Gegensatz zu DirectX8-Pixelshaderprogrammen wird Co-Issue nicht direkt im Shader-Compilat für Pixelshader 2.0 und höher unterstützt. Der Compiler-Output in Assemblerform wird sowieso in keinem Fall "wörtlich" ausgeführt. Zunächst muss das Programm in Befehle übersetzt werden, die der Grafikchip versteht. Während dieses Prozesses (was Aufgabe des Treibers ist) kommt wieder eine Optimierungs-Phase zum Zuge, um die Spezialfertigkeiten des Chips nutzen zu können.

Während der NV40 eine gute Crossbar-Logik hat, um die Daten flexibel zu rangieren, sind die Recheneinheiten weiterhin nur in einer bestimmten Reinenfolge ansprechbar. Eine Pipeline zu bauen, die imstande wäre, ihre internen Einheiten umzusortieren, würde viele neue Probleme aufwerfen: Höhere Latenzen, höherer Transistorverbrauch - und am Ende ist es noch schwieriger, auf so eine Pipeline zu optimieren.

Der NV40 ist nach wie vor eine GPU, die keine echte Steuerlogik hat, welche die Instruktions-Reihenfolge optimieren könnte. Stattdessen wird sie mit VLIWs (very large instruction words) angesteuert, so dass die Aufgabe, die beste Performance aus dem Chip zu holen, weiterhin von der Software übernommen wird. Das spart eine Menge Transistoren in der GPU.

Es kann sein, dass die Zahl der in einem Takt ausführbaren Befehle stärker limitiert ist als wir wissen, weil die Crossbar-Logik vielleicht nicht flexibel genug ist. Zusätzlich gibt es für jeden Takt eine Grenze, auf wie viele Register zugegriffen werden kann. Soweit wie wir bisher wissen, sind das zwei Temp-Register oder Konstanten, sowie ein einziger Interpolator. Konstanten kommen übrigens über den Instruction Flow in die Pipeline. Außerdem kann die Pipe auf bestimmte interne temporäre Register zurückgreifen, um effektives Caching von Daten zu ermöglichen. Davon guten Gebrauch zu machen, ist ebenfalls Aufgabe des Shader-Optimizers.


   Mehr Details zur Optimierung

Die Umsortierung der Shader-Befehle, ohne dabei den mathematischen Ausdruck zu ändern, muss sehr schnell vonstatten gehen. Da wir keine offiziellen detaillierten Informationen darüber haben, was der Optimizer wirklich alles kann, bieten wir stattdessen einige Vermutungen an.

Wir nehmen an, dass der Shader zunächst in voneinander abhänge Stücke aufteilt wird, um danach in einem weiteren Durchlauf die Befehle neu zu sortieren. Einige Schritte hierfür finden vielleicht schon während der Übersetzung statt, wo der Pixelshadercode in die intern genutzten Befehle übertragen wird. Vielleicht wird ein erster Optimierungs-Durchlauf sogar noch auf den originalen PS-Assembler-Code angewendet.

Die Co- und Dual-Issue-Fähigkeit kann nur dann vernünftig ausgenutzt werden, wenn die richtigen Befehle in der richtigen Reihenfolge kommen (um sie dann zu entsprechend mächtigen VLIWs zusammenzufassen). Um eine solche Ausführungsreihenfolge sicherzustellen, muss der Treiber prüfen, welche Instruktionen alle innerhalb eines einzigen Taktes abgearbeitet werden können.

Um die Pipeline bestmöglich auszunutzen, müssen auch die Register optimiert zugeteilt werden. Weiterhin sollte der Optimizer feststellen, wann immer Teile eines Registers (z. B. die A-Komponente) ungenutzt sind, und ob dies nicht ermöglicht, in dieser Komponente im gleichen Takt eine andere Operation auszuführen.

Wir wissen nicht sicher, ob eine NV40 Shader-Einheit pro Takt zwei MUL2-Instruktionen auf den jeweils gleichen Kanälen (z. B. R und G) ausführen kann, aber wir nehmen an, dass das dank der flexiblen Crossbar möglich ist. Einige Befehlsketten können durch weniger Befehle ersetzt werden, dies funktioniert in bestimmten Fällen jedoch nur innerhalb eines gewissen Wertebereiches. Das muss der Optimizer dann erkennen können. Zum Beispiel begrenzen viele Texturformate die möglichen Werte auf einen Bereich zwischen 0 und 1. Ein MUL_SAT, gefolgt von einem ADD_SAT, kann dann durch ein MAD_SAT ersetzt werden.

Der NV40 unterstützt (genau wie schon der NV30) freie Swizzles. Das heißt, Komponenten eines Registers (R, G, B und A-Kanäle) können frei umsortiert werden. Das ist mit ein Hauptgrund, warum komplexe Shader für das 2_A- oder 3_0-Profil kürzer sind als für das 2_0 oder 2_B-Profil. Die Radeon, die nur bestimmte Swizzle-Muster anbietet, kann oft nur mit Hilfe mehrerer Swizzles das gewünschte Ergebnis erreichen. Ob der Optimizer im NV40-Treiber auch Swizzles zusammenfassen kann, wissen wir nicht, aber innerhalb eines überschaubaren Rahmens sollte das möglich sein.






Kommentare, Meinungen, Kritiken können ins Forum geschrieben werden - Registrierung ist nicht notwendig Zurück / Back Weiter / Next

Shortcuts
nach oben