Stromverbrauch in while-Schleifen

  • Hi,

    Ich bin noch Raspberry-Anfänger und mir fiel gleich auf, dass in vielen Python-Skripten oft "while 1" verwendet wird oder ähnliche Endlosschleifen.

    Ich würde mein Programm gerne so schreiben, dass es so wenig wie möglich Strom verbraucht (ich habe langfristig vor, den Pi an einen Akku anzuschließen mit automatischen Herunterfahren wenn Akku leer und so - da gibt es ja schon fertige Lösungen).

    Sind solche Endlos-Schleifen nicht ungünstig wegen dem Stromverbrauch? Würde es nicht Sinn machen, in der while-Schleife ab und zu zu schlafen oder besser eventbasiert zu handeln? Ich komme eher aus der C#-Ecke und wenn ich da in einer Endlosschleife bin, ist meistens einer meiner "Prozessoren" voll ausgelastet (und braucht somit auch sicherlich mehr Strom).

    Danke schon mal auf eure Antworten.

  • Das so allgemein alles abzuhandeln ist mühsam....

    Wie viel in einem Programm abgearbeitet wird hat erst mal kaum Einfluss auf den Stromverbrauch. Erst wenn das Programm eine höher CPU Auslastung zur Folge hat steigt auch der Stromverbrauch weil der SoC dann höher getaktet wird.
    Standardmäßig taktet der CPU des Pi's auf 600MHz wenn er nichts zu tun hat. Steigt die Auslastung auf ~50% taktet der CPU auf "maximum" also beim Pi3 auf 1200MHz und verbraucht dann natürlich auch mehr Strom. Steigt die Auslastung aller Cores dann auf 100% verbraucht der CPU ebenfalls mehr.

    Wichtig ist erst mal das eine while Schleife als solches ziemlich schnell rotiert und somit nahezu 100% CPU Auslastung verursacht:
    [code=php]import time

    while True:
    print time.time()
    [/php]
    Was hier ausbremst ist die Ausgabe durch das print, ansonsten würde die Schleife noch schneller rotieren.

    Um sowas zu verhindern sorgt man dafür dass die Schleife ganz leicht ausgebremst wird, entweder indirekt weil man mehr dort macht, oder direkt durch ein "time.sleep(0.01)" wodurch das Script dort halt für 0.01s blockiert wird.
    [code=php]import time

    while True:
    print time.time()
    time.sleep(0.01)
    [/php]

    Es ist nicht in allen Situationen sinnvoll eine while Schleife zu verwenden. Eigentlich nur dann wenn sich beispielsweise eine Messung ständig und selbstständig wiederholen soll.
    Wenn du GPIO's nutzen willst wäre eine Interrupt-Steuerung sinnvoller, dann bedarf es auch keiner while.

    Ein Programm was 100% CPU Auslastung verursacht ist oftmals schlecht geschrieben oder schlicht die Hardware zu undersized.

    Was auch noch beachtet werden sollte ist dass ein Prozess auch nur auf einem Core läuft. Wenn ein Programm mehrere Sachen gleichzeitig abhandeln soll werden mehrere Threads eingesetzt die wiederum auf mehrere Cores aufgeteilt werden und somit kein einzelner Core zu 100% ausgelastet wird sondern eben gleichmäßig aufgeteilt.


    Am besten wärs du erklärst wie du zu diesen Gedanken kommst, was du erreichen oder machen willst. Es wäre einfacher darauf spezifische Antworten zu liefern.

  • Hallo BigRed,

    da hast Du Recht. Ich habe das gerade mal ausprobiert. Ein Programm, dass einen Core des RPi 3B zu 100 % auslastet, lässt den Stromverbrauch um 40 mA ansteigen.

    Ein Delay von 50 ms erzeugt weder CPU-Last noch einen erkennbar gestiegenen Stromverbrauch. Daraus ergibt sich ein Zusammenhang zwischen CPU-Auslastung und Stromverbrauch.

    Generell sollte man Software so stricken, dass sie die zur Verfügung stehenden Ressourcen nicht zu sehr beansprucht.

    Diese jeweils 40 mA konnte ich auch messen, wenn ich die anderen schrittweise Cores zu 100% ausgelastet habe.

    Beste Grüße

    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

    Einmal editiert, zuletzt von Andreas (9. Januar 2017 um 07:21)

  • ...bei 4 Cores die alle zu 100% ausgelastet werden, würde das rein theoretisch einen gestiegenen Mehrverbrauch von 160mA bedeuten.

    Laut Raspi.TV beträgt der Verbrauch bei Multi-Core Anwendungen beim Pi3B allerdings insgesamt 720mA , wohingegen der Pi2B nur auf 420mA kommt.
    Bedeutet:
    Werden beim Pi3B alle 4 Cores voll ausgelastet werden 430mA mehr benötigt als wenn keiner der Cores etwas zu tun hat (idle). Ein Pi2B würde nur 190mA mehr benötigen, hat aber auch weniger Rechenleistung.

    Trotzdem sollte man es vermeiden alle Cores voll auszulasten, nicht nur dass das Programm dann Optimierungsbedarf hätte sondern auch weil dadurch das gesamte System leidet, weil ja nicht nur das eine Programm läuft sondern auch noch zich andere Prozesse vom Betriebssystem. Von der weitaus höheren Abwärme die dadurch verursacht ganz zu schweigen.

  • Hi!

    Vielen Dank für eure Antworten. Es ist also eher ungünstig, z.B. einen Eingang über eine while -Schleife auszulesen, so wie ich das verstanden habe.
    Es liegt sicherlich in der Aufgabe des Programmieren, die CPU nicht allzu sehr auszulasten. Schade, dass sehr viele Beispiele (z.B. für Anfänger) auf dem while 1 Prinzip beruhen!!!!
    Also mein Hauptziel ist, dass das Python-Programm nicht beendet wird, sondern Endlos die von mir auferlegten Aufgaben ausführt. Solange diese nicht von irgendwelchen Inputs abhängig sind, kann ich die "Zeit zum Schlafen" ja ganz gut an meine Begebenheiten anpassen. Meistens reicht es ja, wenn ich pro Durchgang eine Sekunde schlafe.
    Welche Event basierten Ansätze gibt es denn? Kann man mit python, was für mich eher wie eine Skript-Sprache aussieht, überhaupt Events abbilden? Wenn ja: wie reagiere ich auf:

    • Ein Input hat sich geändert
    • Ein anderes (z.B. php) Skript möchte, dass ich etwas ausführe


    Danke schon Mal!
    Automatisch zusammengefügt:


    Hi!

    Vielen Dank für eure Antworten. Es ist also eher ungünstig, z.B. einen Eingang über eine while -Schleife auszulesen, so wie ich das verstanden habe.
    Es liegt sicherlich in der Aufgabe des Programmieren, die CPU nicht allzu sehr auszulasten. Schade, dass sehr viele Beispiele (z.B. für Anfänger) auf dem while 1 Prinzip beruhen!!!!
    Also mein Hauptziel ist, dass das Python-Programm nicht beendet wird, sondern Endlos die von mir auferlegten Aufgaben ausführt. Solange diese nicht von irgendwelchen Inputs abhängig sind, kann ich die "Zeit zum Schlafen" ja ganz gut an meine Begebenheiten anpassen. Meistens reicht es ja, wenn ich pro Durchgang eine Sekunde schlafe.
    Welche Event basierten Ansätze gibt es denn? Kann man mit python, was für mich eher wie eine Skript-Sprache aussieht, überhaupt Events abbilden? Wenn ja: wie reagiere ich auf:

    • Ein Input hat sich geändert
    • Ein anderes (z.B. php) Skript möchte, dass ich etwas ausführe


    Danke schon Mal!

    Hab grad selbst einige Beispiele gefunden. U m die while -Schleife werde ich nicht drumrum kommen... trotzdem kann ich ja eine Sekunde warten, während ich Veränderungen als add_event_detect - Thread ausführe?... danke für eure Antworten nochmal. Die haben meine Vermutungen bestätigt.

    Einmal editiert, zuletzt von BigRed (9. Januar 2017 um 02:29)


  • Vielen Dank für eure Antworten. Es ist also eher ungünstig, z.B. einen Eingang über eine while -Schleife auszulesen, so wie ich das verstanden habe.

    Nein nicht zwangsläufig. Siehe Beitrag#2


    Also mein Hauptziel ist, dass das Python-Programm nicht beendet wird, sondern Endlos die von mir auferlegten Aufgaben ausführt.

    Das eine hat mit dem anderen nicht viel zu tun. Damit das Script permanent läuft behelfen sich viele der simpelsten Möglichkeit einer "while True", ja, aber das ist nur in selten Fällen sinnvoll.... Das Script pauschal eine Sekunde zu blockieren ist ebenfalls nicht immer sinnvoll.

    Aber wie ich bereits sagte, sowas arg verallgemeinert abzuhandeln ist echt mühsam. Einfacher wärs wenn du beschreibst was DU vor hast.

  • Ich hab mehrere Dinge vor, die mir selbst allerdings auch nicht ganz klar sind ;)

    Im ersten Schritt möchte ich den Pi kontrolliert runter fahren, wenn ein Eingang auf HIGH geschaltet wird. Ich hätte dafür jetzt einen Add_event_detect gemacht und in der while Schleife auf eine Variable geprüft (also nicht while 1, sondern while <variable==1>) und im Callback hätte ich die Variable auf 0 gesetzt (vor der while-Schleife natürlich auf 1). In der while-Schleife dann jedes Mal 1 sek. schlafen und nach der while-Schleife ein shutdown.


  • Im ersten Schritt möchte ich den Pi kontrolliert runter fahren, wenn ein Eingang auf HIGH geschaltet wird. Ich hätte dafür jetzt einen Add_event_detect gemacht und in der while Schleife auf eine Variable geprüft (also nicht while 1, sondern while <variable==1>) und im Callback hätte ich die Variable auf 0 gesetzt (vor der while-Schleife natürlich auf 1). In der while-Schleife dann jedes Mal 1 sek. schlafen und nach der while-Schleife ein shutdown.

    Auch wenn das zu viel des guten wäre, könnte in der while Schleife dann problemlos eine minimale Verzögerung von 0.01s sein damit das Script nicht 100% CPU Auslastung verursacht.
    Der Weg über eine separate while Schleife - so wie du ihn angedacht hast - macht man in dem Interrupt Fall nur bei aufwendigeren Programmen. Da das Script aber nur ein "shutdown" auslösen/durchführen soll kannst du auf eine while verzichten. Einfach direkt in der Callback bei Flankenwechsel den Shutdown durchführen da das Script anschließend eh beendet wird

    Alternativ könnte man anstatt add_event_detect, Callback und einer blockierenden while Schleife auch ganz einfach wait_for_edge verwenden, das blockiert das Script ab der Zeile ebenso wie eine while.

    Besser noch wäre http://raspberrypi.stackexchange.com/questions/4827…nning-only-once da RPi.GPIO schon recht lange nicht mehr weiterentwickelt wird und gpiozero einfach genial ist.
    Vergleich von ein und dem selben Ergebnis:

    RPi.GPIO
    [code=php]
    #!/usr/bin/python3
    from time import time, sleep
    from RPi import GPIO

    GPIO.setmode(GPIO.BCM)

    LEFT_PIR = 20
    RIGHT_PIR = 21

    GPIO.setup(LEFT_PIR, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.setup(RIGHT_PIR, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

    last_update = time()
    last_reset = last_update

    def pir_change(channel):
    print("Change")
    global last_reset
    last_reset = time()

    GPIO.add_event_detect(LEFT_PIR, GPIO.RISING, callback=pir_change, bouncetime=1000)
    GPIO.add_event_detect(RIGHT_PIR, GPIO.RISING, callback=pir_change, bouncetime=1000)

    while True:
    if time() - last_update >= 10:
    print("Idle %d secs" % (time() - last_reset))
    last_update = time()
    sleep(0.02)
    [/php]

    gpiozero
    [code=php]
    #!/usr/bin/python3
    from time import time, sleep
    from gpiozero import MotionSensor

    left = MotionSensor(20)
    right = MotionSensor(21)

    last_update = time()
    last_reset = last_update

    def pir_change():
    print("Change")
    global last_reset
    last_reset = time()

    left.when_motion = pir_change
    right.when_motion = pir_change

    while True:
    if time() - last_update >= 10:
    print("Idle %d secs" % (time() - last_reset))
    last_update = time()
    sleep(0.02)
    [/php]


    PS: Das geht aber mittlerweile am ursprünglichen Thema vorbei, daher wärs besser du erstellst einen neuen Thread um Codedetails deines Vorhabens zu bequatschen.

  • Hi!

    Super! Vielen Dank für die Hilfe erst Mal! Das hat mir schon mal ein viel besseres Bild verschafft.

    Das mit dem Shutdown im Callback habe ich mir auch überlegt.

    Jetzt kommt allerdings mein zweites Vorhaben: Ich möchte eine Message-Box aufmachen, in der der Benutzer 10 Sekunden Zeit hat, den Shutdown abzubrechen. Ich würde dann nämlich eher die Message-Box (die ja wahrscheinlich blockierend ist) im Callback aufpoppen lassen. Die while-Schleife würde dann nach einem bestimmten Timeout (sleep) den Shutdown übernehmen, wenn ich ihn durch eine "cancel"-Variable im Callback nicht überspringe. Störe ich damit irgendwie die Raspberry-Pi-Verarbeitung der GPIO-Ports, wenn ich an der Stelle, also im Callback, blockiere? Kann es sein, dass andere Interrupts nicht verarbeitet werden, bis ich den Callback freigebe?

  • GUIs und Whileschleifen vertragen sich nicht. Wenn du so etwas willst, starte einen Timer. Es haengt jetzt etwas vom verwandten GUI-Framework ab. Mit Tk bist du da recht limitiert:

    In einem per after-methods erzeugten Callback pollst du eine Variable, die du im event-callback des GPIO Systems (also einem anderen Thread) setzt.

    Im Timer-Callback öffnest du dann deine Messagebox, und startest vorher einen Timer, der dann den Shutdown ausloest. Oder bei "nein" als Antwort eben den Timer cancelt.

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!