Seite 1 von 1

Status bei langer Abarbeitung...

Verfasst: Freitag 22. April 2005, 12:33
von jens
Wenn man in einer Schleife sehr viele Dinge abarbeiten läßt und das eine weile dauert, dann wäre es nicht schlecht einen Status (Fortschrittanzeige) zu erhalten.

Beispielsweise kommt das bei der Erstellung des Indexes ( http://www.python-forum.de/topic-3103.html ) vor...

Nun hab ich überlegt, wie man eine Prozentzahl nur alle x-Prozente rausbringt, aber so richtig zufrieden bin ich damit nicht:

Code: Alles auswählen

def abarbeiten( liste ):
    Anzahl = len(liste)

    threshold = Anzahl / 10
    status = 0
    perc = 0
    for i in xrange( Anzahl ):
        status += 1
        if status >= threshold:
            perc += 10
            print perc,"% verarbeitet"
            status = 0

        # Die eigentliche Aktion, die mit der Liste passieren soll
        liste[i] = int( liste[i] ) + 10


TestListe = [str(i) for i in xrange(100000)]

abarbeiten( TestListe )
Wie kann man es einfacher Lösen?

Verfasst: Freitag 22. April 2005, 14:50
von CM
Hi Jens,

ich finde Deinen Ansatz eigentlich gar nicht übel. Na gut, bei einer "richtigen" Anwedung würde man vielleicht ein paar Dinge anpassen (z. B. das man den threshold extern festlegt oder ein etwas informativerer Output), aber ansonsten ist das genau das, was man bei vielen command line Anwendungen sieht.

Ein Vorschlag noch: Ich würde ggf. "\b" nutzen, um wieder auf die Ausgangsposition zurückzufahren und dann erst neue Information ausschreiben - sonst wird die Ausgabe u. U. trotz alledem ewig lang und nicht gerade übersichtlich.

Gruß,
Christian

Verfasst: Freitag 22. April 2005, 14:58
von jens
Durch threshold = Anzahl / 10 ist die Anzeige immer nur 10 Zeilen lang, egal wie viele durchläufe es gibt...
\b ist für eine Konsolenanwendung super, aber für eine Ausgabe im Browser nicht ;)

Ich dachte nur, das man das auch irgendwie schneller/kürzer hinbekommen müßte...

Verfasst: Freitag 22. April 2005, 15:08
von CM
Ja, richtig: In Deinem Beispiel mit der prozentualen Ausgabe kann es nicht mehr als zehn derartige Ausgaben geben. Ich bin aber davon ausgegangen, daß man sehr viele Ausgaben hat und ggf. das auch nicht prozentual ausdrücken kann.
So habe ich z. B. meine letzte Woche bei Messungen mit einer SUN-Workstation verbracht, wo u. A. eine Schiene immer wieder gaaaanz langsam auf eine neue Position gefahren wurde. Dabei wurde einfach immer wieder die Position in mm ausgegeben. Bei anderen Geschichten wußte man gar nicht erst wo die letzte Ausgabe sein würde, weil diese immer von mehreren anderen Parameter abhingen - aber prinzipiell reicht Deine Art der Ausgabe eben auch hierfür aus.

Mit "\b" hast Du natürlich auch Recht: Aber das es ein cgi-Skript werden sollte kann ich natürlich nicht riechen ;-). Dann ist halt Deine Phantasie gefragt ...

Gruß,
Christian

Verfasst: Freitag 22. April 2005, 23:43
von BlackJack
'\b' geht doch immer nur 1 Zeichen zurück, oder? Ich würde eher '\r' nehmen, das geht an den Zeilenanfang zurück. Funktioniert natürlich auch nicht in einem CGI.

Neben dem Schwellwert für eine neue Ausgabe sollte man auch die Funktion, die auf die Liste angewendet wird, als Parameter übergeben. Dann kann man diese Fortschrittsanzeige für beliebige Funktionen immer wieder verwenden.

Verfasst: Samstag 23. April 2005, 14:24
von jens
BlackJack hat geschrieben:Dann kann man diese Fortschrittsanzeige für beliebige Funktionen immer wieder verwenden.
Das wäre Fanstastisch... Aber irgendwie kann ich mir das nicht so recht vorstellen... Im Beispiel wird etwas mit einer Liste gemacht... Was ist, wenn es ein Dict ist?

Re: Status bei langer Abarbeitung...

Verfasst: Montag 25. April 2005, 18:51
von Gast
jens hat geschrieben:

Wie kann man es einfacher Lösen?
wenn man den Modulo Operator einsetzt, kann man die status Variable weglassen: statt einen integer hochzuzählen und wieder auf null zu setzen, testet man direkt gegen den treshhold Value.

Da man perc auch direkt berechnen kann, braucht eine Fortschritsbalken Funktion nur die totale Anzahl und die aktuelle:

Code: Alles auswählen

def print_process(i, total):
    tresh = total / 10
    if tresh == 0:
        tresh = 1
    if i % tresh == 0:
        print "%5.2f" % (float(i)/total*100), i

import sys
total = int(sys.argv[1])

for i in xrange(total):
    print_process(i, total)

Grüße
Michael

Verfasst: Montag 25. April 2005, 20:37
von jens
Bei meinem Test geht's aber nur bis 90% ?

Code: Alles auswählen

def print_process(i, total):
    tresh = total / 10
    if tresh == 0:
        tresh = 1
    if i % tresh == 0:
        print "%5.2f" % (float(i)/total*100), i

TestListe = [str(i) for i in xrange(100000)]

total = len(TestListe)
for i in xrange(total):
    print_process(i, total)
0.00 0
10.00 10001
20.00 20002
30.00 30003
40.00 40004
50.00 50005
60.00 60006
70.00 70007
80.00 80008
90.00 90009

Verfasst: Dienstag 26. April 2005, 12:44
von Gast
jens hat geschrieben:Bei meinem Test geht's aber nur bis 90% ?
das hatte ich für ein Feature gehalten ;-) Die aufrufende Schleife läuft ja durch, es wird nur nichts mehr angezeigt. Wenn man fertig ist, ist man halt fertig....

Mal sehen. Wenn man die 100% auch erwähnt bekommen will ... Fällt mir zunächst auf, dass meine Version, gerne mal mehr als 10 Ausgaben liefert: wenn die Liste zum Beispiel 44 Elemente lang ist wird treshhold 4 und der passt mehr als 10 mal in 45. Ein ähnliches Problem hat der code vom OP. Bei einer Liste mit 12 Elementen printed er bis zu "120% verarbeitet" (bei mir werden die Prozente wenigstens noch richtig berechnet)

hmm. Hätte nicht gedacht, dass das kleine Problem so schwierig/ interessant werden wird. Vielleicht findet ja jemand eine Lösung.

Grüße
Michael

Verfasst: Dienstag 26. April 2005, 16:45
von jens
Eine bessere Lösung:

Code: Alles auswählen

def print_process(i, total):
    i += 1
    tresh = total / 10
    if tresh == 0:
        tresh = 1
    if i % tresh == 0:
        print "%3.i%% %4.i/%i" % ( round(float(i)/total*100), i, total)

TestListe = [str(i) for i in xrange(109)]

total = len(TestListe)
for i in xrange(total):
    print_process(i, total)
Perfekt ist die aber auch nicht (Wobei 109 Elemente auch echt eine doofe Zahl ist :):
9% 10/109
18% 20/109
28% 30/109
37% 40/109
46% 50/109
55% 60/109
64% 70/109
73% 80/109
83% 90/109
92% 100/109

