Alternative zur while-Schleife aus Performance-Sicht?

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.
Antworten
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hallo,

ich habe ein Programm, bei dem anhand von vorgegebenen Zeitlagen die Anzahl der Stunden ermittelt wird. Also z.B. Vorgabe 18 Uhr bis 02 Uhr im Zeitraum 01.04.2019 bis 30.04.2019. Die Ausgabe ist dann die Anzahl der Stunden, wie viel Stunden auf Sonntag liegen, wie viel Stunden an Feiertagen liegen und wie viel Stunden im Nachtzeitraum liegen.
Realisiert habe ich das über eine while-Schleife, die Tag für Tag die Bedingungen abprüft. Leider leidet da so ziemlich die Performance. Gerade wenn ich einen Zeitraum über ein Jahr einstelle, dauert das so einige Zeit. Das Programm läuft unter Windows mit Python3.
Gibt es eine bessere Methode als die while-Schleife für so einen Anwendungsfall?

VG
Stephan
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Verbesserungen an der Performance eines Codes ohne den Code ist schwerlich möglich.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

while-Schleifen lassen sich in vielen Fällen durch for-Schleifen ersetzen und letzteres sollte dann auch bevorzugt werden. Das ist nicht von Natur aus performanter. Jedoch ist man dann gezwungen, nochmal genauer über den Algorithmus nachzudenken, da man ja ein sinnvolles Intervall und ggf auch eine angepasste Schrittweite für die Schleife festlegen muss. Und hier liegt vermutlich auch der Knackpunkt bei dir: Dass deine Berechnung noch nicht ausgeklügelt genug ist und daher so lange braucht. Wenn du magst, dann kannst du mal deinen bisherigen Code zeigen und erhälst dann sicher Verbesserungsvorschläge für die eine oder andere Optimierung...
Benutzeravatar
DeaD_EyE
User
Beiträge: 1020
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Vielleicht kennst du diese Bibliothek: https://dateutil.readthedocs.io/en/stable/index.html

Wenn man damit z.B. von 2018-12-29 bis 2119-12-30 iteriert, dauert das bei mir ca. 5 Sekunden.
Entspricht das auch der Performance deines Programms? Falls ja, bestünde ja die Möglichkeit
dieses Modul zu nutzen. Falls das auch nichts bringt, werden wahrscheinlich die Tests, die du machst, viel Zeit in Anspruch nehmen.
Um den Wochentag zu bekommen, wird eine Methode aufgerufen. Das ist zwar optimiert worden, dauert aber trotzdem und wenn das
10000 mal wiederholt wird, summiert sich das ganze auf.

Mal ganz blöd gefragt: "Kann man das nicht einfach ausrechnen?"
Und falls ja, wie kann man verhindern, dass unnötige Berechnungen zwei mal durchgeführt werden.

Hier mal ein kleines Beispiel:

Code: Alles auswählen

import sys
from collections import defaultdict
from datetime import datetime, timedelta
from dateutil.rrule import rrule, HOURLY


def main():
    if len(sys.argv) != 3:
        print(sys.argv[0], 'iso8601-start-date iso8601-end-date')
        return 1
    start = datetime.fromisoformat(sys.argv[1])
    end = datetime.fromisoformat(sys.argv[2])
    hourly = rrule(freq=HOURLY, dtstart=start, until=end)
    result = defaultdict(int)
    for dt in hourly:
        if dt.hour > 6 and dt.hour < 18:
            result['day'] += 1
            #print('day', dt.isoformat())
        else:
            result['night'] += 1
            #print('night', dt.isoformat())
    print(result)
    return 0

if __name__ == '__main__':
    sys.exit(main())
Die Einteilung von Tag vs Nacht soll auch nur ein Beispiel sein.
Teste das mal für einen Zeitraum von 1000 Jahren :-D
Das dauert ein bisschen (unter einer Minute bei mir).
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hallo,

