Auswertung Profile: lange Methode 'acquire' of 'thread.lock'

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
derhendrik
User
Beiträge: 17
Registriert: Donnerstag 15. Januar 2015, 16:39

Hier noch ein kleiner Nachtrag:
Danke für eure Hilfe, dadurch ist mir Vieles klarer geworden. Eben auch so Sachen wie dass ich aus Versehen noch im main-Thread bin, da mein Timer in der init des Worker-Threads erstellt wurde etc.

Habe es jetzt hinbekommen, dass es ausreichend flüssig läuft.
Die Lesefunktion läuft jetzt wie gewünscht im Workerthread.
Den Timer habe ich jetzt als "Singleshot" in der Lesefunktion implementiert, welcher beim Auslösen die Lesefunktion selber aufruft.

Zusätzlich habe ich alle "print"-statements rausgenommen. Diese haben, wie ich feststellen musste, alles sehr verlangsamt. Ich habe ja alle 100ms verschiedene Listen, Messwerte, "wo bin ich im code", etc ausgeben lassen.

Darüber hinaus plotte ich jetzt nicht alle 100ms neu. Ich habe nämlich feststellen müssen, dass sich dadurch Knöpfe, Tabwechsel etc nicht mehr bedienen ließen. Es wurde zwar geplottet, der Rest fror aber einfach ein.
Hier muss ich aber nochmal nachforschen, ob das nicht trotzdem schneller geht.
Zurzeit nutze ich pyqtgraph: Hier wird ein Benchmark mitgeliefert, welches auf dem Raspberry Pi für einen einzelnen Plot mit 30fps läuft.
Das ist aber nochmal ein ganz anderes Thema.

Zum Abschluss habe ich aber noch eine ganz andere Frage an euch:
Ich hatte mir erhofft, dass durch meinen Multithreading-Versuch alles schneller läuft und ich mir die Möglichkeit der "Gleichzeitigkeit" zunutze mache. Wird so ein Workerthread, oder generell ein Thread in Python automatisch auf einem anderen CPU-Kern als der Hauptthread laufen? Bringt Multithreading überhaupt den gewünschten Geschwindigkeitszuwachs, falls alles nur auf einem Kern läuft? (Ich glaube, dass Multithreading eben keine MulticoreCPU vorraussetzt)

Bei dem Raspberry Pi sieht man es ja immer ganz schön, wenn Anwendungen nicht von den mehreren Kernen Gebrauch machen: Ich habe beispielsweise das pyqtgraph Benchmark gestartet, die CPU Auslastung lag dann bei 25%, was ja zu erwarten war. Ich habe dann insgesamt 4 mal das Benchmark gestartet, die CPU-Auslastung lag dann um die 100% und alle sind mit ihren 30fps gelaufen. Da liefen dann aber ja alle Sachen getrennt in ihrem eigenen Interpreter (???) und nicht als Threads.

Schönen Abend euch!

Hendrik
BlackJack

@derhendrik: Die Frage(n) sind nicht so einfach zu beantworten. Welcher Thread auf welcher CPU oder auf welchem Kern läuft ist erst einmal Sache des Betriebssystems, das kann nach verschiedenen Kriterien entscheiden welcher Prozess und welcher Thread innerhalb eines Prozesses auf welcher CPU läuft. Das ist an sich auch gut so denn nur das Betriebssystem hat ja den Gesamtüberblick den ein einzelner Prozess nicht hat. Also was so alles in anderen Prozessen abgeht, wer gerade welche Ressource benutzt und so weiter.

Bei CPython kommt noch hinzu das immer nur ein Thread zur gleichen Zeit Python-Bytecode ausführen kann wegen dem „global interpreter lock“ (GIL). Wirklich parallel dazu können in anderen Threads Codeteile in C-Erweiterungen laufen die das GIL nicht brauchen. Also beispielsweise Operationen auf Numpy-Arrays oder auch Sachen bei Qt die ”länger” dauern so dass es sich lohnt das GIL freizugeben. Wenn Du eine 100%-Auslastung mit mehreren Graphen bei einem Mehrkernprozessor bekommst, dann läuft da das meiste tatsächlich parallel. Bei einem Einkern-System bekommt man natürlich keinen Geschwindigkeitsschub durch Threads. Wie sollte das den auch gehen‽ Es gibt halt nur den einen Kern und der kann immer nur einen Thread in einem Prozess zur gleichen Zeit ausführen.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@derhendrik:
Wenn es bei einem Timerevent alle 100ms ruckelt oder die GUI zeitweise einfriert, brauchen Deine Aktionen im Hauptthread zulange (länger als die 100ms). Falls Du noch viele Berechnungen im Worker in Python machst, addiert sich das des GILs wegen hinzu. Um die FPS hoch zu kriegen, wäre dann vllt. ein Blick auf das multiprocessing-Modul lohnenswert. Zuvor würde ich aber mittels eines Profilers schauen, was da eigentlich die Rechenzeit beansprucht.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

