Python Language
Ottimizzazione delle prestazioni
Ricerca…
Osservazioni
Quando si tenta di migliorare le prestazioni di uno script Python, prima di tutto si dovrebbe essere in grado di trovare il collo di bottiglia del proprio script e notare che nessuna ottimizzazione può compensare una scelta sbagliata nelle strutture dati o un difetto nella progettazione dell'algoritmo. Identificare i colli di bottiglia delle prestazioni può essere fatto profilando il tuo script. In secondo luogo, non cercare di ottimizzare troppo presto il processo di codifica a scapito della leggibilità / design / qualità. Donald Knuth ha rilasciato la seguente dichiarazione sull'ottimizzazione:
"Dovremmo dimenticare le piccole efficienze, diciamo circa il 97% delle volte: l'ottimizzazione prematura è la radice di tutto il male. Tuttavia non dovremmo perdere le nostre opportunità in quel 3% critico ".
Codice di profilazione
Prima di tutto dovresti essere in grado di trovare il collo di bottiglia del tuo script e notare che nessuna ottimizzazione può compensare una scelta sbagliata nella struttura dei dati o un difetto nella progettazione dell'algoritmo. In secondo luogo, non cercare di ottimizzare troppo presto il processo di codifica a scapito della leggibilità / design / qualità. Donald Knuth ha rilasciato la seguente dichiarazione sull'ottimizzazione:
"Dovremmo dimenticare le piccole efficienze, diciamo circa il 97% delle volte: l'ottimizzazione prematura è la radice di tutto il male, ma non dovremmo perdere le nostre opportunità in quel 3% critico"
Per profilare il tuo codice hai diversi strumenti: cProfile
(o il profile
più lento) dalla libreria standard, line_profiler
e timeit
. Ognuno di loro ha uno scopo diverso.
cProfile
è un profiler deterministico: la funzione chiamata, la funzione di ritorno e gli eventi di eccezione sono monitorati e vengono eseguiti tempi precisi per gli intervalli tra questi eventi (fino a 0,001 s). La documentazione della biblioteca ([ https://docs.python.org/2/library/profile.html][1]) ci fornisce un caso di utilizzo semplice
import cProfile
def f(x):
return "42!"
cProfile.run('f(12)')
O se preferisci avvolgere parti del tuo codice esistente:
import cProfile, pstats, StringIO
pr = cProfile.Profile()
pr.enable()
# ... do something ...
# ... long ...
pr.disable()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print s.getvalue()
Ciò creerà le uscite come nella tabella sottostante, in cui è possibile vedere rapidamente dove trascorre la maggior parte del tempo il programma e identificare le funzioni da ottimizzare.
3 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <stdin>:1(f)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Il modulo line_profiler
([ https://github.com/rkern/line_profiler][1]) è utile per avere un'analisi linea per linea del tuo codice. Questo ovviamente non è gestibile per script lunghi ma è indirizzato a frammenti. Vedi la documentazione per maggiori dettagli. Il modo più semplice per iniziare è usare lo script kernprof come spiegato nella pagina del pacchetto, nota che dovrai specificare manualmente le funzioni del profilo.
$ kernprof -l script_to_profile.py
kernprof creerà un'istanza di LineProfiler e la inserirà nello spazio __builtins__
nomi __builtins__
con il nome del profilo. È stato scritto per essere usato come decoratore, quindi nella sceneggiatura, decori le funzioni che vuoi profilare con @profile
.
@profile
def slow_function(a, b, c):
...
Il comportamento predefinito di kernprof è di mettere i risultati in un file binario script_to_profile.py.lprof
. Puoi dire a kernprof di visualizzare immediatamente i risultati formattati sul terminale con l'opzione [-v / - visualizza]. Altrimenti, puoi visualizzare i risultati in un secondo momento in questo modo:
$ python -m line_profiler script_to_profile.py.lprof
Infine timeit
fornisce un modo semplice per testare un liner o una piccola espressione sia dalla riga di comando che dalla shell python. Questo modulo risponderà a domande come, è più veloce fare una comprensione di lista o usare la list()
built-in list()
quando si trasforma un set in una lista. Cerca la parola chiave setup
o -s
per aggiungere il codice di configurazione.
>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.8187260627746582
da un terminale
$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop