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.
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: 14529
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: 14529
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: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

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

Dieser Link funktioniert nicht (ggf wegen ad blocker). Was hindert dich daran den Code hier rein zu kopieren?
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Muntliger: Ich komme auch nicht an den Code. Da steht nur endlos lange etwas von „connecting“ und animierte Punkte und nix passiert.

Iiiih, ``global``. Und die Sachen die da in den Bedingungen verwendet werden sollten als Argumente übergeben werden. Also `data` und `ch6_active_hm`. Letzteres müsste dann auch der Rückgabewert der Funktion sein.

Das zweite ``if`` kann ja nicht wahr sein wenn das erste schon wahr war, also eher ``elif`` statt ``if``. Die Bedingung davon kann man auch leicht vereinfachen zu ``elif not (data.channel6_active or ch6_active_hm):``
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

ui. Das ist ziemlich wild. Ich habe dir hier wiederholt erklärt, dass die I2C-Kommunikation so wenig wie möglich tun muss. Und du startest stattdessen eine Feiertagsparade in Form eines neuen Prozesses, und wunderst dich, dass Stau ist.

In meinen Augen ist da Hopfen und Malz verloren. Das ist zu viel zu verquer, und muss von vorne begonnen werden.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Bei mir hat das mit dem Download jetzt auch geklappt. Nur mal kurz drübergeschaut und gegruselt. Ich dachte ja bisher immer `data` wäre ein Exemplar von einem Datentyp – das ist ein Modul in dem globale Variablen abgelegt sind auf die alle möglichen anderen Module zugreifen. Das ist ja noch mal eine Stufe schlimmer als das ``global`` in den ”Funktionen”.

Ich habe jetzt nicht alles durchgesehen, aber in jedem Modul das ich gesehen habe sind globale Variablen, ein Haufen ``def``\s aber *keine* Argumente und kein ``return`` in Sicht. Es gibt vielleicht im ganzen Code keine einzige Funktion die den Namen auch verdient hätte. Obwohl doch es gibt wohl zwei. Die dann in allen sechs `channel*`-Modulen als Kopie vorliegen.

Denn für jeden der 6 Kanäle gibt es ein eigenes Modul mit etwas unter 5 Kilobyte die aber bis auf die Kanalnummer anscheinend den identischen Code enthalten. Argh! Ich kann ja noch verstehen das Anfänger auf die Idee kommen einzelne Funktionen oder Codeblöcke zu kopieren, aber durchnummerierte Module mit nahezu gleichem Code sehe ich gerade zum allerersten mal.

Was ich auch zu ersten mal sehe ist das jemand in Python ``<>`` statt ``!=`` verwendet. Das war seit dem ich mit Python angefangen habe schon veraltet. Kein Tutorial oder Buch das ich kenne hat das jemals als Option angeboten. Ich kenne das nur weil es der Vollständigkeit halber in der Liste der Vergleichsoperatoren in der Python-Referenzdokumentation steht. Also bei Python 2. In Python 3 gibt's diesen Operator nicht mehr, da geht nur noch ``!=``.

Jetzt wo ich weiss das es Python 2 ist: Geh den ganzen Code nach Vergleichen mit ``is`` durch und ersetze die durch ``==`` denn Du willst so ziemlich garantiert in keinem der Fälle auf Objektidentität testen sondern auf Gleichheit. Wobei auch das nicht wenn einer der beiden Operanden ein literales `True` oder `False` ist. Das hatten wir ja schon: das fällt entweder weg weil es total unnötig ist, oder man setzt ein ``not`` vor den nicht-literalen Wert. Wobei in dem Code auch viel zu viel Gebrauch von `bool()` gemacht wird. Und von `str()` auch. Beispielsweise bei ``str(float_to_str(act_value))`` ist entweder der `str()`-Aufruf unsinnig oder `float_to_str()` hat einen sehr falschen weil sehr irreführenden Namen. Falls das `act_` in `act_value` „actual“ heissen soll, möchtest Du wahrscheinlich mal in einem Wörterbuch die Bedeutung von „actual“ und „current“ nachschlagen. Falls das `act_` nicht für „actual“ steht ist es ein gutes Beispiel warum man keine Abkürzungen verwenden sollte. Die werden zu leicht missverstanden.

Neben `os.system()` sieht man in ein und dem gleichen Modul auch `os.popen()` und `commands.getoutput()` in wenigen Zeilen Abstand. Normal wäre das man *eine* dieser Varianten versucht und dabei dann in der Dokumentation davon findet das man die nicht mehr verwenden soll, weil die durch das `subprocess`-Modul abgelöst wurden. Der Code sieht aus als hätte da jemand in die Dokumentation vom `subprocess`-Modul geschaut und dann entschieden die dort als veraltet aufgeführten Varianten möglichst *alle* zu verwenden, und ja nur nicht das zu machen was empfohlen wird. Das grenzt ja schon fast an einen Trollversuch. :-D

Den Code kann man komplett in die Tonne hauen. Dann Grundlagen lernen wie Funktionen, und ziemlich wahrscheinlich auch Klassen. Danach dann Anfangen und darauf achten, das auf Modulebene nur Code steht der Konstanten, Funktionen, und Klassen definiert. Kein ``global`` und Funktione und Methoden bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. Und es gibt ``return`` wenn Werte aus der Funktion/Methode an den Aufrufer zurückgelangen sollen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Vor allem ist der Code so niemals zu warten.
Die Duplizierung aller Funktionen für jeden einzelnen Kanal zieht sich nämlich durch alle Teile. Auch auch durch die config.py.

In der fristboot.py gibt es dann auch noch ein "if True:".

Die Abläufe kann man nicht nachvollziehen. Ich würde das Programm von Grund auf neu schreiben, und zwar ohne auch nur eine einzige Zeile aus dem alten Programm zu kopieren.
Antworten