Optimieren der Vertexbuffer
-
hustbaer schrieb:
Mal ne blöde Frage: Wie böse kann man sich eigentlich die Performance töten wenn man genau counter-optimal sortierte Index-Buffer verwendet?
normalerweise macht man schon irgendwie zusammenhaengende mesches, da kommt im schnitt ein vertex pro triangle hinzu. was schon nicht schlecht ist. wenn du die caches kaputt machen wollen wuerdest, muestest du jedesmal ein komplett unabhaengiges triangle zeichnen und wuerdest ca 3mal mehr machen muessen.
es gibt mehrere caches und die sind alle recht klein und meistens so ausgelegt, dass sie, bei daten lokalitaet, gerade ausreichen damit die graka _nie_ stallt (bei normaler auslastung->theorie). das ist der grosse unterschied zu cpus den viele nicht kapieren. bei cpus ist der cache dafuer da zufaellige zugriffe im speicher moeglichst abzufangen. bei grakas sind die caches dafuer da die werte die gleich benoetigt werden, zwischen zu speichern. eine graka hat aber im normalbetrieb keine cachemisses, weil die daten vorher reingestreamt werden, bevor sie gebraucht werden (eben in die besagten caches), sie hat hoechstens eine groessere latenz, weil die daten ueberall laenger verweilen. durch doppelt soviel cache wuerdest du in 99% der faelle keine geschwindigkeitssteigerung erhalten.
deswegen, der post transform cache is so gross, dass bei der erwarteten vertexshaderauslastung trotzdem genug vertices fuer das trianglesetup bereitstehen. die Vertexshader sind in der menge da, dass sie zur gegebenen speicherbandbreite immer ausreichend 'gefuettert' werden koennen. der pre-transform-cache ist wiederrum so angelegt dass er in etwa zwei allignte oder einen unallignten vertexstream ausreichend zwischenspeichert damit die naechsten paar anfragen befriedigt werden.man muss sich das so ueberlegen. eine cpu weiss so ziemlich nie welche daten in zukunft benoetigt weren. einer GPU sind meist im voraus ALLE vertexdaten bekannt. sie koennte also immer schon den naechsten vertex bereithalten. das macht sie sogar auch.
aber da der speicher nicht nur fuer vertexdaten benutzt wird, hat sie mal die ganze bandbreite fuer vertexdaten, mal nur einen winzigen bruchteil, also gibt es einen kleinen cache in den zu guten zeiten viel abgelegt wird, in schlechten leert er sich langsam (das ist also der grund fuer den cache).
Nun denkt man sich, ok, was wenn ich riesige vertices habe, dann braucht doch der prefetcher auch vieeel laenger? ja das stimmt, aber dann macht der vertexshader vermutlich auch viel mehr weil er ja mehr daten wollte. Ist dann das trianglesetup nicht am idlen? vermutlich auch nicht, denn die mehr an daten werden auch an das tri-setup weitergeleitet und das muss nun viel laenger rechnen um ein dreieck aufzubauen.deswegen nennt man eine gpu auch einen streamingprozessor. so arbeite auch video decoder prozessoren, audio encoder, und deswegen sind diese meist auch soviel schneller als eine cpu bei deren spezieller arbeit, weil sie so ziemlich perfekt ausgerechnet sind, fuer ihren speziellen zweck.
aber entsprechend kann man auch sehen, wenn irgendetwas falsch gefuettert wird, bricht die ganze leistung ein, und wenn's nur dadurch kommt dass alle vertexdaten nicht lokal beieinander liegen, sondern jedes vertexattribut von einer komplett anderen stelle bezogen wird. dann kann es sein dass alle nachfolgenden pipelines idlen.
andersrum sieht man auch, dass in dieser kaskade von einheiten eine immer das bottleneck sein wird. bevor man also optimiert, sollte man _sehr_ genau wissen an welchem der hundert stellen von vertex zu pixel auf dem monitor das bottleneck ist, ansonsten sorgt man nur dafuer dass entweder ein cache noch schneller vollaeuft (wenn man vor dem bottleneck ist) oder schneller geleert wird (wenn man danach ist), die gesamtperformance aendert man damit dann nur maginal, obwohl man diese eine sache vielleicht 10mal schneller gemacht hat.
Manchmal kann man auch mit absicht dinge 'schlechter' machen, damit es insgesammt dann besser laeuft (ein paradebeispiel ist, dass man in shadern statt spruengen an if-else stellen, einfach beide faelle abarbeitet und sich dann am ende nur das richtige ergebniss schnappt -> doppelte arbeit, aber trotzdem schneller, weil einhetliche pipeline).(ich koennte noch die ganze nacht schreiben *hehe*)
-
Im Prinzip ist mir viel von dem was du da schreibst nicht neu.
Ich wollte aber bloss ne Zahl wissen. Ein Maximum anzugeben ist wahrscheinlich nicht möglich, aber vielleicht hat jmd. schon einen Fall gehabt wo er einen grossen Unterschied gesehen hat der nur durch "umordnen" der Index-Buffer entstanden ist. Dann könnte er/sie/es schreiben "Faktor 2.7 hab ich selbst schon gesehen" - oder sowasNun denkt man sich, ok, was wenn ich riesige vertices habe, dann braucht doch der prefetcher auch vieeel laenger? ja das stimmt, aber dann macht der vertexshader vermutlich auch viel mehr weil er ja mehr daten wollte.
Ich hätte eher an den Fall "mini vertices" gedacht, denn dann kann sich die Adresse von einem "fetch" zum nächsten jedesmal komplett ändern. Mag sein dass ich da noch in Grossvaters Zeiten feststecke, aber gibt's beim Zugriff auf RAMs nicht noch immer Strafe wenn man die "row" und/oder "bank" wechselt?
ein paradebeispiel ist, dass man in shadern statt spruengen an if-else stellen, einfach beide faelle abarbeitet und sich dann am ende nur das richtige ergebniss schnappt -> doppelte arbeit, aber trotzdem schneller, weil einhetliche pipeline
Ja, DAS ist mir von den CPUs her bekannt. Ist ja nicht allzulange her dass wir CPUs mit mörder-langen Pipelines hatten, wo ein branch-prediction-miss ordentlich teuer war. Bzw. es gibt diese CPUs ja immer noch
Deswegen war/ist auch der "conditional move" Befehl bei bestimmten Schleifen eine ganz feine Sache
-
hustbaer schrieb:
Im Prinzip ist mir viel von dem was du da schreibst nicht neu.
Ich wollte aber bloss ne Zahl wissen. Ein Maximum anzugeben ist wahrscheinlich nicht möglich, aber vielleicht hat jmd. schon einen Fall gehabt wo er einen grossen Unterschied gesehen hat der nur durch "umordnen" der Index-Buffer entstanden ist. Dann könnte er/sie/es schreiben "Faktor 2.7 hab ich selbst schon gesehen" - oder sowaswenn du rein vertexshader limitiert bist und hast den worst case von jeweils 3 neuen vertices (was in etwa bei particlen sein kann, da haste ja immer 4 die du danach nie wieder benutzt), gegenueber einem mesh, dass du im zickzack muster abarbeitest, was in etwa 0.5vertices pro dreieck bedeutet, kannst du es ziemlich mit faktor 6 ansetzen... wie gesagt, vertexshader fall.
Nun denkt man sich, ok, was wenn ich riesige vertices habe, dann braucht doch der prefetcher auch vieeel laenger? ja das stimmt, aber dann macht der vertexshader vermutlich auch viel mehr weil er ja mehr daten wollte.
Ich hätte eher an den Fall "mini vertices" gedacht, denn dann kann sich die Adresse von einem "fetch" zum nächsten jedesmal komplett ändern. Mag sein dass ich da noch in Grossvaters Zeiten feststecke, aber gibt's beim Zugriff auf RAMs nicht noch immer Strafe wenn man die "row" und/oder "bank" wechselt?
bei mini vertices ist die wahrscheinlichkeit kleiner dass du zwitchen musst. den penalty gibt es natuerlich immer noch, was eigentlich noch schlimmer geworden ist, weil mit jeder speichergeneration die bandbreite gesteigert wird, die latenz aber gleich bleibt. man ist also sehr auf lokalitaet angewiesen, jedoch arbeiten die gpu hersteller auch dran moeglichst viel in einem rutsch zu lesen (sprich, die cachelines werden laenger und laenger).
ein paradebeispiel ist, dass man in shadern statt spruengen an if-else stellen, einfach beide faelle abarbeitet und sich dann am ende nur das richtige ergebniss schnappt -> doppelte arbeit, aber trotzdem schneller, weil einhetliche pipeline
Ja, DAS ist mir von den CPUs her bekannt. Ist ja nicht allzulange her dass wir CPUs mit mörder-langen Pipelines hatten, wo ein branch-prediction-miss ordentlich teuer war. Bzw. es gibt diese CPUs ja immer noch
Deswegen war/ist auch der "conditional move" Befehl bei bestimmten Schleifen eine ganz feine Sachenaja, nicht ganz. eine cpu haette massig register hazards bei sowas wie
a=a*a; a=a*a; a=a*a; a=a*a; a=a*a;
und wuerde die ganze zeit die pipe stallen, cmove ist, wie du sagst, um die pipe nicht zu flushen.
einer gpu macht diese reihe nichts aus, entsprechend wuerde ein jump auch nicht die pipeline an sich stallen, aber eine gpu als streamprozessor lebt von einem geradelinigen datendurchlauf, generierst du nun unmengen verzweigte logikpfade, teilst du die daten auf die pfade auf. die gpu kann dafuer weder ein sonderlich gutes load balancing betreiben, noch kann sie die entscheidungsfindung parallelisieren.
-
rapso schrieb:
Azrael, il Meraz schrieb:
Wie optimiert man Sortiertechnisch einen Vertex und Indexbuffer am besten?...
Wie kriegt man die beste performance?btw. nicht noetig zu jeder frage einen neuen thread zu starten
Ich komm damit jetzt mal garnicht klar. Ist das normal?
In der Readme steht was von einem Beispiel, welches ich aber nicht finden kann
See the StripTest source code (in function LoadXFileStripped) for an example of using the library.
Vllt brauch ich ja ne Brille. Ich seh da kein StripTest source code und danach suchen klappt auch nicht so wirklich
[edit] grrr - da will man sich bei Irrlicht was abgucken und entdeckt, dass irrlicht Vertexbuffer garnicht verwendet argh xD
-
hustbaer schrieb:
Im Prinzip ist mir viel von dem was du da schreibst nicht neu.
Ich wollte aber bloss ne Zahl wissen. Ein Maximum anzugeben ist wahrscheinlich nicht möglich, aber vielleicht hat jmd. schon einen Fall gehabt wo er einen grossen Unterschied gesehen hat der nur durch "umordnen" der Index-Buffer entstanden ist. Dann könnte er/sie/es schreiben "Faktor 2.7 hab ich selbst schon gesehen" - oder sowasAlso die paar Mal, dass ich das explizit gesehen habe, weil ich ein Model erst ohne Re-indexing importiert habe, und dann mit, hats immer einen merkbaren Boost gegeben. Ich schätze so 1.5x FPS. Wobei, wie schon ausführlich dargelegt, es natürlich immer darauf ankommt, was der limitierende Faktor ist.
@nvtristrip: Hat sich da so viel geändert? Wie ich das seinerzeit benutzt habe, waren das einfach eine handvoll Methoden, die zum Glück ziemlich selbsterklärend waren. Ich glaub "GenerateStrips" heißt die Methode, der man ein Array an Indices füttert und dann eine Liste an Arrays an umgeordneten Indices zurückbekommt (man bekommt deshalb mehrere Arrays zurück, weil das Model eventuell in mehrere Strips zerlegt wird, die nicht miteinander verbunden sind. Gibt auch eine Methode dafür, dass alle Strips in 1 Array zusammengestitcht werden sollen).
-
Ich wollte aber bloss ne Zahl wissen
Du musst hier bedenken, dass ein 3D-Modell ja auch irgendwie "entstanden" ist und die Polygone dementsprechend nicht in voellig zufaelliger Reihenfolge auf dem Objekt liegen - man wird also aeussert selten den Worst-Case sehen.
Bei meinen Versuchen mit hochtesselierten Objekten lag die Cache-Miss-Rate im unoptimierten Zustand durchschnittlich knapp ueber 1 Vertex/Triangle.
-
Hab mal ein paar Artikel zur Optimierung gelesen und bastel mir hier eine Optimierungsfunktion zurecht. (Ja, ich bin mir sicher, dass ich das selber programmieren will :P) jetzt hab ich eindeutig ein Fehler gemacht (Geometrie leicht zerschrottet und die Optimierung stimmt nu auch nicht wirklich, für diese Frage aber irrelevant^^). Ich habe aber immer noch dieselben vertices und indices. Hab einen framedrop von ca 200 fps auf 30 fps. Irgendwie finde ich den framedrop etwas seltsam. Liegt er nur an cache misses?
-
Hab einen framedrop von ca 200 fps auf 30 fps. Liegt er nur an cache misses?
Nein.
-
So leutz. Ich hab diesen Algorithmus hier implementiert : http://www.cs.princeton.edu/gfx/pubs/Sander_2007_>TR/tipsy.pdf
Ich bekomme bei meinem ca. 5000 dreiecken im Model einen performanceverlust von 20%. Ich vermute mal einfach, dass Blender3D (Daher hab ich das Model mit gleichen Indices exportiert) einen internen Superoptimierer hat :D...Und sorry für die leicht ehm... überflüssige frage.
-
Ich vermute mal einfach, dass Blender3D einen internen Superoptimierer hat
Und ich vermute mal einfach, dass Du einen Fehler gemacht hast.
Schmeiss Deinen Indexbuffer mal in einen simulierten Vertex-Cache (zb hier) und poste mal das Verhaeltnis zwischen hit und miss (VCache::add liefert true/false).
-
vor dem optimieralgorithmus:
misses: 6008
hits: 9628
hits per miss: 1.60253nach dem optimieralgorithmus (vcache_size = 16)
misses: 4232
hits: 11404
hits per miss: 2.69471ok, ehm. was muss man noch beachten? "^^
-
mach den test nochmal mit vcache_size = 24 bitte
-
vorher:
misses: 5523
hits: 10113
hits per miss: 1.83107nachher:
misses: 3982
hits: 11654
hits per miss: 2.92667Wieso ist optimierte geometrie bei OpenGL langsamer und bei DirectX schneller?
fps vorher/nachher:
openGL: 310/292Direct3D: 350/388
-
misses: 6008
hits: 9628Bei 6008+9628 Indizes (5212 Triangles, vermutlich weniger als 3000 Vertices) bist Du nicht Vertexdurchsatz-limitiert.
-
Ok, anderes Mesh:
vorher:
misses: 45965
hits: 168307
hits per miss: 3.66163nachher:
misses: 47227
hits: 167045
hits per miss: 3.53707mit vcache_size = 16 krieg ich folgendes ergebnis:
vorher:
misses: 54442
hits: 159830
hits per miss: 2.935784872misses: 49434
hits: 164838
hits per miss: 3.33451Mein Mesh ist eine einfache Kugel. ich denke mal, hier wurden die indices schon bei der erschaffung in einer güngstige reihenfolge gewählt.
Jetzt noch ne frage zu DirectX/OpenGL
Ich habe bei OpenGL eine framerate von ca. 115 fps.
bei DirectX habe ich eine zeit lang auch etwa 120 fps. nach einigen sekunden springen die fps aber plötzlich auf 180-200. Was passiert hier?
-
falls du shader benutzt, kann es sein dass der treiber fertig wurde den zu optimieren und dann ersaetzt er den provisorischen eventuell mit dem 'optimalen'
es kann aber auch an 100 anderen dingen liegen
-
noch fahr ich ohne shader.
Man kann mich ruhig auf 100 andere dinge verweisen wenns unnötig ist sie aufzuzählen. Bin neugierig