Multithreading - Aufbau(?)
-
Im Grunde muss das Programm vier Aufgaben bewältigen:
Daten einlesen, verarbeiten, Daten ausgeben und dem Benutzer eine Oberfläche bieten.
ROFL, welches Programm macht den irgendwas anderes?
-
DEvent schrieb:
Im Grunde muss das Programm vier Aufgaben bewältigen:
Daten einlesen, verarbeiten, Daten ausgeben und dem Benutzer eine Oberfläche bieten.
ROFL, welches Programm macht den irgendwas anderes?
Lies die Beiträge zu Ende.
Es ist noch genau genug erläutert.Ich gebe zu, dass das zu Anfang unglücklich formuliert war, aber wenn du einfach mal weiter gelesen hättest, wäre uns sowas erspart geblieben.
hustbaer schrieb:
Zur Frage "5. Thread ja/nein": nein. Lass den UI Thread die anderen Starten. Soll dein Programm auch "command line" Fähig sein bzw. mal als Service laufen, kann der UI Thread immer noch alle anderen rausstarten, und dann einfach nixmehr tun ausser auf eventuelle service-control-messages zu reagieren.
Danke, dann lass ich das.
hustbaer schrieb:
@Sinthoras:
Zum Rest: ich denke es macht dann Sinn es in mehrere Threads aufzuteilen wenn das Einlesen synchron passiert.Ja, das soll es.
Wie aufwendendig das Zerschneiden sein wird, weiß ich noch nicht genau, jedenfalls wird der Einlesethread ja auf die Kamera warten müssen und währenddessen kann der Rest ja weiterarbeiten.
Das fertige Programm wird auf einem Dualcore laufen, also lohnt sich das Aufteilen der "Datenverarbeitung mit der CPU" wohl schon. Danke.
Für die Implementierung mit einem eigenen Thread bietet sich eine bounded FIFO Queue an, da kannst du die Daten von einem Thread aus reinstecken, und von einem anderen aus auslesen. "bounded" heisst dabei dass du eine Obergrenze festlegst wie viel Daten maximal gepuffert sein können (z.B. nur max. 1 Bild oder auch max. 3 Bilder).
Vielen Dank, das ist eine wirklich gute Idee!
Bis auf die Ausgabe lässt sich das wohl so machen (die nämlich arbeitet nicht mehr mit den Bildern).Zum letzten Abschnitt: Eine geringe Latenz ist leider auch nicht ganz unwichtig.
Kann ich es irgendwie so lösen, dass im Notfall der Bildverarbeitung mehr Rechenzeit zugeteilt wird, als dem Einlesen?Das wäre doch bei mehreren Threads (wenn ich es richtig verstehe) das Problem, dass z.B. der Einlese-Thread der Verarbeitung Rechenzeit klaut, weshalb das einzelne Bild dann länger in der Verarbeitungskette rumdümpelt, oder nicht?
Ideal wäre es ja wohl, wenn ein Kern die ganze Zeit nichts anderes machen würde, als Rechen (die Verarbeitung erledigen), während der andere sich um den "Rest" kümmert... keine Ahnung, ob sowas möglich ist...
Soweit jedenfalls schonmal vielen Dank.
-
Sinthoras schrieb:
DEvent schrieb:
Im Grunde muss das Programm vier Aufgaben bewältigen:
Daten einlesen, verarbeiten, Daten ausgeben und dem Benutzer eine Oberfläche bieten.
ROFL, welches Programm macht den irgendwas anderes?
Lies die Beiträge zu Ende.
Es ist noch genau genug erläutert.Ich gebe zu, dass das zu Anfang unglücklich formuliert war, aber wenn du einfach mal weiter gelesen hättest, wäre uns sowas erspart geblieben.
Wieso hast du das nicht gleich gemacht? So wars total nichtssagend.
-
Ich habe meinen Fehler doch eingestanden - reicht das denn nicht?
Können wir jetzt bitte zum eigentlichen Thema zurückkommen?
-
Sinthoras schrieb:
Ich habe meinen Fehler doch eingestanden - reicht das denn nicht?
Können wir jetzt bitte zum eigentlichen Thema zurückkommen?
Sorry, ich fand das nur unglaublich Witzig, weil das ja im ersten post stand.
Sollte auch nicht abwertend sein, war einfach nur witzig.
-
Ja, ist ja ok, ich bin dir ja auch nicht böse.
War ja mein Fehler und in der Tat nicht sehr... zielführend
Ich wollte nur jetzt gerne zum Thema zurückkommen und nicht weiter über den einen Satz diskutieren, das wollte ich damit bezwecken.
-
Zum letzten Abschnitt: Eine geringe Latenz ist leider auch nicht ganz unwichtig.
Kann ich es irgendwie so lösen, dass im Notfall der Bildverarbeitung mehr Rechenzeit zugeteilt wird, als dem Einlesen?Ja, das passiert "automatisch" wenn du eine bounded Queue verwendest - zumindest wenn der "Insert" Aufruf blockiert (siehe weiter unten). Wenn "das Einlesen" schon "weiter vorne" ist als "die Bildverarbeitung", dann wird die Queue zwischen "Einlesen" und "Bildverarbeitung" automatisch "voll". Wie "voll" eben "voll" ist kannst du ja selbst definieren (max. 1 Bild, max. 2 Bilder etc.).
Wenn das passiert, also die Queue "voll" wird kann man 3 Dinge tun:
-
die Queue blockiert den "Insert" Aufruf - der Einlese-Thread muss also so lange warten bis der Bildverarbeitungs-Thread das Bild komplett verarbeitet hat an dem er gerade arbeitet. Dadurch sparst du Rechenzeit beim Einlesen, da der blockierte Thread kaum Rechenzeit braucht.
-
die Queue lässt den "Insert" Aufruf einfach fehlschlagen, und das neu einzufügende Bild wird verworfen. Der Einlese-Thread kann sofort weitermachen und ein neues Bild holen. Dadurch wird zwar ein Bild verworfen ("dropped frame"), allerdings ist das nächste Bild welches in die Queue eingefügt wird (wenn sie irgendwann mal "nicht-voll" wird) "akuteller" - es sinkt also die Latenzzeit.
-
die "Queue" verwirft das zuletzt eingefügte Bild, und ersetzt es durch das neue. Der Einlese-Thread kann auch sofort weitermachen. Weiterer Vorteil: das nächste Bild welches verarbeitet wird ist nicht das (ältere) Bild das zuvor in der Queue gepuffert war, sondern das "neuere" Bild (das ältere wurde ja "überschrieben"). Die Latenzeit sinkt dadurch nochmals.
Eine maximale Queue-Länge von 1 mit Möglichkeit 3 von oben kombiniert sollte die geringst-mögliche Latenz bringen, ausgenommen eben der "mach alles in einem Thread" Variante, die aber u.U. zu wesentlich mehr dropped Frames führen könnte (da sie nur einen Core verwendet und nichts parallelisieren kann).
Wenn die "Bearbeitungszeiten" für ein Bild nicht sehr stark schwanken sollte eine maximale Queue-Länge von 1 auch ausreichend sein um maximalen Throughput (geringste "dropped frames" Rate) zu bekommen. Schwanken die "Bearbeitungszeiten" sehr stark könnte es etwas bringen eine längere Queue zu verwenden, allerdings geht das dann auf Kosten der maximalen Latenz.
Eine Aufteilung auf verschiedene Threads zwischen verschiedenen Teilen der "Bearbeitung" (also eben z.B. "Zerschneiden" und "Weiterverarbeiten") ist IMO sinnvoll, wenn diese Schritte "aufwändig genug" sind, und eben mehr als 1 Core zur Verfügung steht.
Die Queues zwischen den einzelnen Verarbeitungsschritten sollte IMO immer blockierend sein (also Möglichkeit 1 von oben), da man sonst in der ganzen Kette vor der Queue sinnlos Rechenzeit verblasen lässt. Im Prinzip trifft das auf das Einlesen eines Bildes auch zu, bloss gehe ich davon aus dass das Einlesen nicht wirklich sehr viel CPU Leistung verbrauchen wird. Normalerweise sollte das über die Grafikkarte/Capturekarte und DMA/Bus-Master passieren, plus eventuell noch einem dummen Mem-Copy. Nichst was man "sich nicht leisten könnte" quasi.Bis auf die Ausgabe lässt sich das wohl so machen (die nämlich arbeitet nicht mehr mit den Bildern).
Auch die Ausgabe lässt sich so machen. Der letzte Verarbeitungsschritt hat ja dann nicht ein Bild als Output, sondern eben was auch immer in der Ausgabe ausgegeben wird. Die Queue zwischen dem letzten Verarbeitungsschritt und der Ausgabe ist dementsprechend keine Queue<Bild> sondern eine Queue<AusgabeDatenTypDings>.
----
Was die Latenz angeht... scchwierig abzuschätzen. Probier es aus.
Auf jeden Fall musst du wissen was du im "worst case" willst, also dann wenn die CPU Leistung nichtmehr ausreicht um alle Bilder zu verarbeiten so schnell wie sie hereinkommen.Wenn es viel viel wichtiger ist möglichst wenig Frames zu verlieren: alles in eigenen Threads + längere Queues + alles blockierende Queues.
Wenn "beides irgendwie wichtig ist": rechenaufwändige Verarbeitungsschritte in eigenen Threads (kleine zu dem benachbarten Schritt dazu der weniger rechenaufwändig ist), Queue-Länge 1, erste Queue = "überscchreiben", alle weiteren = "blockieren".
Wenn es viel viel wichtiger ist möglichst geringe Latenz zu haben: alles in einem einzigen Thread.
----
Eine "beides ist immer ganz ganz wichtig" Lösung gibt es nicht, ausser du schaffst entsprehend schnelle Hardware an die immer "mitkommt". Dann ist es aber ziemlich egal wie du es programmierst

-
-
Ok, vielen Dank. Damit wären alle meine Fragen soweit geklärt.
Wenn es irgendwann demnächst an die konkrete Umsetzung geht und noch Fragen auftauchen, melde ich mich wieder.
Vielen Dank nochmals,
Sinthoras
-
Wobei ich noch zu bedenken geben würde, dass jeder Thread einen gewissen Overhead produziert.
Wenn das Zielsystem über mindestens 2 CPUs oder Kerne verfügt, würde ich auch den Weg über einen 'Controller-Thread' gehen. Wenn das Zielsystem nur über eine CPU mit nur einem Kern verfügt, würde ich alles in einen Thread packen. Da kann man dann ein bißchen Verwaltungsoverhead und Synchronisationsaufwand sparen, da sowieso alle Prozesse auf einem Kern laufen.
-
Eventuell kann es aber auch auf einer CPU helfen mehrere Threads zu haben. Beispielsweise wenn einer rechnet und einer IO macht.
edit: hatte hustbaer ja auch schon erläutert.
-
Ok, danke für euer aller Hilfe.