2016-03-04 14 views
5

Questo è un esempio di base di che cosa sto parlando:Python, calcolando lo stato della computazione rallenta il calcolo in sé

contare da 0 a 10000000

import time 

k = 0 

beginning = time.time() 

while k < 10000000: 

    k = k+1 

elapsed = round(time.time()-beginning, 5) 

print (elapsed) 

Ora, con una sorta di " stato" della percentuale (display ogni 1 secondo) di calcolo:

import time 

k = 0 

beginning = time.time() 
interval = time.time() 

while k < 10000000: 

    if time.time() - interval > 1: 
     print (round((k/100000), 5)) 
     interval = time.time() 

    k = k+1 

elapsed = round(time.time()-beginning, 5) 

print (elapsed) 

il primo esempio prende 3.67188 secondi, il secondo esempio prende 12.62541 secondi. Immagino sia perché gli script devono controllare continuamente se è trascorso 1 secondo. C'è un modo per risolvere questo problema? Ho trovato qualcosa su thread e multiprocesso ma non riesco a capire come implementarlo. Grazie

+0

Non ha provato questa implementazione ma qualcosa di simile funziona? https://docs.python.org/2/library/threading.html#timer-objects – Tom

+0

Probabilmente potresti racchiudere il calcolo in una sorta di classe 'Task' e avere un altro thread per controllare periodicamente lo stato di tale attività. Un po 'vago, però. –

+0

Dipende dal processore. Quanti core hai? ho provato il secondo esempio su un imac e mostra l'output '39.0 79.0 2.53144' Sto pensando ci sono voluti 2.53s – pbu

risposta

2

Benchmarking

ho scritto diverse soluzioni e confrontarli. Dovevo moltiplicare il tuo valore per 10 per ottenere risultati misurabili. Innanzitutto senza alcuna misurazione del progresso per vedere quanto velocemente funziona sulla mia macchina.

def process_without_measuring(self): 
    self._start = time.time() 

    self._k = 0 
    while self._k < 100000000: 
     self._k = self._k+1 

    print (time.time() - self._start) 

ottengo una durata di 13,8 secondi.

Cominciamo con l'implementazione:

def process_with_original_measuring(self): 
    self._start = time.time() 
    next = self._start 
    self._k = 0 
    while self._k < 100000000: 
     self._k = self._k+1 
     if time.time() - next > 1: 
      print (round((self._k/1000000), 5)) 
      next = time.time() 

    print("duration:") 
    print (time.time() - self._start) 

Se corro questo, ho una durata 30,31 secondi e circa 3 percentuali fino al secondo. Il problema è che deve confrontare il tempo ogni ciclo e fare un'operazione aritmetica. È possibile ridurre il tempo modificando il loop:

def process_with_manual_measuring(self): 
    self._start = time.time() 
    next = self._start + 1 
    self._k = 0 
    while self._k < 100000000: 
     self._k = self._k+1 
     if time.time() > next: 
      print (round((self._k/1000000), 5)) 
      next = time.time() + 1 

    print("duration:") 
    print (time.time() - self._start) 

invece di sottrarre i timestamp ogni ciclo a calcolare la prossima timestamp solo una volta e paragono ad esso. Questo ovviamente non è molto veloce, ma più veloce di prima. Mi porta a 22,0 secondi, quindi risparmiando 8 secondi solo rimuovendo questa operazione.

Con un oggetto timer come filo si ottiene un risultato molto migliore, ed è il modo preferibile:

def show_progress(self): 
    print (round((self._k/1000000), 5)) 
    self._timer = Timer(1, self.show_progress) 
    self._timer.start() 

def process_with_timer(self): 
    self._start = time.time() 
    self._timer = Timer(1, self.show_progress) 
    self._timer.start() 
    self._k = 0 
    while self._k < 100000000: 
     self._k = self._k+1 
    self._timer.cancel() 

    print("duration:") 
    print (time.time() - self._start) 

L'esecuzione di questo ho un'uscita del 7 per cento in più ogni secondo e si è fatto dopo 13,8 secondi. Come puoi vedere, nessuna differenza. Ci sono solo poche altre chiamate da fare e queste sono fatte in pochissimo tempo.

Come utilizzare classe Timer

Il costruttore di Timer aspetta una durata di tempo in pochi secondi e un metodo da chiamare dopo il tempo trascorso. Puoi usare il metodo di classe, una funzione o un'espressione lambda. Dopo la costruzione è necessario avviare il timer con start().

Il primo timer viene avviato dal processo stesso. Dopo questo con ogni chiamata del timer viene avviato un nuovo timer per ottenere un intervallo di un secondo. Al termine del processo ricordarsi di chiamare cancel() sul timer. Altrimenti funzionerà senza fine perché si riavvierà ogni secondo.

Come eseguire gli esempi

Si prega di notare che i metodi di cui sopra sono i metodi della classe in modo da guardare indentazione.

import time 
from threading import Timer 

class ProgressMeter: 
    def __init__(self): 
     self._k = 0 
     self._timer = 0 

    # insert above methods with indentation as same level as __init__(self): 

Per eseguirli è sufficiente creare un'istanza di ProgressMeter e chiamare il metodo desiderato.

meter = ProgressMeter() 
meter.process_with_timer() 
+0

Scusa, questa potrebbe essere una domanda stupida, ma come la eseguo? – Matteo

+0

Bello, proverò più tardi e fornirò un feedback, grazie – Matteo

+0

Gradirei un feedback. – DreyFax

1

Se si utilizza un sistema simile a Unix, è possibile utilizzare signal.alarm dal modulo signal.

Il seguente codice è un po 'disordinato, ma potresti renderlo più ordinato incapsulando le cose in una classe.

import time 
import signal 

# Alarm delay in seconds. Must be an integer. 
alarm_delay = 1 

def handler(signum, frame): 
    report() 

    # Request a new SIGALRM signal 
    signal.alarm(alarm_delay) 

# Set a handler for the SIGALRM signal 
signal.signal(signal.SIGALRM, handler) 

# Request the initial SIGALRM signal 
signal.alarm(alarm_delay) 

# Report current progress 
def report(): 
    print(round(k/100000, 5)) 

k = 0 
beginning = time.time() 
while k < 10000000: 
    k = k + 1 

elapsed = round(time.time() - beginning, 5) 
print (elapsed) 

# Cancel the pending alarm 
signal.alarm(0) 

uscita tipica sulla mia macchina 2GHz.

18.90911 
35.98427 
50.17902 
64.53358 
83.42723 
5.94397