Status bei langer Abarbeitung...

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
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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?
Zuletzt geändert von jens am Dienstag 22. August 2006, 07:00, insgesamt 1-mal geändert.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

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
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

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
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.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
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
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
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
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
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
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

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???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten