Wie viele Threads sollte man nutzen?



  • Graphen. ❤


  • Mod

    wenn man 40threads zur entkoppelung nutzen wuerde, koennte man ganz schnell das gegenteil von dem erreichen was man erreichen will. wenn 40threads um die cpu kaempfen, wird es recht unkontrolliert wer wann dran kommt und da die threads voneinander abhaengen, kann es sein, dass wartende threads rennen waehrend die auf die gewartet wird schlafen. wenn man dann mit prioritaeten versucht das problem zu loesen, kann das noch schlechter werden, weil dann z.b. die UI responsiver gemacht wird, auf kosten der berechnungen die auf der UI angezeigt werden sollten, dann hat man ein sich fluessig rotierendes sanduhrechen, quasi.



  • rapso schrieb:

    wenn 40threads um die cpu kaempfen, ...

    Unter dieser Bedingung hat man sowieso insgesamt zu wenig Rechenleistung und man kann nur noch das Beste draus machen.

    ... wird es recht unkontrolliert wer wann dran kommt und da die threads voneinander abhaengen, kann es sein, dass wartende threads rennen waehrend die auf die gewartet wird schlafen.

    Wie soll das gehen? Wartende Threads werden nur aktiv, wenn die Bedingung weswegen sie blockiert worden sind aufgehoben worden ist, z.B. weil eine Berechnung in einem anderen Thread fertig gestellt worden ist und dieser deswegen ein Signal gesetzt hat: d.h. immer wenn ein zuvor blockierter Thread aktiv wird, kann er auch etwas sinnvolles tun.



  • gamer8o4 schrieb:

    Ich erstelle gerade eine GameEngine (bin noch in der Planung) und frage mich was so das passende Anzahl an Threads ist, aus denen das Spiel besteht.

    Wenn man noch solche Fragen stellt, sollte man noch keine Threads verwenden.


  • Mod

    Morle schrieb:

    rapso schrieb:

    wenn 40threads um die cpu kaempfen, ...

    Unter dieser Bedingung hat man sowieso insgesamt zu wenig Rechenleistung und man kann nur noch das Beste draus machen.

    das ist eben der trugschluss, denn wenn man weit mehr threads erstellt als die hardware verarbeiten kann, ist man dem scheduling vom OS ausgeliefert und falls die 40threads wirklich sinnvoll arbeiten muesten z.b. auf einer dualcore maschine unter windows, kann es sein, dass der 1ms task den du in einen anderen thread ausgelagert hast, damit dein aktueller thread weiterarbeiten kann, erst in 10ms drankommt, wenn du dann darauf synchronisieren willst, wirst du erst 11ms spaeter weiterarbeiten, statt 1ms. das fuehrt dann dazu, dass, obwohl du eigentlich nur 60ms rechenarbeit hast, diese statt 30ms dann 50ms auf dem dualcore braucht und auf einem quadcore vielleicht wirklich in 15-20ms durchlief (was entgegen der normalen erwartung ist, dass programme hoeher skalieren als die core zahl).
    ein system dass nur soviele threads wirklich auslastet wie cores/hw-threads vorhanden sind wuerde dieses problem nicht haben, wenn du dein scheduling selber machst.
    und nein, priorities werden dir nicht zwingend helfen, windows scheduled alle 10ms falls alle cores busy sind. was zu dem weiteren problem fuehrt, kommunikation zwischen threads, wenn du 40hast aber nur 2cores, kann extrem lange latenzen aufweisen, selbst wenn du ein signal schickst und hoffst dass der andere thread gleich aufwacht.

    das resultat solcher probleme ist, dass ein spiel dass zu 80% ein quadcore auslastet, es dennoch nicht schaft 100% eines dual cores auszulasten.

    ... wird es recht unkontrolliert wer wann dran kommt und da die threads voneinander abhaengen, kann es sein, dass wartende threads rennen waehrend die auf die gewartet wird schlafen.

    Wie soll das gehen? Wartende Threads werden nur aktiv, wenn die Bedingung weswegen sie blockiert worden sind aufgehoben worden ist, z.B. weil eine Berechnung in einem anderen Thread fertig gestellt worden ist und dieser deswegen ein Signal gesetzt hat: d.h. immer wenn ein zuvor blockierter Thread aktiv wird, kann er auch etwas sinnvolles tun.

    um das problem ein wenig einzudaemmen was ich im obigen absatz anriss, schlafen threads nicht sofort ein normalerweise. mutexe, signals/notifies etc. haben einen 'initialen spin'. stell dir vor du hast 2 cores und 2 threads, du implementierst eine LZH kompression (wie z.b. in zip), indem du einen core LZ komprimieren laesst, wenn der buffer voll ist, gibst du dem zweiten thread ein signal und der springt dann an und macht die H(uffman) kompression, wenn der buffer leer ist, gibt es ein signal an den ersten thread. wie du schon sagst, wird das zur "Entkoppelung" genutzt, aber hey, zwei threads, mit double buffering kannst du auch leicht doppelte performance haben. also hast du einen theoretisch zwei threads die packen, jedoch sind sie nicht synchron, einer braucht vermutlich leicht laenger als der andere. du wirst also dennoch beide threads immer wieder in den sleep state setzen wenn sie auf das signal warten. es kann sein, wenn du das hochfrequent genug machst, dass diese 100-1000 takte die windows dafuer braucht, viel mehr kosten als man durch das double-buffering usw. eigentlich sparen will. (sogar ohne double buffering hat man natuerlich dieses problem, weshalb man fuer sowas eigentlich schon immer fiber/coroutines nutzt).
    aber du weisst ja, der anderer thread wird jederzeit aufwachen, das warten auf das signal ist nur synchronisierung (egal ob du das mit einem signal/notify oder exklusivem lock mittels mutex machst), bevor du also in den sleep state gehst, waere es doch schlau ein paar mal zu versuchen den zweiten buffer zu bekommen und dann erst in den sleep state zu gehen, oder?
    genau das machen viele imlementierungen, gerade bei feinem threading (was bei vielen threads/jobs wahrscheinlich ist). dafuer gibt es keinen einheitlichen namen, wird "mutex with initial spin" oder "adaptive mutex" oder "hybrid lock" oder sonst wie genannt.

    wenn man wirklich 40 unabhaengige context haben will um "Entkoppelung" zu betreiben, sollte man eher auf fiber bzw couritines umstellen, denn dann hat man auf applikationsebene kontrolle was passiert (wie oben im beispiel von kompression). damit meine ich nicht browser die auf 4cores 3threads erstellen, zum laden, decoden und UI/update, sondern schon spiele, die vielleicht die CPU nicht voll ausnutzen, aber dennoch diese art von lazy entkopplung nutzen (lazy, weil es nicht noetig ist wie z.b. bei UI, aber einfacher einzubauen ist, statt es richtig zu machen).
    wenn man performance haben will, sollte man ein 'worker'-thread pro hardware thread haben. bei servern, wo man tausende requests handlen muss, ist man frueh auf das problem gestossen (nennt man C10K problem). einfach pro request ein thread erstellen klingt einfach und gut, zumal die requests ja eigentlich komplett unabhaengig sind, aber alleine schon das erstellen eines threads kann mehr kosten als das behandeln des requests. da kommt man schnell auf die idee "lass uns ein paar threads parken" und denkt natuerlich "wieviele?" -> mehr als es CPUs (bzw cores heutzutage) gibt macht keinen sinn. solche server laufen nicht nur stabiler, sie verarbeiten auch wirklich mehr.



  • Hab nicht alles gelesen.
    Aber ich würde die Anzahl der Kerne auslesen und dann genauso viele Threads maximal zulassen. Mainthread für Rendering und alles andere was schnell ausgeführt werden muss dann einem solchen Thread zuweisen.
    Der Teufel liegt aber dann im Detail mit der Priorität der einzelnen Aufgaben.



  • rapso schrieb:

    Morle schrieb:

    rapso schrieb:

    wenn 40threads um die cpu kaempfen, ...

    Unter dieser Bedingung hat man sowieso insgesamt zu wenig Rechenleistung und man kann nur noch das Beste draus machen.

    das ist eben der trugschluss, denn wenn man weit mehr threads erstellt als die hardware verarbeiten kann, ist man dem scheduling vom OS ausgeliefert und falls die 40threads wirklich sinnvoll arbeiten muesten z.b. auf einer dualcore maschine unter windows, kann es sein, dass der 1ms task den du in einen anderen thread ausgelagert hast, damit dein aktueller thread weiterarbeiten kann, erst in 10ms drankommt, wenn du dann darauf synchronisieren willst, wirst du erst 11ms spaeter weiterarbeiten, statt 1ms.

    Das ist richtig, aber IMHO ein Sonderfall. Es gibt eigentlich immer Situationen, die zu einem ungünstigen Ergebnis führen. Um beim Beispiel GUI Entkopplung zu bleiben: Wenn ich weiss, dass meine Logik jeweils nur 1ms rechnet, dann brauche ich nichts zu entkoppeln. Wenn die Logik aber im Mittel 100ms rechnet, dann wird sich die Entkopplung lohnen. Wenn dann noch viele kleine Arbeitspakete mit jeweils 1ms dazwischen sind, kann man sich überlegen, ob man dafür einen Sonderfall schafft und diese direkt auf dem GUI Thread rechnen lässt, oder ob man in Abwägung eines sauberen Designs die Nachteile in Kauf nimmt und sie durch sein uniformes Worker-Thread System ausführen lässt.

    Weiterhin ist es ein deinem Beispiel so, dass ja "jemand anderes" gerechnet hat und dadurch deine Verzögerung entstanden ist. Die Rechenleistung ist also nicht verloren gegangen. Die Latenz des Systems hat sich also erhöht, aber der Gesamtdurchsatz hat nicht zwangsläufig darunter gelitten. Daher auch hier: Wenn Du den Sonderfall hast, dass die GUI absolut bevorzugt bedient werden soll, dann müsste man dies halt im Design auf irgendeine Art und Weise berücksichtigen.

    Meiner Erfahrung nach tut man gut daran seine Module so gut wie möglich zu strukturieren und abszukapseln. Wenn man jetzt irgendwann bemerkt, dass die Latenz der GUI oder eines anderen Subsystems unter wegen solchen Entkopplungen leidet, kann man immer noch entscheiden für diese irgendwo einen Sonderfall einzubauen.


  • Mod

    Morle schrieb:

    rapso schrieb:

    Morle schrieb:

    rapso schrieb:

    wenn 40threads um die cpu kaempfen, ...

    Unter dieser Bedingung hat man sowieso insgesamt zu wenig Rechenleistung und man kann nur noch das Beste draus machen.

    das ist eben der trugschluss, denn wenn man weit mehr threads erstellt als die hardware verarbeiten kann, ist man dem scheduling vom OS ausgeliefert und falls die 40threads wirklich sinnvoll arbeiten muesten z.b. auf einer dualcore maschine unter windows, kann es sein, dass der 1ms task den du in einen anderen thread ausgelagert hast, damit dein aktueller thread weiterarbeiten kann, erst in 10ms drankommt, wenn du dann darauf synchronisieren willst, wirst du erst 11ms spaeter weiterarbeiten, statt 1ms.

    Das ist richtig, aber IMHO ein Sonderfall. Es gibt eigentlich immer Situationen, die zu einem ungünstigen Ergebnis führen. Um beim Beispiel GUI Entkopplung zu bleiben: Wenn ich weiss, dass meine Logik jeweils nur 1ms rechnet, dann brauche ich nichts zu entkoppeln. Wenn die Logik aber im Mittel 100ms rechnet, dann wird sich die Entkopplung lohnen. Wenn dann noch viele kleine Arbeitspakete mit jeweils 1ms dazwischen sind, kann man sich überlegen, ob man dafür einen Sonderfall schafft und diese direkt auf dem GUI Thread rechnen lässt, oder ob man in Abwägung eines sauberen Designs die Nachteile in Kauf nimmt und sie durch sein uniformes Worker-Thread System ausführen lässt.

    die idee UI in einem thread laufen zu lassen ist aber auch nur erfolgreich weil wenn es relativ wenig threads in realtion zur den cores gibt. erstellst du auf einem single core einen UI thread und hast dann noch 3threads die wirklich arbeiten muessen, wird dein UI thread unter windows alle 40ms drankommen und dann hast du nicht viel gewonnen. natuerlich, den UI thread high priority machen (was das erste zeichen von fail ist 😉 ), aber dann bekommen die arbeitenden threads viel weniger zeit, UI ist responsiv, user wartet dennoch ewig -> natuerlich, Sleeps einbauen (das zeichen an dem man sicher sein kann, man hat es falsch gemacht).

    Weiterhin ist es ein deinem Beispiel so, dass ja "jemand anderes" gerechnet hat und dadurch deine Verzögerung entstanden ist. Die Rechenleistung ist also nicht verloren gegangen. Die Latenz des Systems hat sich also erhöht, aber der Gesamtdurchsatz hat nicht zwangsläufig darunter gelitten. Daher auch hier: Wenn Du den Sonderfall hast, dass die GUI absolut bevorzugt bedient werden soll, dann müsste man dies halt im Design auf irgendeine Art und Weise berücksichtigen.

    wenn wir hier bei forumthema "spiele(programmierung) bleiben, aeussert sich die latenz entweder in
    - schlechterer framerate, heisst also dass der thread der ewig gewartet hat, wenn er endlcih dran kommt, alleine laeuft und nichts mehr fuer die anderen cores zu tun ist.
    - input lag, du drueckst also eine taste und es dauert ueber 100ms bis du die reaktion siehst, system ist zwar top ausgelastet, du hast 30fps, aber das spiel laeuft wie ein schwam, schau z.b. das video da an: http://www.eurogamer.net/articles/digitalfoundry-lag-factor-article
    - framerate ist instabil, je nach scheduling deiner threads (die ja nicht konsistent ist), laeuft mal das ganze optimal und mal so suboptimal wie moeglich, du hast mal 60fps, mal 20fps. sowas fuehlt sich bei spielen schlechter an als durchgehend 20fps, da viele spiele die position des naechsten frames anhand der dauer des vorherigen frames bestimmen, entsprechend wartest du mal 16ms um ein fortschrit von 50ms anzuzeigen, mal 50ms um einen fortschrit von 16ms anzuzeigen.

    Meiner Erfahrung nach tut man gut daran seine Module so gut wie möglich zu strukturieren und abszukapseln. Wenn man jetzt irgendwann bemerkt, dass die Latenz der GUI oder eines anderen Subsystems unter wegen solchen Entkopplungen leidet, kann man immer noch entscheiden für diese irgendwo einen Sonderfall einzubauen.

    saubere kapselung ist halt der erste schritt, wenn man es durchziehst, dann kann man auch dafuer sorgen dass die UI responsiv ist.
    UI in einen thread zu packen, ist eher eine notloesung, da die implementierung einer sauberen loesung im nahinein unverhaeltnis lange dauern wuerde. es verursacht oft viele bugs und unvorhersehbare dinge, zudem suggestiert es dem user ein design konzept das nicht existiert. z.b. nimmt ein user an er kann dann einfluss haben auf etwas, was im hintergrund laeuft. in z.b. visual studio, hat man dann einfach die buttons in menues ausgegraut->die UI funktioniert, hat aber keine funktionalitaet. eine saubere kooperation zwischen modulen ist halt kein programming oder engineering job, sondern architecture, quality of service und management.



  • rapso schrieb:

    saubere kapselung ist halt der erste schritt, wenn man es durchziehst, dann kann man auch dafuer sorgen dass die UI responsiv ist.
    UI in einen thread zu packen, ist eher eine notloesung, da die implementierung einer sauberen loesung im nahinein unverhaeltnis lange dauern wuerde.

    Also vielleicht sollte man hier auch nochmal zwischen Spiel und Windows-Anwendung unterscheiden. Was bedeutet es denn eine GUI responsive zu halten?

    a) In Windows-Anwendungen, dass weiterhin Windows-Nachrichten verarbeitet werden. Entweder hat man dann in seinem "Arbeites-Code" sowas schreckliches wie "ProcessMessages" oder man lagert halt die Arbeit aus, womit man dann die GUI auf einem seperaten Thread laufen hat. Von daher finde ich sowas "per Design" von vorneherein zu implementieren ist keine Notlösung.

    b) Spiele. Da ist die GUI eigentlich ein Teil der normalen Spiel-Szene, die als Overlay über den Rest gerendert wird. Dies ist also Teil des Renderings und normalerweise da zu finden, wo man auch seinen restliche GameLoop bearbeitet. Wie ich das im Falle von Spielen machen würde, hatte ich ja schon geschrieben. Es gäbe da jedenfalls keinen explusiven GUI Thread.

    Im Allgemeinen stimme ich Dir aber zu, dass eine vernünftige Architektur Vorraussetzung ist.



  • Jop, denke ich auch, dass man das trennen sollte. Bei "normalen" Anwendungen dienen Threads eigentlich eher dazu, das Programm "responsive" zu halten. Man lagert also die gesamte I/O aus, und eventuell "schwere" Berechnungen, die im Hintergrund gemacht werden. Bei Spielen geht das eher in Richtung parallele Verarbeitung aus Performancegründen. Man kann den GUI Kram zwar auch entkoppeln (Netzwerk, I/O), aber das ist da eben nur der halbe Kuchen. Physik parallel zu berechnen o.Ä. drängt sich eben nicht so auf, und bedarf ganz anderer Überlegungen. Und bei einem Spiel hilft es auch nur bedingt die UI "responsive" zu halten, wenn alles andere hängt. Wobei, wenn ich so darüber nachdenke. Vielleicht ist das gar keine so schlechte Idee. Hm.


  • Mod

    Morle schrieb:

    rapso schrieb:

    saubere kapselung ist halt der erste schritt, wenn man es durchziehst, dann kann man auch dafuer sorgen dass die UI responsiv ist.
    UI in einen thread zu packen, ist eher eine notloesung, da die implementierung einer sauberen loesung im nahinein unverhaeltnis lange dauern wuerde.

    Also vielleicht sollte man hier auch nochmal zwischen Spiel und Windows-Anwendung unterscheiden. Was bedeutet es denn eine GUI responsive zu halten?

    naja, ich ging bei UI eh von anwendungen aus, nicht spielen, bisher kam bei spielen niemand auf diese idee sie in einem anderen thread zu verarbeiten...naja... noch nicht *Seufz*, fuehle dich angestarr @cooky451 🤡

    a) In Windows-Anwendungen, dass weiterhin Windows-Nachrichten verarbeitet werden. Entweder hat man dann in seinem "Arbeites-Code" sowas schreckliches wie "ProcessMessages" oder man lagert halt die Arbeit aus, womit man dann die GUI auf einem seperaten Thread laufen hat. Von daher finde ich sowas "per Design" von vorneherein zu implementieren ist keine Notlösung.

    es ist eine notloesung die durch 'bei mir lief das toll und einfach' zu einer 'packt alle eure UI in einen thread'-loesung propagiert wurde. es macht keinen sinn eine UI in einen thread zu packen, wenn der funktionsteil in einem anderen thread arbeitet und nicht reagiert. das ist als ob man dem fahrer erlauben wuerde seinen lenker wild drehen zu koennen waehrend sein fahrzeug geradeaus faehrt, gibt sicher einen kurzen moment an dem man zaghaft bis voller motivation dran dreht, aber es aendert doch nichts. es traegt eher zum gegenteil bei, dadurch dass die 'worker' immer weiter im hintergrund sind, wird die applikation weniger responsive, wenn auch die UI in einem thread arbeitet. z.b. koennen manche textverarbeitungsprogramme super fluessig bewegt werden und menues aufklappen, rechtschreibkorrektur tingelt aber sekunden hinterher, mein nokia 3210 konnte mit t9 sowas in echzeit (und ich denke nicht, dass die suchmenge derart gestiegen ist, dass ein 3GHz PC heutzutage 1s brauchen wuerde).

    Ich denke threads in applikationen sind gut in z.b. eclipse, wo ein thread (bzw prozess) abseits der IDE immer mitcompiliert, aber es ist eben keine UI-vom-rest-dank-thread-entkopplung.

    vielleicht kannst du mir ein beispiel geben wo es sehr sinnig ist und nicht anders sauber machbar ist.

    b) Spiele. ...

    hmm, ja, da hatte ich nie in bezug auf UI gesprochen, spiele entkopplen aber network, input, file i/o, sound oft durch threads, lazy man solution(tm).
    wobei ich mir nicht sicher bin, ob es wirklich lazy man ist, oder weil viele programmierer von frueher kommen, wo es fuer sowas interrupts gab und man natuerlich einen interrupt haendler hatte, jedoch ist es dann ein missverstaendniss von modernen betriebssystemen. der interrputhaendler ist ja quasi immer noch da, aber das OS regelt es und steckt es in buffer.
    frueher war es halt:
    interrupt->interruptthread der es in buffer kopiert->logik die es aus dem interrputhaendler-buffer kopiert&verarbeitet
    jetzt sollte es sein:
    interrupt->interruptthread vom OS der es in buffer kopiert->logik die es aus den OS buffern kopiert&verarbeitet
    leider ist es oft:
    interrupt->interruptthread vom OS der es in buffer kopiert->socket/input thread vom spiel der es in einen internen buffer kopiert -> logik die es aus den internen buffern kopiert&verarbeitet

    @cooky451
    hoer auf mich hier fertig zu machen, ui thread 🤡
    wobei das auf konsolen und handies ja in etwa so ist, afaik kann auf der xbox dein spiel sterben, du kannst aber noch den home button druecken und bekommst das menue eingeblendet. bei android/iOS scheint mir das auch so zu sein. lustigerweise machen es manche spiele genau andersrum, UI bekommt weniger updates als das ganze spiel, da sie sich eh kaum bewegt und kaum jemand merkt, ob da jetzt 30fps oder 10fps auf der UI sind, solange der rest fluessig ist. (wenn man ueberall nach einzelnen ms sucht, ist selbst das rendern der UI, besonders flash-artig, nicht gerne gesehen).



  • Ich denke du siehst das falsch. Wenn Systeme eh kaum kommunizieren müssen, (und sowas wie eine queue reicht), dann dürfte der Overhead von Threads kaum zu spüren sein. Das Argument mit dem 1 Thread/Client bei high performance Servern gilt nicht, davon wurde nie gesprochen. Man produziert ja keine 1000, sondern vielleicht 8 Threads, und deren Anzahl kann man sehr genau kontrollieren. Dass die Berechnungen in einem "hintergrund"-Thread wesentlich verlangsamt werden denke ich auch nicht. Die Checks werden nicht mal 1% Performance kosten und wenn doch, war die Berechnung entweder trivial, oder man hat etwas falsch gemacht.* Siehst du asynchrone OS-API Aufrufe eigentlich als Thread an? Denn letztlich ist das ja nichts anderes, nur liegt der Thread beim System. Und Windows selbst lässt durchgängig so etwa 600 Threads laufen, sooo schlimm können die da doch wohl nicht sein. 😉

    * Bedenke auch hier: 1 GUI, 1 Hintergrund Thread! Nicht 1 Thread pro Berechnung! Edit: Oder besser: Ein Producer, Ein Consumer. Wie viele Threads das dann genau sind, ist eigentlich nebensächlich.



  • Siehst du asynchrone OS-API Aufrufe eigentlich als Thread an? Denn letztlich ist das ja nichts anderes, nur liegt der Thread beim System.
    

    Denkst du? Zb asynchrones Lesen von einer Datei heißt ja im Optimalfall dass das OS eine DMA-Aktion startet und den Auftraggeber informiert sobald der Controller seinen ganzen Krempel in den RAM geschrieben hat. Da hat das OS/die CPU ja 0.0 Rechenaufwand dabei.



  • rapso schrieb:

    es ist eine notloesung die durch 'bei mir lief das toll und einfach' zu einer 'packt alle eure UI in einen thread'-loesung propagiert wurde. es macht keinen sinn eine UI in einen thread zu packen, wenn der funktionsteil in einem anderen thread arbeitet und nicht reagiert. [...] es traegt eher zum gegenteil bei, dadurch dass die 'worker' immer weiter im hintergrund sind, wird die applikation weniger responsive, wenn auch die UI in einem thread arbeitet.

    [...]
    vielleicht kannst du mir ein beispiel geben wo es sehr sinnig ist und nicht anders sauber machbar ist.

    Also wenn für dich "Responsiveness" das absolute Ziel ist, von dem nicht abgewichen werden darf, dann hast Du Recht. Für mich spielen aber auch andere Dinge eine Rolle: Der Benutzer darf gerne sein Programmfenster noch verschieben dürfen, während irgendeine komplizierte Berechnung läuft. Gerne er auch die lange Berechnung abbrechen dürfen - auch das geht nur mit einer wenigstens minimal bedienbaren GUI - Responsiveness ist hier zweitrangig.

    Man muss also Eingaben verarbeiten (bei Windows in Form von Messages) während man "Arbeitet".
    Wie ich schon sagte läuft das in der "Arbeit" darauf hinaus, immer mal wieder ProcessMessages aufzurufen. Man koppelt also "GUI-Arbeit" ganz direkt in seine "Berechnungs-Arbeit" rein (wo bleibt da die von dir angesprochene "saubere Architektur"?). Schlimmer noch, man hat eigentlich Calls von einer niederwertigen Schicht in eine viel höhere Schicht rein. Weiterhin setzt es vorraus, dass man seine Arbeit überhaupt in kleine Pakete teilen kann, um ProcessMessages aufrufen zu können. Nutzt einem wenig, wenn man in einer synchronen Betriebssystemfunktion festhängt.
    Oder man kann einfach beides in verschiedene Threads packen und nutzt damit die Preemption von seinem OS, kann Layer sauber trennen und muss sich auch keine Gedanken um synchrone OS-Funktionen machen, etc.

    Ich hoffe das reicht dir als Beispiel.

    rapso schrieb:

    hmm, ja, da hatte ich nie in bezug auf UI gesprochen, spiele entkopplen aber network, input, file i/o, sound oft durch threads, lazy man solution(tm).
    wobei ich mir nicht sicher bin, ob es wirklich lazy man ist, oder weil viele programmierer von frueher kommen, wo es fuer sowas interrupts

    Ich denke zwar nicht, dass Leute gerne Threads benutzen weil sie früher mit IRQs zu tun hatten, aber will darauf jetzt nicht weiter eingehen, weils dann doch zu sehr offtopic wird 😉



  • Ethon schrieb:

    Denkst du? Zb asynchrones Lesen von einer Datei heißt ja im Optimalfall dass das OS eine DMA-Aktion startet und den Auftraggeber informiert sobald der Controller seinen ganzen Krempel in den RAM geschrieben hat. Da hat das OS/die CPU ja 0.0 Rechenaufwand dabei.

    Von Rechenaufwand hat niemand gesprochen, nen Thread brauchst du trotzdem dafür.



  • cooky451 schrieb:

    Ethon schrieb:

    Denkst du? Zb asynchrones Lesen von einer Datei heißt ja im Optimalfall dass das OS eine DMA-Aktion startet und den Auftraggeber informiert sobald der Controller seinen ganzen Krempel in den RAM geschrieben hat. Da hat das OS/die CPU ja 0.0 Rechenaufwand dabei.

    Von Rechenaufwand hat niemand gesprochen, nen Thread brauchst du trotzdem dafür.

    Sagen wir es mal so, du brauchst keinen einzigen Thread mehr als wenn das Feature nicht vorhanden wäre.


  • Mod

    cooky451 schrieb:

    Ich denke du siehst das falsch. Wenn Systeme eh kaum kommunizieren müssen, (und sowas wie eine queue reicht), dann dürfte der Overhead von Threads kaum zu spüren sein.

    worauf beziehst du dich damit gerade, mir fehlt der kontext um antworten zu koennen. letzter beitrag von mir bezog sich nicht auf kommunikationsaufwand, glaube ich.

    Siehst du asynchrone OS-API Aufrufe eigentlich als Thread an? Denn letztlich ist das ja nichts anderes, nur liegt der Thread beim System.

    ich glaube das hatte ich damit beantwortet gehabt:

    frueher war es halt:
    interrupt->interruptthread der es in buffer kopiert->logik die es aus dem interrputhaendler-buffer kopiert&verarbeitet
    jetzt sollte es sein:
    interrupt->interruptthread vom OS der es in buffer kopiert->logik die es aus den OS buffern kopiert&verarbeitet
    leider ist es oft:
    interrupt->interruptthread vom OS der es in buffer kopiert->socket/input thread vom spiel der es in einen internen buffer kopiert -> logik die es aus den internen buffern kopiert&verarbeitet

    Und Windows selbst lässt durchgängig so etwa 600 Threads laufen, sooo schlimm können die da doch wohl nicht sein.

    wie ich schon zum thema starcraft sagte, wenn du da 40threads hast und die sidn einfach nur idle, ist es performance maessig nicht schlimm, design maessig ist es einfach nur traurig.
    beim OS darfst du das nicht direkt 1:1 anwenden, denn dein OS hat schonmal 65536 "threads" dadurch dass es verschiedene interrupts gibt. wenn du mal ein OS programmiert hast, oder unter DOS einen treiber, dann weisst du, wie das laeuft. unter DOS hies das auch nicht 'thread' sondern TSR (terminate but stay resistent), was soviel bedeutet, dass du dein program beendest, aber speicher 'leakst' auf den ein hardware interrupt zeigt und wo du binary reinkopiert hattest. dass protected mode systeme dafuer Threads erstellen ist eher ein sicherheits bedingtes, als design bedingtes system,... ich glaube der post wird zu lang wenn ich das ausfuehre.

    * Bedenke auch hier: 1 GUI, 1 Hintergrund Thread! Nicht 1 Thread pro Berechnung! Edit: Oder besser: Ein Producer, Ein Consumer. Wie viele Threads das dann genau sind, ist eigentlich nebensächlich.

    nein, es ist kein producer konsumer system, wenn leute threads einbauen fuer UI, dann ist es meistens ein entkoppelter "reponsibility" thread. im hintergrund kann ein producer-konsumer system laufen z.b. beim browser, aber dann scrollst du mit dem UI thread und wenn das system im hintergrund mit anderen dingen beschaeftigt ist, zieht dir der sehr responsive Internet Explorer oder auch safari auf dem iPad einfach nur schlieren bzw ein leeres bild hoch und dann kannst du manchmal etwas warten bis das system im hintergrund merkt, dass ein screenupdate noetig ist und zeichnet ein neues bild.
    ein producer consumer system braeuchte keinen 'thread', denn jedes moderne OS ist schon ein producer, alle eingaben liegen in einer message queue, dein program muss sie nur aufgreifen und als consumer verarbeiten. im konkreten fall eines browsers, wuerdest du deinen 'worker threads' die aufgabe erteilen das bild zu verschieben und fehlende stellen neu zu zeichnen.
    wenn dein system gut laeuft, ist es responsiv ohne UI thread, wenn es schlecht laeuft, sieht der user auch mit UI thread erstmal nichts.



  • Ethon schrieb:

    cooky451 schrieb:

    Ethon schrieb:

    Denkst du? Zb asynchrones Lesen von einer Datei heißt ja im Optimalfall dass das OS eine DMA-Aktion startet und den Auftraggeber informiert sobald der Controller seinen ganzen Krempel in den RAM geschrieben hat. Da hat das OS/die CPU ja 0.0 Rechenaufwand dabei.

    Von Rechenaufwand hat niemand gesprochen, nen Thread brauchst du trotzdem dafür.

    Sagen wir es mal so, du brauchst keinen einzigen Thread mehr als wenn das Feature nicht vorhanden wäre.

    Bei einem sychronen Aufruf passiert alles auf deinem Thread. Bei einem asynchronen Aufruf erfolgt der Callback der asynchronen Routine auf einem Kernel-Thread oder aber man schickt dir eine Windows Nachricht. Irgendjemand hat aber diese Nachricht im Kernel auch erzeugen müssen. Da das nicht auf deinem Thread passiert ist, hat das also ein Kernel-Thread getan.

    Ein DMA Controller oder was auch immer informiert deine *CPU* (nicht OS), über eine Interruptleitung. Dadurch wird eine Interrupt-Service Routine ausgeführt (nach Rapsos Defintion soll das wohl der Interrupt-Thread sein). ISR sind aber eigentlich Threads (zumindest nicht im OS Sinne), denn mit Threads ist ein Kontext assoziiert, mit einer ISR erstmal nicht.
    Es würde mich schwer wundern, wenn dein Asynchroner Callback direkt innerhalb der ISR ausführt werden würde. Damit könntest dein ganzes System zum Stillstand bringen, denn während ein Interrupt verarbeitet wird, kann normalerweise kein anderer verarbeitet werden. Deswegen wird in der ISR der oben genannte Kernel Thread informiert, dass jetzt Daten vorliegen. Der macht dann dein Callback.



  • Morle schrieb:

    ISR sind aber eigentlich Threads (zumindest nicht im OS Sinne), denn mit Threads ist ein Kontext assoziiert, mit einer ISR erstmal nicht.

    Es muss natürlich heissen:
    ISR sind aber eigentlich keine Threads (zumindest nicht im OS Sinne), denn mit Threads ist ein Kontext assoziiert, mit einer ISR erstmal nicht.



  • @rapso: Wenn nicht Kommunikation, wo ist dann der Punkt? OS Scheduling? Ich verstehe auch nicht wirklich, wie du das dann zusammenbasteln willst mit einer einzigen queue. Wenn da jetzt 4 rechenintensive Aufgaben drin stehen, und dahinter { process_gui_stuff(); }, hängt doch alles nur noch? Sicher, die Aufgaben werden 1 ms schneller fertig, aber das ist es doch wohl kaum Wert, wenn der User während der Zeit nichts machen kann. Wenn der GUI Thread durchgängig bei GetMessage() hängt und nur nach "hinten" durchleitet, sollte das wohl kaum Probleme geben. Ja, man verlässt sich auf das OS, aber dafür ist es doch da. Ich versuche wirklich zu sehen wo jetzt das Problem ist, aber ich verstehe es einfach nicht.


Anmelden zum Antworten