vielen Dank für die Hinweise. Wenn ich über ein ganzes Jahr iteriere, dauert das mehrere Minuten. Da ich aber das ganze Programm überarbeiten möchte, werde ich die Vorschläge einbauen. An das Modul dateutil habe ich nicht gedacht.
Anscheinend brauchen bei mir die ganzen Berechnungen zusätzlich zu viel Zeit. Ich muss das ganze für verschiedene Zeitintervalle (das sind i.d.R. Arbeitsschichten) und verschiedene Zeiträume testen. Also z.B. 01.01.2019 bis 31.12.2019 von 18:00 Uhr bis 02:00 Uhr, 01.03.2019 bis 30.09.2020 von 06:00 Uhr bis 13:50 Uhr, usw. Diese Werte kommen aus einer wxPython-GUI.

Bild
Daher rechne ich auch mit wxPython.

Code: Alles auswählen

        while shift_start.IsEarlierThan(loop_stop_date):
            logging.info('Schichtbeginn: %s, Schichtende: %s'%(shift_start, shift_end))
            if self.weeekday_is_valid(shift_start):
                self.shift_time += shift_end - shift_start
                during = shift_end - shift_start
                logging.info('Schichtdauer: %s' % during.Format('%H:%M'))
                nh = NightHours(shift_start, shift_end)
                self.night_hours += nh.get_values()
                sh = SundayHours(shift_start, shift_end)
                self.sunday_hours += sh.get_values()

                hh = HolidayHours(shift_start, shift_end, self.holiday_list)
                self.holiday_hours += hh.get_values()
            else:
                logging.info('Schicht nicht gültig')

            shift_start += wx.DateSpan().Day()

            shift_end += wx.DateSpan().Day()
Als erstes errechne ich mir die Feiertage und speichere die in einer Liste. Dann geht es in die Schleife mit den Berechnungen.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Also ein paar Minus- und Plus-Berechnungen sind es bestimmt nicht. Da steckt sicher noch viel im Detail, was du im vorliegenden Code aber nicht zeigst. Entweder du zeigst mehr Details oder - falls du es nicht möchtest oder darfst - wir können nur mit allgemeinem Blabla helfen.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1020
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Du könntest ja auch ein Profil erstellen. Am besten nur die Funktion ohne GUI.

Code: Alles auswählen

python3 -m cProfile -o calc_hours.prof calc_hours.py argumente ...
# pip install snakeviz
# https://jiffyclub.github.io/snakeviz/
snakeviz calc_hours.prof
Könnte dann so aussehen: https://screenshots.firefox.com/G0P9JYE ... /127.0.0.1

Bei dir wird sicherlich folgender Teil ins Gewicht fallen:

Code: Alles auswählen

            shift_start += wx.DateSpan().Day()
            shift_end += wx.DateSpan().Day()
Ohne jetzt das Framework zu kennen, vermute ich mal, dass hier jedes mal ein Objekt aus der Klasse DateSpan erstellt wird und dessen Methode Day aufgerufen wird.
Dann wird das Objekt direkt verworfen. Das passiert zwei mal hintereinander in der Schleife.
Reicht es nicht aus, wenn man diesen Wert einem Namen außerhalb der Schleife zuweist und diesen wieder verwendet?
Wird denn da zurückgegeben? Woher kommt der Wert?

Außerdem erstellst du jedes mal neue Objekte aus den Klassen NightHours, SundayHours, HolidayHours.
Kann man die nicht auch außerhalb der Schleife erstellen und dann innerhalb der Schleife die Instanzen verwenden?
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Hier drin

Code: Alles auswählen

nh = NightHours(shift_start, shift_end)
self.night_hours += nh.get_values()
kann sehr viel sehr lange dauern.
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@Mawilo: das ist ja kein Code, den irgendwer ausführen kann. Was ist `NightHours` und `SundayHours` und `HolidayHours`? Dieses `weeekday_is_valid` sieht komisch aus. Gibt es auch nicht gültige Woochentage? Was heißt den get_values? Zeig vollständigen Code der auch ausführbar ist.
Antworten