22 Performance
22.010 Was muss ich über Performance grundsätzlich wissen ?
Grundsätzliche Aussagen gibt das (englischsprachige) Buch OpenGL on Silicon Graphics Systems
in den Kapiteln 11 bis 14, dass auf den
Webseiten von SGI zu finden sein sollte.
Auch wenn einige Informationen speziell auf SGI-Maschinen zugeschnitten sind, sind die meisten
Hinweise allgemein für OpenGL-Programmierer sehr hilfreich.
Zunächst sollte man die echten Schwachstellen eines Programms eingrenzen: Am Beispiel
einer Datenbank, die etwa 5% der Rechenzeit zum Durchsuchen des Datenbestands und 95%
für die Weitergabe der Daten über das Netzwerk benötigt, gelingt es einem
Programmierer, die Zeit zur Suche im Datenbestand um 50% zu reduzieren. Nach der Optimierung zeigt
sich aber kein sichtbar schnellerer Programmlauf.
Auch wenn die Änderungen im Quellcode nicht viel Zeit in Anspruch nehmen, einen sichtbaren Erfolg
bringt eine derartige Herangehensweise nicht. Die Lokalisierung der sogenannten
Bottlenecks (=Flaschenhälse) muss also immer der erste Schritt der Optimierung sein.
Grafische Anwendungen sind in vielen Bereichen nur mit hohem Rechenaufwand realisierbar.
Es lassen sich drei Kathegorien finden: CPU-Auslastung, Geometrie-Berechnung und Grafikkarte
(CPU limited, geometry limited, fill limited).
Die CPU-Auslastung beschreibt zum einen die (fehlende) Rechengeschwindigkeit des Hauptprozessors,
aber auch die unmittelbar damit zusammenhängenden Baugruppen des Rechners (Speicherbus, Cache, Hauptspeicher).
Hier ist es möglich, leistungsfähigere Komponenten (schnellere CPU, mehr RAM) zu verwenden oder
das Programm entsprechend weniger anspruchsvoll zu entwerfen.
Durch die Geometrie-Berechnung gebremst wird ein Programm, wenn die Matrizenoperationen
(Transformationen, Clipping, Beleuchtung, Culling...) die meiste Rechenzeit beanspruchen.
Auf preiswerteren Systemen wird diese Berechnung noch durch die CPU erledigt, so dass
hier kein echter Unterschied zwischen dem CPU limited und geometry limited besteht. Erst bei
Vorhandensein eines Geometrie-Prozessors auf der Grafikkarte macht diese Unterscheidung Sinn.
Eine durch die Grafikkarte gebremste Anwendung kann so viele Pixel in den
Framebuffer schreiben, wie durch die CPU bzw. Geometrie-Berechnung bereitgestellt werden.
Hier hilft es, die Anzahl der Pixel zu begrenzen (kleineres Fenster) oder die Pixel weniger
aufwendig darzustellen (einfaches Shading etc.).
Gerade die letzte Engstelle ist leicht zu bestimmen: Erhöht sich die Framerate bei verkleinertem Fenster,
ist die Anwendung meist fill limited.
Bleibt die Programmgeschwindigkeit annähernd gleich, liegt die Ursache in der CPU bzw. Geometrie-Berechnung.
Zur weiteren Eingrenzung kann man eine vorausberechnete, statische Szene mehrfach darstellen.
Erhöht sich die Framerate jetzt, wird der grösste Teil der Rechenleistung für Aufbereitung der
Daten und nicht für die Transformation etc. verwendet. In diesem Fall muss also der Bereich des
Programms optimiert werden, der sich noch nicht direkt mit der Darstellung sondern mit anderen Berechnungen
beschäftigt.
Lassen sich CPU und fill limited ausschliessen, liegt der Flaschenhals des Programms in der Geometrie-Berechnung.
Hier muss optimiert werden, entweder durch Mehrfachnutzung von Vertices (z.B. mit Triangle Strips) oder vereinfachter
der Darstellung (weniger Lichtquellen, Vertices etc.).
22.020 Wie kann ich die Performance meines Programms bestimmen ?
Der übliche Weg besteht darin, die Systemzeit festzustellen, einige Frames
ablaufen zu lassen und dann wiederum die Systemzeit abzufragen. Teilt man die Anzahl
der Frames (kompletten Durchläufe einer Szene) durch die gemessene Zeit, ergibt
sich der Vergleichswert in fps (frames per second). Je mehr Frames dargestellt werden konnten, desto
genauer wird das Ergebnis.
Will man die Anzahl der dargestellten Primitive bzw. Dreiecke je Sekunde bestimmen,
muss ein entsprechender Zähler installiert werden (was im Nachhinein ziemlich kompliziert werden
kann). Besteht diese Absicht von Anfang an, sollte man das Programm schon entsprechend
aufbauen.
Noch schwieriger wird die Anzahl der Pixel je Sekunde. Hier sollte nach Möglichkeit ein
Primitive mit bekannter Anzahl an Pixeln zum Einsatz kommen und gezählt werden.
Es gibt aber auch freie Benchmark Programme im Netz, z.B. bringt GLUT 3.7 das
Programm progs/bucciarelli/gltest mit oder man kann sich auf den Seiten der
Standard Performance Evaluation Corporation
umsehen.
22.030 Welches Primitive wird am schnellsten gerendert ?
GL_TRIANGLE_STRIP gilt als das am besten optimierte Primitive. Vorteile erzielt man aber
nur dann, wenn das Programm im Bereich der Geometrie-Berechnung die längste Rechenzeit
beansprucht.
22.040 Was für Nachteile haben redundante Aufrufe ?
Obwohl einige OpenGL Implementationen durch redundante Aufrufe nur wenig ausgebremst werden,
sollte man diese grundsätzlich vermeiden. Ein tatsächliche Geschwindigkeitsverlust
lässt sich weder genau abschätzen noch ausschliessen.
22.050 Ich nutze derzeit (n) Lichtquellen. Sobald ich (n+1) Lichtquellen einsetze,
bricht die Geschwindigkeit meines Programms erheblich ein. Warum ?
Normalerweise wird nur ein Teil der maximal möglichen Lichtquellen in Hardware
unterstützt. Bei (n+1) eingesetzten Lichtquellen hat man diese Grenze überschritten
und OpenGL berechnet die Beleuchtung nur noch in Software. Kommt man nicht mit weniger
Lichtquellen (n) aus, hilft nur noch eine leistungsfähigere Grafikkarte.
22.060 Ich nutze derzeit (n) Texturen. Sobald ich (n+1) Texturen einsetze,
bricht die Geschwindigkeit meines Programms erheblich ein. Warum ?
Jede Grafikkarte hat nur einen begrenzten Texturspeicher. In dem angesprochenem Fall
reicht der Texturspeicher zwar noch für (n) Texturen, (n+1) Texturen passen aber nicht
mehr hinein. Jetzt beginnt die OpenGL, bestimmte Texturen von dem Texturspeicher der
Grafikkarte in den Hauptspeicher des Rechners auszulagern. Dieser Vorgang (über den
Speicherbus) ist langsam, genauso wie der Hauptspeicher selbst langsamer als der Speicher
auf der Grafikkarte ist.
Hier sollte man versuchen, Texturen mit kleinerer Auflösung zu verwenden und gewisse
Abstriche in der Bildqualität in Kauf zu nehmen.
22.070 Warum ist glDrawPixels() und glReadPixels() so langsam ?
Während die Geschwindigkeit des 2D-Pfads (also der angesprochenen Befehle) auf
vielen teuren Systemen durchaus akzeptabel ist, wird insbesondere bei preiswerteren
Karten auf eine entsprechende Optimierung aus Zeit- und Kostengründen meist
verzichtet. Nimmt man den Stand Anfang 2000, kann man bei Grafikkarten unter 1.000 DM
kaum eine Optimierung des 2D-Pfads erwarten.
Falls eure Grafikkarte eigentlich schnell genug sein sollte, aber trotzdem nicht den
Erwartungen entspricht, kann man folgende Tipps versuchen:
Alle Statusvariablen von glPixelTransfer() sollten auf den Standardwerten bleiben.
Das gleiche gilt für glPixelStore(), mit Ausnahme von GL_PACK_ALIGNMENT und
GL_UNPACK_ALIGNMENT, deren Werte auf 8 gesetzt werden sollten. Die Datenfelder
sind dann als Double-Word (8 Byte) bereitzustellen.
Die Parameter von glDrawPixels() und glReadPixels() sollten auch auf den genutzten
Framebuffer abgestimmt werden. Ein 24-Bit Framebuffer mit weiteren 8-Bit Alpha (insgesamt also 32 Bit) erfordert
dann als Parameter GL_RGBA (4 Komponenten) und GL_UNSIGNED_BYTE (mit jeweils 8 Bit).
Wird auf eine derartige Abstimmung verzichtet, muss die OpenGL eine geeignete Umrechnung
der Daten für die Übernahme in den Framebuffer vornehmen, was entsprechend aufwändig ist.
Vor allen Dingen sollte man aber nicht vergessen, dass das vorhandene System (Systembus, Speicher)
keine unbegrenzte Geschwindigkeitssteigerung ermöglicht. Die Erwartungen sollten bei diesen
Operationen also auch nicht zu hoch angesetzt werden.
22.080 Was bringt mehr Geschwindigkeit, relative oder absolute Koordinaten ?
Arbeitet man immer mit direkten Koordinaten (also im Weltkoordinatensystem),
kann man auf viele Transformationen verzichten, der Rechenaufwand vermindert sich
also. Andererseits lassen sich gleichartige Objekte in eigenen (relativen) Koordinaten
(Modellkoordinatensystemen) mehrfach verwenden, also Speicherplatz sparen.
Ein Beispiel soll die Entscheidung erleichtern:
Angenommen sei die Darstellung eines Hotels, mit sehr vielen gleichartigen Räumen,
Türen, Türknöpfen, Lampen etc.
Eine Möglichkeit besteht nun darin, jedes Objekt nur einmal anzulegen und dann
mit geeigneten Modelltransformationen an den gewünschten Platz zu verschieben.
Der erforderliche Speicherplatz ist gering, der Rechenaufwand hoch.
Als Alternative kann man jedes Objekt einzeln darstellen, mit einer entsprechenden
Anpassung der (jetzt absoluten) Koordinaten. Durch den deutlich verringerten Rechenaufwand
ist zwar ein Performance-Schub zu erwarten, allerdings wird der Speicherverbrauch sehr hoch.
Muss das System jetzt Speicher auf die Festplatte auslagern, wird die Gesamtperformance
deutlich einbrechen, auch wenn zunächst Rechenzeit gespart werden konnte.
Insgesamt gesehen gibt es also keine eindeutige Antwort auf die Frage. Es hängt alles
der jeweiligen Situation ab. Die beste Lösung lässt sich z.B. durch
Benchmarking ermitteln.
22.090 Was ist schneller, Display Listen oder Vertex Arrays ?
Die Antwort hängt vom genutzten System ab.
Wird die Performance nicht durch die Geometrie-Berechnung vermindert (sondern durch CPU oder Pixelrate),
ist ein Unterschied zwischen Display-Listen oder Vertex Arrays nicht zu erwarten.
22.100 Wie kann ich einzelne Dreiecke am besten zu Triangle Strips zusammenfassen ?
Wie schon in Frage 22.030 gesagt, stellt GL_TRIANGLE_STRIP das
höchstoptimierte Primitive dar. Enthält das Programm einzelne Dreiecke, die
auch gemeinsame Eckpunkte teilen, lassen sich diese meist auch als Triangle Strips
zusammenfassen und so schneller darstellen.
Wie dieses Zusammenfassen realisiert werden kann, hängt aber von den jeweiligen
Daten ab. Findet man keinen geeigneten Algorithmus, kann man auch entsprechende Tools
im Internet suchen (z.B. STRIPE).
|