Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Mi Mai 15, 2024 13:22

Foren-Übersicht » Programmierung » Einsteiger-Fragen
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 6 Beiträge ] 
Autor Nachricht
BeitragVerfasst: Mo Jul 22, 2013 20:36 
Offline
DGL Member

Registriert: Di Mai 10, 2011 19:38
Beiträge: 63
Servus,

habe mir ein paar Klassen für die Verwaltung erstellt.
Jetzt stellt sich mir die Frage, wie ladet ihr eure Daten? Also wenn die Daten geladen werden,
wird meist ein Ladebild angezeigt. Ich würde mir jetzt einen Thread erstellen der das Bild rendert nach dem Laden der Daten wird der Thread beendet und die Daten die gerade geladen wurden angezeigt. So könnte ich mir das ganze vorstellen, wie macht ihr das ganze?

gruß


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mo Jul 22, 2013 21:44 
Offline
DGL Member
Benutzeravatar

Registriert: Do Dez 29, 2005 12:28
Beiträge: 2249
Wohnort: Düsseldorf
Programmiersprache: C++, C#, Java
Die Sache ist nicht ganz so einfach. Du kannst nur aus dem Hauptthread heraus rendern. Und du kannst nur im Hauptthread Texturen (etc.) zur Grafikkarte schieben. Im Grunde sollten ALLE Zugriffe auf OpenGL aus dem Hauptthread heraus stattfinden, den OpenGL ist nicht Thread-safe!
Üblicherweise baut man sich einen Ladethread in dem alle Zugriffe auf die Festplatte passieren. Außerdem kannst du dort die Daten aufbereiten, etwa JPG oder PNG dekodieren, ZIP's entpacken, XML lesen, etc. Die aufbereiteten Daten werden dann an den Hauptthread geschoben der diese nach und nach an OpenGL übergibt.

Solltest du viel Zeit zum aufbereiten der Daten benötigen, kann es sich lohnen dafür einen oder mehrere weitere Threads zu haben, damit der Ladethread kontinuierlich von der Festplatte laden kann und nicht warten muss. Also schon mal den nächsten Datensatz laden während du aufbereitest. Die Festplatte ist der langsamste Faktor und sollte daher ausgelastet sein.

Optische Laufwerke sind noch langsamer. Bei Titeln die von Disc geladen werden sorgt man dafür das die Daten exakt in der Reihenfolge auf der Disk liegen wie sie benötigt werden. Das geht am einfachsten indem man die Dateien einfach in eine große Datendatei wirft. Bei der Gelegenheit kann man die Daten auch gleich komprimieren. Zum Teil macht es auch Sinn die Daten auch einfach mehrfach auf der Disk zu haben, wenn sie in verschiedenen Zusammenhängen genutzt werden. Auf DVD und BluRay ist eh soviel Platz der sonst ungenutzt wäre. ;) (Gut, Hobbyprojekte packt man eher selten auf BluRay :D aber große Datendateien machen auch beim laden von Festplatte sinn)

_________________
Yeah! :mrgreen:


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Di Jul 23, 2013 07:32 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
Im Prinzip bietet sich also eine Pipeline folgender Art an:

1 I/O-Thread -> n Aufbreitungs-Threads -> Hauptthread

wobei n die Anzahl der zur verfügung stehenden Prozessoren ist. Für den Anfang kann man das auch mit einem Thread machen. Wenn's dann zui langsam wird, kann man mehr benutzen. Es ergibt keinen Sinn (und ist sogar Kontraproduktiv), mehr als einen I/O-Thread zu haben (außer du weißt, dass dein Nutzer ein RAID 1 oder sowas hat).

Der I/O-Thread liest die Rohdaten von der Platte und packt sie in einen Speicherblock. Dieser Speicherblock wird an den Aufbereitungs-Thread weitergereicht, der z.B. die PNG-Daten da drin dekodiert und zurechtschneidet (oder was sonst noch zu tun ist), oder den ersten Opus-Block der Audiodaten in Wave umwandelt, damit es keine Latenz beim Abspielen gibt. Dann wird alles an den Hauptthread weiter gereicht, der ggf. Daten auf die GPU laden kann und alles andere in deiner Resourcenverwaltung registriert. Dadurch, dass du das im Hauptthread machst, sparst du dir den Aufwand, die Resourcenverwaltung Threadsafe zu machen.

Umgesetzt vorstellen könnte man sich das Design, indem man eine Basisklasse für ladbare Dinge hat, die die virtuellen Methoden LoadFromFile, Prepare und Finish hat. LoadFromFile wird im I/O-Thread aufgerufen und lädt die Rohdaten in den RAM, Prepare dekodiert die Rohdaten und schmeißt sie danach weg und Finish kümmert sich um das hochladen auf die GPU bzw. die Registrierung im Resourcenmanager. Die Spezialisierungen für diese Methoden implementierst du dann einfach in abgleiteten Klassen, was die Threads wunderschön schlank hält.

grüße

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Mi Jul 24, 2013 16:58 
Offline
DGL Member

Registriert: Di Mai 10, 2011 19:38
Beiträge: 63
Ah okay. Habe schon ein paar Threadversuche durchlaufen, allerdings wollte ich dort das Ladebild rendern. Was nicht so funktioniert hat, jetzt weis ich auch wieso :)
Danke.


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Do Jul 25, 2013 00:02 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Meine Vorredner haben schon zweifelsfrei klar gemacht, dass man Threads zum laden verwenden sollte, damit das ganze Asynchron von statten geht.
Ich will da mal ein sehr tiefen Sprung ins Detail machen.