Verfasst: Mittwoch 27. April 2005, 16:37
von Gast
Stimmt, den aktuellen Index um eins hochzurechnen ist schonmal eine gute Sache. Dann muss man wohl noch den treshhold value als floating point berechnen, damit krumme total Werte genauer aufgeteilt werden in zehn Päckchen. Der Modulo Test darf dann aber nicht mehr auf "== 0" vergleichen sondern eher auf "< 1". Außerdem brauch ich um die 100% anzuzeigen eine Extra condition. Es schaut also in etwa so aus:

Code: Alles auswählen

def print_progress(i, total):
    i += 1
    tresh = total / 10.
    if i % tresh < 1 or i == total:
        proc = float(i)/total*100
        print "%4.1f (%s/%s)" % (proc, i, total)

import sys
total = int(sys.argv[1])
for i in xrange(total):
    print_progress(i, total)
Ich müsste mich sehr tief konzentrieren, wenn ich erklären sollte, warum die Testbedingung genau so aussehen muss. Aber es liefert Output der okey ist.

Außerdem ist es ziehmlich langsam - wenn das Programm also sonst nichts zu tun hat, dann wenigstens mit der Berechnung des exakten Fortschritts-Outputs ;-) Ich will sagen: im echten Leben würde ich einfach eine Zeile

Code: Alles auswählen

if i % 1000 == 0: print "%s elements done" % i
direkt ins Programm reinschreiben und die 1000 grob anpassen, so dass die Meldung oft-aber-nicht-zu-oft kommen.

Grüße
Michael

Verfasst: Freitag 18. November 2005, 08:55
von jens
Möchte nur mal anmerken, das ich bei md5sum dafür einen anderen Ansatz verfolgt habe.

Es ist auch eine Schleife. In dieser prüfe ich die Zeit. Wenn es eine Sek. her ist, seit der letzten Statusausgabe, dann wird eine neue gemacht und wieder eine Sek. "gewartet" bis zur nächsten...
Hier mal der relevante Teil:

Code: Alles auswählen

            f = file(self.file_name_path, "rb")
            bytesreaded = 0
            threshold = file_size / 10
            time_threshold = start_time = int(time.time())
            while 1:
                data = f.read(bufsize)
                bytesreaded += bufsize
                if not data:
                    break

                current_time = int(time.time())
                if current_time > time_threshold:

                    elapsed = float(current_time-start_time)      # Vergangene Zeit
                    estimated = elapsed / bytesreaded * file_size # Geschäzte Zeit
                    performance = bytesreaded / elapsed / 1024 / 1024

                    if estimated>60:
                        time_info = "%.1f/%.1fmin" % (elapsed/60, estimated/60)
                    else:
                        time_info = "%.0f/%.1fsec" % (elapsed, estimated)

                    sys.stdout.write("\r")
                    sys.stdout.write(
                        "%3.i%% %s %.1fMB/sec    " % (
                            round(float(bytesreaded)/file_size*100),
                            time_info,
                            performance
                        )
                    )
                    time_threshold = current_time

Verfasst: Donnerstag 17. April 2008, 15:43
von jens
Da man das öfters mal braucht, hab ich mir nochmal Gedanken gemacht, wie eine Status Ausgabe den eigentlichen Schleifenablauf am wenigsten verzögert... Das ist raus gekommen:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys, time

LOOPS = 60

start_time = time.time()
time_threshold = start_time + 1

for count in xrange(LOOPS):

    # mache normalerweise irgendwas anderes
    time.sleep(0.1) # sleep, damit man überhaupt etwas sieht

    if time.time() > time_threshold:
        # alive info every second
        current_time = time.time()
        duration = current_time - start_time
        rate = count/duration

        time_threshold = current_time + 1
        sys.stdout.write("\r")
        sys.stdout.write("%.2fitems/sec - duration: %.1fsec" % (rate, duration))
        sys.stdout.flush()

print
print "---ENDE---"
duration = time.time() - start_time
rate = count/duration
print "%.2fitems/sec - duration: %.1fsec" % (rate, duration)
Noch jemand eine Idee, wie man das ganze optimieren könnte???