derhendrik hat geschrieben:Habe es jetzt hinbekommen, dass es ausreichend flüssig läuft.
Die Lesefunktion läuft jetzt wie gewünscht im Workerthread.
Den Timer habe ich jetzt als "Singleshot" in der Lesefunktion implementiert, welcher beim Auslösen die Lesefunktion selber aufruft.
Es ist fraglich, ob überhaupt ein Timer erforderlich ist. Denn wie sollte ein modernes Betriebssystem etwa bei input funktionieren:

Wenn ein input da ist und es wurde noch keine Zeile eingegeben, dann blockiert das Systen. Allerdings bei einem modernen Betriebssystem sieht dann das so aus, dass der Thread schlafen geht und ein anderer Thread dran kommt. Erst wsenn der User eine Zeile eingegeben hat, erwacht der Thread wieder.

Bei readline aber bin ich mir nicht sicher. Wenn man einen File etwa als eine Arte pipe benutzt und eine Anwendung schreibt hinein und eine andere liest jeweils eine Zeile heraus, dann kehrt sie auch mit 0 Daten zurück, wenn noch gar nichts geschrieben wurde. In so einem Falle ist ein Timer zu empfehlen.

Wenn es allerdings bei einer seriellen Schnittstelle wie bei einem input ist, macht ein Timer, wie Du ihn vorher hattest, überhaupt keinen Sinn, denn dann gibt es einen Stau beim Input Threadlock.
Bei input ist die Endlosschleife zu empfehlen , weil dort ja beim input sowieso gewartet wird und man keinen zusätzlichen Timer braucht. Allerdings ein Timer mit 0 Wartezeit danach wäre dasselbe wie eine Endlosschleife.

Wenigstens jetzt, wo Du den Timer danach erst setzt, knallen keine Timerfunktionen mehr auf den readline Threadlock

Du solltest testen, wie das mit readline bei der seriellen Schnittstelle ist. Wenn da readline auch wartet, bis eine Zeile eintrifft, dann brauchst Du keinen Timer und kannst eine Endlosschleife machen oder aber Du setzt die Wartezeit Deines Timers auf 0.

Und schneller würde es gehen, wenn Du ein GUI Update erst machst, wenn sich die Daten geändert haben, sich also von den vorherigen unterscheiden.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@derhendrik:
Wie sieht denn der Output des Arduino über die Zeit aus? Ohne timeout blockiert der Subthread in der Tat mit `readline` bis ein EOL an der seriellen Schnittstelle gefunden wurde.
derhendrik
User
Beiträge: 17
Registriert: Donnerstag 15. Januar 2015, 16:39

Hallo ihr drei!

Danke BlackJack für die Erklärung zum GIL. Ich muss gestehen, dass ich noch nicht so tief in der Materie drinstecke bzw. hinter die Kulissen von Python geschaut habe, um davon vorher gehört zu haben.

Ich hole mal wieder ein bisschen aus, falls es jemanden interessiert:
Diese GUI, die ich zurzeit programmiere, ist für ein Uniprojekt, in welchem die Leistungsaufnahme von einer Fräse geloggt und geplottet werden soll. Es existiert bereits der Arduino zum Messen, welcher von Studenten vor meiner Zeit programmiert wurde. In welchem Interval dieser Daten sendet ist nicht 100% klar, da hier und da immer mal wieder "delays" (beispielsweise in Interrupt-Routinen oder zum entprellen von Tastern) verwendet wurden.
Um die Arbeit ein wenig portabler zu machen und nicht an den Versuchsaufbau gebunden zu sein, habe ich mir ein vergleichbares Programm für meinen privaten Arduino geschrieben. Dieser sendet im 100ms-Takt (+ eben die Zeit zum Ausführen des restlichen Codes). Das vereinfacht mir natürlich ein bisschen meine Arbeit, schlussendlich werde ich wahrscheinlich nicht um eine art "timestamp" herumkommen.
Alternativ kann sich natürlich auch herausstellen, dass für meine Zwecke ein Senden der Daten alle 1000ms ausreichend ist und ich einfach erstmal einen Mittelwert auf dem Arduino errechne (für Messwerte wie eben die Leistungsaufnahme oder die Füllhöhe (vom Kühlschmierstoff, falls sich jemand wundert, warum in meinem Code "fuellhoehe" existiert).

@Alfons: Da ich die Daten ja über eine fortschreitende zeitliche Achse plotte, werde ich wahrscheinlich schon den Plot oft erneuern müssen, auch wenn sich die Messwerte erstmal nicht ändern.

Der Hinweis mit dem Blockieren bis EOL von readline war übrigens auch gold wert, danke dafür.
Als nächstes werde ich mich mal mit dem multiprocessing von pyqtgraph auseinandersetzen. Ist zwar jetzt nicht soooo dringend, aber macht ja auch Spaß da ein bisschen zu basteln.
Werde auch nochmal mit dem Profiler schauen, jerch. Als mir die GUI eingefroren ist, ist auch Python abgestürzt und irgendwas ist mit dem Profile schief gelaufen bzw. es wurde keines erstellt.

1000 Dank nochmal für eure Hilfe.
Hoffe, dass die Diskussion hier noch im vertretbaren Rahmen ist, habe nämlich gerade nochmal den Post "An alle Schüler und Studenten mit Informatikproblemen" gelesen ...

Grüße
Hendrik
Antworten