Idealer weise benutzt man einen Threadpool, welcher für blockende IO Tasks gedacht ist, dieser Pool ist nicht wie der übliche Threadpool für Worker Tasks und enqueued den rest sondern erstellt weitere Threads für den Pool und wird maximal durch ein Threadlimit, im Pool festgelegt, beschränkt.
IO Tasks sind blockende Tasks und es ist am effizientesten IO-Threads einzelnen blocken zu lassen und die neu erstellten Threads im Pool zu halten, als zu queuen, über select mehrere blocking operationen zu sammeln oder async API vom OS zu verwenden.
Wenn du von der Platte mehrere Read befehle abgibst, die in verschiedenen Threads blockieren, dann tut das OS trotzdem die Festplattenzugriffe sammeln, neu sortieren und auf dem effizientesten Weg lösen. Was dazu führen kann, das der letzte Thread als erstes raus kommt, da der Lesekopf gerade über die benötigte Stelle huscht.
Wenn du Daten von der Platte lesen willst, dann erzeuge den Speicher und achte darauf, dass du nicht darauf arbeitest, denn das OS gibt dem DMA-Controller Zugriff, damit er dort direkt hin schreiben kann, wenn er die Daten von dem Festplattencache bekommt.
Die Daten werden in 4KB-Chunks hin und her geschoben, wenn du also deine Datein nicht mit 4KB alignment ablegst und aus diesen Streamst, kann es spürbar an der Performance zeeren, da unnötige Festplatten zugriffe gemacht werden müssen, wenn das letzte Byte im nächsten 4KB Chunk liegt.
Daher werden Benchmarks auch mit 4KB read/write Befehlen gemacht.
Daten fragmentierung sorgt dafür, dass 2x 4KB read nicht unbeding zur gleichen Zeit fertig werden, wenn die Chunks zu weit auseinander auf der Platte liegen.
Daher werden bei streaming Daten die Anfrage in mehrere Threads verpackt, statt ein großes read zu machen, weil die Latenz geringer ist.
Bei nicht so kritischer Latenz, wie z.B. einer Textur, macht ein einzelner read Befehl sinn, da dieser ein höheren Datenduchsatzt gewehrleistet.

Die OS bieten verschiede API's zum lesen, dabei gibt es in den neueren API's Möglichkeiten Einfluss auf das Optimierungsverfahren, welches das OS benutzt, zu nehmen. So kann man z.B. sagen, die Datei wird sequenziell gelesen, dann kann das OS schon weitere Pages, von der File, mit in den Festplattencache lagern und wenn du diese Anforderst gibt es ein Cache-Hit :)
Es gibt auch für schreiben, random access, die ganze file in den speicher mappen und einiges mehr.

Du weist nun also, dass es noch ne menge weiterer Möglichkeiten gibt, zu optimieren, das ganze komplizierter zu machen und eine Menge Voodoo passiert.

Wie auch immer nun die Daten von einem Thread geladen werden, dieser sollte möglichst wieder verwendet werden, da unter Windows Threads erstellen und zerstören einfach viel zu teuer ist.
Nun sollten die von der Platte geladenen Daten verarbeitet werden, dies passiert nicht im IO-Thread, der enqueued ein entsprechenden Task im ThreadWorker-Pool.
Dieser Pool ist ein fixer Pool aus Threads, er wächst und schrumpft nicht, kann aber Priorisieren und arbeitet die Auflaufende Queue ab.
In diesem Pool solltest du deine Filedaten nun parsen/dekodieren/serialisieren/was auch immer.
Wenn du dies machst, arbeitet deine CPU und wenn zu viele Threads CPU-Zeit brauchen, dann bekämpfen sie sich gegenseitig.
Dies würde eintreten, wenn ein IO-Thread direkt nach dem lesen anfängt die Daten zu verarbeiten und so lange den Thread nicht wieder verwenden kann und ein weiterer für den kommenden Read/Write Befehl erstellt werden muss.
Sind nun 11 Anfragen raus, dann können im schlimmsten Fall 11 weitere Threads die CPU gleichzeitig belagern und das ist ineffizient.
Daher nutzt man ein IO-Thread-Pool nur für Tasks, die den Thread schlafen legen(blocking commands) und somit keine Last erzeugen.
Der ThreadWorker-Pool ist ein fester Pool der laut Intel und AMD Papers 2*Logische Prozessoren groß sein sollte(für maximale effizienz).
Also bei einer CPU mit 4 Kernen 8 Threads und einer CPU mit 4 Kernen und Hyper Threading 16 Threads.
Die Threads werden auch Logischen Prozessoren zugewiesen, damit diese nur auf diese Arbeiten und damit können maximal 2 Threads um die ausführung kämpfen.
Zum schluss sollte der Task die endgültigen Daten dem Renderthread übergeben, da dieser das Fenster und den Renderkontext hält.
Der Thread, der das Fenster erstellt, kann auch nur den Renderkontext erstellen und entsprechend exklusive auf diesen arbeiten.
Dafür eignen sich lock free queues.

Nun weißt du wie es in den Professionellen Engines von statten geht, wie viel Arbeit du investieren willst um wieviel davon um zu setzen bleibt nun bei dir aber du weißt nun wo die Straße hin führen kann.

PS.: Für Virtuelle Dateisysteme wird das ein bisschen komplizierter ^^ aber ich gehe davon aus, dass du kein VFS verwendest.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
BeitragVerfasst: Fr Jul 26, 2013 14:06 
Offline
DGL Member

Registriert: Di Mai 10, 2011 19:38
Beiträge: 63
okayyy, Danke für die Ausführliche Info.
So komplex wollte ich es nicht machen, erstmal klein anfangen :)


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 6 Beiträge ] 
Foren-Übersicht » Programmierung » Einsteiger-Fragen


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.024s | 17 Queries | GZIP : On ]