Come faccio a valutare sistematicamente le prestazioni del mio script Python?

2

Come faccio a sapere se il mio codice funziona abbastanza velocemente? C'è un modo misurabile per testare la velocità e l'ampli? prestazioni del mio codice?

Ad esempio, ho uno script che legge i file CSV e scrive nuovi file CSV mentre usa Numpy per calcolare le statistiche. Sotto, sto usando cProfiler per il mio script Python ma dopo aver visto le statistiche risultanti, cosa faccio dopo? In questo caso, posso vedere che i metodi significano, astype, ridurre da numpy, la scrittura del metodo da csv e l'appendice del metodo delle liste python sta prendendo una parte significativa del tempo.

Come posso sapere se il mio codice può migliorare o no?

  python -m cProfile -s cumulative OBSparser.py
     176657699 function calls (176651606 primitive calls) in 528.419 seconds
  Ordered by: cumulative time
  ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       1    0.003    0.003  528.421  528.421 OBSparser.py:1(<module>)
       1    0.000    0.000  526.874  526.874 OBSparser.py:45(start)
       1  165.767  165.767  526.874  526.874 OBSparser.py:48(parse)
 7638018    6.895    0.000  179.890    0.000 {method 'mean' of 'numpy.ndarray' objects}
 7638018   56.780    0.000  172.995    0.000 _methods.py:53(_mean)
 7628171   57.232    0.000   57.232    0.000 {method 'writerow' of '_csv.writer' objects}
 7700878   52.580    0.000   52.580    0.000 {method 'reduce' of 'numpy.ufunc' objects}
 7615219   50.640    0.000   50.640    0.000 {method 'astype' of 'numpy.ndarray' objects}
 7668436   28.595    0.000   36.853    0.000 _methods.py:43(_count_reduce_items)
15323753   31.503    0.000   31.503    0.000 {numpy.core.multiarray.array}
45751805   13.439    0.000   13.439    0.000 {method 'append' of 'list' objects}

Qualcuno può spiegare le migliori pratiche?

    
posta Steven Chen 16.07.2016 - 08:39
fonte

5 risposte

0

How do I know if my code is running fast enough?

Dipende molto dal tuo caso d'uso - il tuo programma funziona per 1,4 ore che potrebbero essere o non essere abbastanza veloci. Se questo è un processo una tantum, 1,4 ore non sono tanto, spendere un tempo sull'ottimizzazione non vale la pena investire. D'altra parte, se questo è un processo che dovrebbe essere eseguito ad es. una volta ogni ora, chiaramente vale la pena trovare un approccio meno dispendioso in termini di tempo

Is there a measurable way to test the speed & performance of my code?

sì, profilazione - e lo hai già fatto. È un buon inizio.

what do I do next?

Le best practice includono:

  1. misura le prestazioni di base (prima di qualsiasi ottimizzazione)
  2. analizza le parti in cui il programma passa la maggior parte del suo tempo
  3. riduce la complessità in fase di esecuzione (il tipo Big-O)
  4. verifica il potenziale del calcolo parallelo
  5. confronta con le prestazioni di base

Hai già fatto 1. Quindi passiamo a 2.

Analisi

Nel tuo caso il programma passa la maggior parte del tempo in linea a OBSparser.py:48, di cui un terzo viene impiegato per calcolare la media 7638018 volte.

Come mostra l'output del profiler, questo è su un narray, cioè usando numpy, e non sembra che ci voglia molto tempo per ogni chiamata. Un rapido calcolo conferma che:

179 '/ 7.638.018 = 23,6 microsecondi per chiamata

