2012-01-05 6 views
5

Attenzione, questo è un po 'ricorsivo;)funzioni di temporizzazione

ho risposto a questa domanda: Python:How can i get all the elements in a list before the longest element?

E dopo ho presentato là dove un'altra risposta che dovrebbe essere più veloce (l'autore pensiero, e così feci io) . Ho provato a programmare le diverse soluzioni, ma la soluzione che dovrebbe essere più lenta era in realtà più veloce. Questo mi ha fatto pensare che ci fosse qualcosa di sbagliato nel mio codice. O è?

import string 
import random 
import time 

def solution1(lst): 
    return lst[:lst.index(max(lst, key=len))] 

def solution2(lst): 
    idx, maxLenStr = max(enumerate(lst), key=lambda x:len(x[1])) 
    return lst[:idx] 

# Create a 100000 elements long list that contains 
# random data and random element length 
lst = [] 
for i in range(100000): 
    s = "".join([random.choice(string.letters+string.digits) for x in range(1, random.randint(1,50))]) 
    lst.append(s) 

# Time the first solution 
start = time.time() 
solution1(lst) 
print 'Time for solution1', (time.time() - start) 

# Time the second solution 
start = time.time() 
solution2(lst) 
print 'Time for solution2', (time.time() - start) 

Aggiornamento

Prima che qualcuno cita il motivo per cui ho messo questo è come una nuova domanda. La domanda è più su di me imparare come misurare il tempo di esecuzione ...

+1

queste due funzioni non restituiscono lo stesso tipo di oggetto – joaquin

+0

Doh! Certo :) Grazie! –

+0

risolto. Ma questo rende anche il mio codice più veloce ... E ho ancora pensato che soluzione2 sia più veloce .. –

risposta

3

Il lambda sta costando caro nella seconda soluzione.

ho fatto il profilo sia i codici e dai dati del profilo, sembra, la prima soluzione è più veloce

Come il wiki direbbe chiamata di funzione è costoso e nella seconda soluzione, la lambda e le chiamate di funzione len sono facendo correre più lento

si prega di notare, ho tagliato l'elenco per una lunghezza di 1000 elementi

>>> cProfile.run('solution1(lst)') 
     5 function calls in 0.000 CPU seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.000 0.000 0.000 0.000 <pyshell#305>:1(solution1) 
     1 0.000 0.000 0.000 0.000 <string>:1(<module>) 
     1 0.000 0.000 0.000 0.000 {max} 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
     1 0.000 0.000 0.000 0.000 {method 'index' of 'list' objects} 


>>> cProfile.run('solution2(lst)') 
     2004 function calls in 0.012 CPU seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.000 0.000 0.012 0.012 <pyshell#306>:1(solution2) 
    1000 0.006 0.000 0.009 0.000 <pyshell#306>:2(<lambda>) 
     1 0.000 0.000 0.012 0.012 <string>:1(<module>) 
    1000 0.003 0.000 0.003 0.000 {len} 
     1 0.003 0.003 0.012 0.012 {max} 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
+0

Vorrei accettare tutte le risposte perché sono preziose. Ma posso sceglierne solo uno e penso che questa sia stata la migliore spiegazione. Grazie :) –

2

Il tempismo sembra ok.

solution1 può essere più veloce, perché non utilizza lambdas, quindi non ha bisogno di chiamare il codice Python nel ciclo. Fa iterare la lista due volte, ma non è un grosso problema.

+0

@joaquin Sì, hai ragione. Sto cancellando il mio commento per evitare qualsiasi confusione. Grazie per avermi fatto sapere. – jcollado

3

Sembra che la prima versione stia effettuando molte meno chiamate rispetto alla seconda.

btw, questo è probabilmente un altro esempio di come idiomatica, codice semplice è spesso anche il più veloce uno in Python

>>> dis.dis(solution1) 
    2   0 LOAD_FAST    0 (lst) 
       3 LOAD_FAST    0 (lst) 
       6 LOAD_ATTR    0 (index) 
       9 LOAD_GLOBAL    1 (max) 
      12 LOAD_FAST    0 (lst) 
      15 LOAD_CONST    1 ('key') 
      18 LOAD_GLOBAL    2 (len) 
      21 CALL_FUNCTION   257 
      24 CALL_FUNCTION   1 
      27 SLICE+2    
      28 RETURN_VALUE   
>>> dis.dis(solution2) 
    2   0 LOAD_GLOBAL    0 (max) 
       3 LOAD_GLOBAL    1 (enumerate) 
       6 LOAD_FAST    0 (lst) 
       9 CALL_FUNCTION   1 
      12 LOAD_CONST    1 ('key') 
      15 LOAD_CONST    2 (<code object <lambda> at 000000000422BEB8, file "<input>", line 2>) 
      18 MAKE_FUNCTION   0 
      21 CALL_FUNCTION   257 
      24 UNPACK_SEQUENCE   2 
      27 STORE_FAST    1 (idx) 
      30 STORE_FAST    2 (maxLenStr) 

    3   33 LOAD_FAST    0 (lst) 
      36 LOAD_FAST    1 (idx) 
      39 SLICE+2    
      40 RETURN_VALUE 
+0

Non importa. Le funzioni chiamate richiedono molto più tempo, per qualsiasi dato ragionevolmente grande. – zch

+0

@zch scusa non capisco, potresti spiegare il tuo commento? – joaquin

+0

Tutte le istruzioni vengono chiamate una sola volta per test. Il tempo di esecuzione della funzione 'max' è proporzionale alla lunghezza dell'elenco di input. Quindi, per una lista abbastanza grande (in questo caso non molto grande), eseguire 'max' richiederebbe molto più tempo rispetto all'esecuzione di altre istruzioni. – zch

Problemi correlati