2012-12-15 7 views
5

Abbastanza semplice, mi piacerebbe eseguire un comando/programma esterno da uno script Python, una volta finito vorrei anche sapere quanto tempo CPU consumato.Esegui un comando esterno e ottieni la quantità di CPU consumata

Modalità hard: l'esecuzione di più comandi in parallelo non causerà inesattezze nel risultato della CPU consumata.

+2

Questo deve essere multipiattaforma? In caso contrario, su quale sistema operativo dovrebbe funzionare? La maggior parte dei sistemi operativi fornisce l'accesso a diversi timer, ma quelli più accurati potrebbero richiedere privilegi elevati. –

+0

Solo Linux va bene, la compatibilità con Windows è apprezzata ma non necessaria. –

risposta

10

In UNIX: (a) utilizzare modulo resource (vedi anche risposta di icktoofay), oppure (b) utilizzare il comando time e analizzare i risultati, o (c) utilizzare /proc file system, analizza/proc/[ pid]/stat e analizza i campi utime e . L'ultimo di questi è specifico per Linux.

Esempio di utilizzo resource:

import subprocess, resource 
usage_start = resource.getrusage(resource.RUSAGE_CHILDREN) 
subprocess.call(["yourcommand"]) 
usage_end = resource.getrusage(resource.RUSAGE_CHILDREN) 
cpu_time = usage_end.ru_utime - usage_start.ru_utime 

Nota: non è necessario fare forcella/execvp, subprocess.call() o gli altri metodi sono subprocess bene qui e più facile da usare.

Nota: è possibile eseguire più comandi dello stesso script python simultaneamente sia utilizzando subprocess.Popen o subprocess.call e le discussioni, ma risorsa non tornerà il loro corretto singoli tempi di CPU, restituirà la somma del loro tempo in tra le chiamate a getrusage; per ottenere le singole volte, esegui un piccolo wrapper python per comando per crearlo come sopra (potrebbe avviarlo dal tuo script principale), o usare il metodo time al di sotto del quale corrisponderà al correttamente con più comandi simultanei (il tempo è fondamentalmente solo tale un involucro).

Esempio di utilizzo di time:

import subprocess, StringIO 
time_output = StringIO.StringIO() 
subprocess.call(["time", "yourcommand", "youroptions"], stdout=time_output) 
# parse time_output 

Su Windows: È necessario utilizzare i contatori delle prestazioni (aka "aiutanti di dati di performance") in qualche modo. Ecco un C example dell'API sottostante. Per ottenerlo da Python, è possibile utilizzare uno dei due moduli: win32pdh (parte di pywin32; sample code) o pyrfcon (multipiattaforma, funziona anche su Unix; sample code).

Ognuno di questi metodi soddisfa effettivamente i requisiti della "modalità hard" di cui sopra: devono essere precisi anche con più istanze in esecuzione di processi diversi su un sistema occupato. In questo caso, potrebbero non produrre esattamente gli stessi risultati rispetto all'esecuzione di un solo processo su un sistema inattivo, poiché il cambio di processo ha un certo overhead, ma saranno molto vicini, perché alla fine ottengono i loro dati dallo scheduler del SO.

+0

+1 con l'osservazione che l'utilizzo di '/ proc' in questo modo è specifico per Linux. –

3

È possibile eseguire le temporizzazioni all'interno di Python, ma se si desidera conoscere il consumo complessivo della CPU del programma, è piuttosto stupido. La cosa migliore da fare è usare semplicemente il programma GNU time. Viene persino fornito nella maggior parte dei sistemi operativi.

+0

Voglio eseguire questo comando a livello di codice da uno script Python, ho reso la mia domanda un po 'più chiara. Voglio essere in grado di ottenere il risultato del comando e la quantità di tempo della CPU utilizzata. –

1

Si può fare questo usando del %time magic function ipython:

In [1]: time 2**128 
CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s 
Wall time: 0.00 
Out[1]: 340282366920938463463374607431768211456L 

In [2]: n = 1000000 

In [3]: time sum(range(n)) 
CPU times: user 1.20 s, sys: 0.05 s, total: 1.25 s 
Wall time: 1.37 
Out[3]: 499999500000L 
+1

Sarebbe bello sapere perché questo è stato downvoted ... –

4

Sulle piattaforme in cui è disponibile, the resource module può fornire quello che ti serve. Se è necessario programmare più comandi contemporaneamente, è possibile che si desideri eseguire (per ciascun comando che si desidera eseguire) fork e quindi creare il sottoprocesso in modo da ottenere informazioni solo per tale processo. Ecco un modo si potrebbe fare questo:

def start_running(command): 
    time_read_pipe, time_write_pipe = os.pipe() 
    want_read_pipe, want_write_pipe = os.pipe() 
    runner_pid = os.fork() 
    if runner_pid != 0: 
     os.close(time_write_pipe) 
     os.close(want_read_pipe) 
     def finish_running(): 
      os.write(want_write_pipe, 'x') 
      os.close(want_write_pipe) 
      time = os.read(time_read_pipe, struct.calcsize('f')) 
      os.close(time_read_pipe) 
      time = struct.unpack('f', time)[0] 
      return time 
     return finish_running 
    os.close(time_read_pipe) 
    os.close(want_write_pipe) 
    sub_pid = os.fork() 
    if sub_pid == 0: 
     os.close(time_write_pipe) 
     os.close(want_read_pipe) 
     os.execvp(command[0], command) 
    os.wait() 
    usage = resource.getrusage(resource.RUSAGE_CHILDREN) 
    os.read(want_read_pipe, 1) 
    os.write(time_write_pipe, struct.pack('f', usage.ru_utime)) 
    sys.exit(0) 

È quindi possibile utilizzarlo per eseguire alcuni comandi:

get_ls_time = start_running(['ls']) 
get_work_time = start_running(['python', '-c', 'print (2 ** 512) ** 200']) 

Dopo che il codice ha eseguito, entrambi questi comandi deve essere in esecuzione in parallelo. Quando si vuole attendere per loro di finire e ottenere il tempo hanno preso per l'esecuzione, chiamare la funzione restituita da start_running:

ls_time = get_ls_time() 
work_time = get_work_time() 

Ora ls_time conterrà il tempo ls preso per l'esecuzione e work_time conterrà il tempo python -c "print (2 ** 512) ** 200" preso eseguire.

1

Il modulo timeit di python è molto utile per scopi di benchmarking/profiling. Oltre a questo puoi persino chiamarlo dall'interfaccia della riga di comando. Per punto di riferimento di un comando esterno, si dovrebbe andare in questo modo:

>>> import timeit 
>>> timeit.timeit("call(['ls','-l'])",setup="from subprocess import call",number=1) #number defaults to 1 million 
total 16 
-rw-rw-r-- 1 nilanjan nilanjan 3675 Dec 17 08:23 icon.png 
-rw-rw-r-- 1 nilanjan nilanjan 279 Dec 17 08:24 manifest.json 
-rw-rw-r-- 1 nilanjan nilanjan 476 Dec 17 08:25 popup.html 
-rw-rw-r-- 1 nilanjan nilanjan 1218 Dec 17 08:25 popup.js 
0.02114391326904297 

L'ultima riga è il tempo di esecuzione restituito. Qui, il primo argomento a timeit.timeit() è il codice per chiamare il metodo esterno e l'argomento setup specifica il codice da eseguire prima dell'inizio della misurazione del tempo. L'argomento number è il numero di volte in cui si desidera eseguire il codice specificato e quindi è possibile dividere il tempo restituito dallo number per ottenere il tempo medio.

È inoltre possibile utilizzare il metodo timeit.repeat() che prende argomenti simili a quelli timeit.timeit() ma richiede un ulteriore argomento repeat per specificare il numero di volte timeit.timeit() dovrebbe essere chiamato e restituisce una lista di tempi di esecuzione per ogni corsa.

Nota: il tempo di esecuzione restituito dal metodo timeit.timeit() è l'ora dell'orologio a parete, non il tempo della CPU. Quindi, altri processi potrebbero interferire con i tempi. Quindi, nel caso di timeit.repeat(), devi prendere il valore minimo anziché provare a calcolare la deviazione media o standard.

Problemi correlati