Prioritäten bei Threads

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.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Input/Output. Dateien lesen, schreiben, Speicher allokieren (ist in Python nicht zu vermeiden), im Grunde jeder Systemaufruf der das Potential hat, lange auf die Bank geschoben zu werden. Wenn du logging betreibst, dann ist das unnoetig. Das kannst du auch aufsammeln und zB genauso wie die Nutzdaten an deinen zweiten Prozess uebergeben. Das ist dann nur noch ein Aufruf, und eine PIPE (welche der Queue unterliegt) sollte kein Problem darstellen.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

IO = jegliche Art von Eingabe / Ausgabe Operation, IO steht für Input/Output. IO ist halt immer um ein vielfaches langsamer als die CPU. Sprich: viel IO bremst das Programm.

Gruß, noisefloor
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

noisefloor hat geschrieben: Montag 14. Januar 2019, 19:50 IO = jegliche Art von Eingabe / Ausgabe Operation, IO steht für Input/Output. IO ist halt immer um ein vielfaches langsamer als die CPU. Sprich: viel IO bremst das Programm.
Das ist so nicht richtig. IO ist auch das beschreiben oder lesen von einer Pipe, und das ist zB komplett im Speicher und dem Kernel abgehandelt. Es kostet den Uebertritt in den Kernel, und der muss/soll dann natuerlich auch den wartenden Prozess/Thread aufwecken - aber das ist keine grosse, vor allem aber auch nicht unberechenbare Verzoegerung, wenn man das System ausreichend austariert hat.

Sonst koennten Echtzeit-Prozesse/Threads kaum sinnvolle Arbeit leisten - nur, wenn alles, was sie machen, auch in ihnen berechnet wuerde. Aber zB ein Roboter, der Sensordaten lesen und Aktuatoren bewegen soll, muss das in RT machen, bekommt ja aber Daten zugeliefert von langsamen und ggf. latentz-behafteten Systemen wie einer Kartierung oder aehnlichem.
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

__blackjack__ hat geschrieben: Montag 14. Januar 2019, 16:06 @Muntliger: Jetzt verschiebt sich das zu was auch immer in ``channel_logger_count.info(str(setpoint))`` gemacht wird. Logger können ja beliebig komplizierte Dinge tun.

Ja, an der Geschwindigkeit ändert sich etwas, das ist aber nicht der Punkt. Eine Schleife ist kürzer, leichter lesbar, besser wartbar und damit weniger fehleranfällig. Wenn Du manuelles „loop unrolling“ machen willst, dann ist Python an der Stelle die falsche Programmiersprache.

Was Du auch sein lassen solltest ist das Vergleichen mit literalen Wahrheitswerten. Wobei ``is`` auch erst in aktuellen Python 3-Versionen überhaupt erst erlaubt ist. Und die beiden verschachtelten ``if``-Bedingungen hätte ich auch in *eine* gesteckt.

Ok, hab das jetzt alls Schleife gemacht - Danke für den Tipp

was hab ich für eine alternative als der Vergleich mit "is True" oder "is False"?
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

'is True' oder 'is False' sowieso niemals. Eigentlich brachst du niemals mit True oder False vergleichen, denn da kommt dann ja auch nur wieder True oder False raus. Also statt

Code: Alles auswählen

if condition == True:
kannst du gleich

Code: Alles auswählen

if condition:
schreiben. Fuer

Code: Alles auswählen

if condition == False:
dann

Code: Alles auswählen

if not condition:
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Ich kann das Problem nicht finden.
Momentan füre ich nur diesen Code aus mit höchster Priorität (nice -10):
einige andere Codes laufen auf (nice 0)

Code: Alles auswählen

    
    while True:
        last = (time.time() * 1000) - now # speedtest
        now = (time.time() * 1000) # speedtest

        if last > 5:
            print last

        time.sleep(0.001)
 
alle 2-3 Sekunden dauert ein "Zyklus" mehr alls 5ms.
Meistens jedoch nur 1,3ms.

Bei einer Prio von -10 sollte doch dieser Codeteil immer gleich lang benötigen.

Wenn ich per TOP die CPU anschaue ist sie auf 72% idle.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und wie im Verhältnis zu unseren Ausführungen würdest du das print einordnen?
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und mit nice erreichst du keine Echtzeit-Priorität. Dazu kannst du das Tool chrt benutzen. Die Prioritäten sind dort anders nummeriert als bei nice. Je höher, je wichtiger. Ich würde dir mal SCHED_FIFO & 80 empfehlen.

ABER ACHTUNG: das ist alles Makulatur, wenn du die Ausführungen zu IO nicht beherzigst. Denn dann kann eine Operation dich beliebig lahm legen.
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

__deets__ hat geschrieben: Dienstag 15. Januar 2019, 08:49 Und wie im Verhältnis zu unseren Ausführungen würdest du das print einordnen?
Ok ich verstehe - hab das mal mit diesem Beispiel getestet:

Code: Alles auswählen

from time import time

start = time()
for i in range(10000):
    print(i)
print('run time for printing:' + str(time() - start))

start = time()
for _ in range(10000):
    pass
print('run time for no printing:' + str(time() - start))
Ergebniss:

Code: Alles auswählen

run time for printing:1.39491295815
run time for no printing:0.0173499584198
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und die Sache ist nicht ganz so einfach. Es geht nicht darum, das print zu benutzen mehr Zeit braucht als nichts zu machen. Das ist keine Erkenntnis. Es geht darum, ob die Wiederholungen des sleeps eine vorhersehbare Wartezeit produzieren. Dazu solltest du zb mit time.monotonic die tatsächliche Schlafenszeit messen. Und den Maximalen wert bestimmen. Und den ausgeben, aber erst nach längerer Laufzeit, und am besten unter Stress (Stress-ng —matrix 4 zb )

Allerdings its Python für sowas nicht ideal, weil man keine Kontrolle über SpeicheranForderung hat. Die allocation sollte bei einem simplen Programm das im Grunde immer das gleiche tut - warten, I2C, Interprozesskommunikation - stabil sein. Weil immer der gleiche Speicher angefordert, und dann freigegeben wird.

Ggf aber ist trotzdem der garbage collector Teil des Problems. Dann würde ich den mal prophylaktisch immer erzwingen zu laufen, damit dessen Arbeit immer gleich bleibt.

Wenn das alles nichts bringt, bleibt nur der Umstieg auf C/C++ für den I2C-Teil.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und noch ein Nachtrag: print ist IO. Und braucht nicht nur “normal” lange. Sondern kann unter Umständen beliebig lange blockieren, weil der empfangene Prozess (das Terminal) nicht bereit ist, die Daten anzunehmen.
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Ich hab gerade nochmal all meine Funktionen nach dem "is True" und "is False" durchsucht und richtig gestellt.
Nun ist die CPU statt 40% nur noch 1-2% belastet ... kann das soviel ausmachen?
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

ist doch noch 12% ausgelastet - aber sieht aus als wäre es schon weniger.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bitte poste deinen Code. Die von mir beschrieben Änderungen machen deinen Code besser, aber sollten keine Laufzeitveränderung erzeugen.
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Solche Funktionen habe ich für jeden Kanal.

Code: Alles auswählen

def channel3_active():
	global ch3_active_hm
	if data.channel3_active and ch3_active_hm:
		ch3_active_hm = False
		conf_logger.info('channel 3 - ACTIVATE')
		print('channel 3 - ACTIVATE')
	else:
		pass

	if not data.channel3_active and not ch3_active_hm:
		ch3_active_hm = True
		conf_logger.info('channel 3 - DEACTIVATE')
		print('channel 3 - DEACTIVATE')
	else:
		pass
Gibt es eine bessere Lösung für einen "Hilfsmerker"?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Das «else: pass» ist unsinnig. Globale Variablen nicht verwenden. Eine Funktion für alle Kanäle mit einem Parameter für die Kanalnummer wäre besser. `data` kommt aus dem nichts und sollte wohl auch ein Parameter sein, wahrscheinlich eine Liste mit einem Eintrag pro Kanal, am besten Instanzen einer Kanal-Klasse.

Aber ohne den gesamten Code zu kennen, kann man da halt schlecht helfen.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ist das der Code der zuverlässig schnell laufen soll? Wenn ja - wieso sind da print und log statements drin?
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Das ist im "langsamen" Teil.
Hab's nun so gemacht:

Code: Alles auswählen

def channel6_active():
	global ch6_active_hm
	if data.channel6_active and ch6_active_hm:
		ch6_active_hm = False
		conf_logger.info('channel 6 - ACTIVATE')

	if not data.channel6_active and not ch6_active_hm:
		ch6_active_hm = True
		conf_logger.info('channel 6 - DEACTIVATE')
Benutzeravatar
Muntliger
User
Beiträge: 40
Registriert: Montag 19. November 2018, 09:09

Hab den Code mal Online gestellt - bin für jeden Tipp dankbar.
Jetzt schonmal vielen Dank - ihr habt mir schon sehr weiter geholfen.
So macht das Python - lernen Spaß :)

http://gofile.me/3n8Ri/tTCe22Xs4
Antworten