Poiché ciò è già implementato in C-code (numpy), è probabile che non si possa fare molto per migliorare il rendimento per chiamata cambiando il codice mean effettivo (o usando un'altra libreria).

Tuttavia, poniti diverse domande:

  1. Come può essere ridotto il numero di chiamate a .mean() ?
  2. Le chiamate a .mean() possono essere implementate in modo più efficiente?
  3. I dati possono essere raggruppati e ogni gruppo può essere elaborato indipendentemente?
  4. chiedi altre domande

Altre chiamate che vale la pena guardare sono .astype() and reduce , mi sono concentrato su .mean() semplicemente per illustrazione.

Riduzione della complessità

Non sapendo che cosa fa il tuo codice, ecco i miei 5cents sulle specifiche, comunque:

On 2., un rapido controllo sul mio core i7 rivela che per ndarray.mean() prendere 20 microsecondi, questo richiede circa 50 valori. Quindi suppongo che tu stia raggruppando i valori e poi chiamando .mean() su ogni gruppo. Potrebbero esserci modi più efficienti: una ricerca su performance aggregata di gruppo numpy o qualche variante di questo potrebbe trovare utili indicazioni.

Calcolo parallelo

On 3. Immagino che la multielaborazione non sia probabilmente una soluzione qui, dal momento che i calcoli sembrano per lo più legati alla CPU e il sovraccarico di avviare attività separate e lo scambio di dati probabilmente supera i benefici.

Tuttavia potrebbe esserci qualche uso dell'approccio SIMD, cioè la vettorizzazione. Di nuovo, solo un sospetto.

Confronta con le prestazioni di base

Per ridurre il tempo necessario per riprofilare, considera l'inserimento dei dati in modo che il comportamento delle prestazioni sia ancora visibile (ovvero 23 us per chiamata a .mean() ), ma dove il tempo di esecuzione totale è inferiore a 1-2 minuti o anche meno. Questo ti aiuterà a valutare diversi approcci prima di applicarli completamente al tuo programma. Non serve a ripetere l'intero processo più e più volte solo per testare una piccola ottimizzazione.

    
risposta data 17.07.2016 - 06:38
fonte
1

Hai dimenticato la domanda più semplice:

La velocità è soddisfacente per il caso d'uso?

  • Se la risposta è "sì" - > non profilo
  • Se no, potresti guardare il tuo tavolo.

Ma onestamente, non sembra terribilmente utile, perché quasi tutto il tempo è trascorso in OBSparser.py:48(parse), che richiede molto tempo. Ti suggerirei di rifattare questo metodo in diversi metodi separati. È possibile utilizzare un visualizzatore per visualizzare i risultati, pycharm ha un buon supporto per tale caso d'uso.

    
risposta data 16.07.2016 - 09:18
fonte
1

Questo è ciò che i requisiti non funzionali delle prestazioni sono per.

La nozione di abbastanza veloce non ha nulla di tecnico in sé. Dipende dalla percezione dell'utente del tuo prodotto e dovrebbe essere tradotto attraverso i requisiti. Questo è il modo obiettivo solo per stabilire se la tua implementazione effettiva è abbastanza veloce o meno.

Se non hai questi requisiti, qualsiasi altra cosa è speculativa e non costruttiva.

  • L'utente ti dice che l'app è lenta, ma in qualsiasi momento qualcuno specifica cosa significa "lento" in termini di millisecondi, su quale hardware e per quale caratteristica? Non costruttivo: non puoi migliorare il codice basato su quello, e in sostanza non puoi dire che una revisione fa, il codice era inaccettabilmente lento, e ora è abbastanza veloce.

  • È che pensi che una funzione specifica possa essere eseguita più velocemente di quanto sia attualmente? Questa è l'ottimizzazione prematura e va contro i tuoi utenti, a cui potrebbe non interessare affatto la velocità di questa funzione, e può dare la priorità a un bug specifico, o aver bisogno di una nuova funzionalità, o aver bisogno di qualcos'altro per essere più veloce.

How can I know if my code can improve or not?

Supponiamo che sia sempre possibile. Alcune delle tecniche includono:

  • Riscrivere il codice per utilizzare più memoria ma meno CPU o più CPU ma meno memoria. Questo spesso porta a un codice che è molto difficile da leggere, capire e mantenere; questo è uno dei motivi per cui l'ottimizzazione prematura dovrebbe essere evitata.

  • Utilizzo di strutture di dati differenti.

  • Fare affidamento sulla memorizzazione nella cache, sul precomputing o sull'utilizzo dei cubi OLAP.

  • Spostamento di basso livello, incluso fino all'Assembler.

  • Non eseguire l'attività. Affatto. Questa è l'ottimizzazione definitiva da N secondi a zero.

risposta data 16.07.2016 - 09:19
fonte
0

Come altri hanno notato, non ottimizzare a meno che la velocità non sia soddisfacente.

Sei passato al prossimo passaggio che è il profilo.

Una volta che hai profilato il suo tempo per cercare possibili candidati per l'ottimizzazione:

  • Esamina il tuo processo che viene eseguito per 528 secondi.
  • Hai una chiamata a OBSparser.py:48(parse) usando 166 secondi. Se si riuscisse a eliminare completamente il tempo, si ridurrebbe il tempo totale solo del 31%
  • Hai un numero di chiamate alle routine che consumano tra 50 e 60 secondi. Eliminare il tempo trascorso su una qualsiasi di quelle chiamate farebbe risparmiare circa il 10% delle volte.

Non vedo alcun posto in cui sia possibile migliorare in modo significativo le prestazioni. Con molto lavoro, potresti essere in grado di ottenere un miglioramento delle prestazioni dal 10 al 20%. A meno che non vi siano validi motivi per migliorare le prestazioni, prenderei in considerazione l'ottimizzazione fatta.

Di solito non trovo l'ottimizzazione molto utile se non ho identificato una routine utilizzando almeno l'80% delle volte. Un miglioramento delle prestazioni di dieci volte su una tale routine ridurrà il tempo al 30% o meno.

Se trovi una tale routine, cerca un algoritmo migliore. Se non lo fai, non perdere tempo.

    
risposta data 17.07.2016 - 05:12
fonte
0

Non si tratta di testing , si tratta di tuning . Dato che stai facendo un sacco di I / O, qualsiasi tipo di "CPU profiler" è non quello che vuoi. Il metodo che utilizzo sempre è questo .

Ecco cosa farei se fossi in te: sintonizzare il programma finché non è il più veloce possibile. Quindi se non è abbastanza veloce per essere soddisfacente, procurati un hardware più veloce.

Il modo in cui lo farei è prendere un numero di campioni manualmente. Alcuni di loro saranno in procinto di fare I / O. Se sono per lo più in I / O, quindi vorrei chiedere se c'è un modo per evitare alcuni di questi I / O. (Non dare per scontato che sia necessario tutto l'I / O che sta facendo. Potresti scoprire che sta facendo qualcosa che potrebbe essere effettivamente evitato.) Se puoi evitare alcuni degli I / O, ciò ti velocizzerà di conseguenza.

Ora guarda i campioni che arrivano in elaborazione non I / O. È significativo, come ci vuole più del 10% dei campioni? Se è così, c'è un modo per velocizzarlo, evitando parte del lavoro?

Ogni volta che trovi qualcosa da migliorare, correggi il programma ed eseguilo di nuovo. Potresti essere piacevolmente sorpreso dal fatto che, dall'ultima correzione, qualche cosa nuova si presenta per essere risolta, che non hai mai visto prima, ma ora è importante. Quando non riesci a trovare altro da correggere, puoi dichiarare il programma "alla velocità con la quale tu o probabilmente chiunque può farlo".

Quindi se non è ancora abbastanza veloce, la tua unica opzione è CPU più veloce, unità a stato solido o altro.

    
risposta data 19.07.2016 - 02:23
fonte

Leggi altre domande sui tag