2015-05-06 22 views
9

Sto imparando Python e ho trovato this example di una simulazione di un modello che ho visto prima. Una delle funzioni sembrava inutilmente lunga, quindi ho pensato che sarebbe una buona pratica cercare di renderla più efficiente. Il mio tentativo, pur richiedendo meno codice, è circa 1/60 come il più veloce. Sì, l'ho fatto 60 volte peggio.Perché il mio codice (principiante) è così lento?

La mia domanda è: dove ho sbagliato? Ho provato a cronometrare le singole parti della funzione e non vedo dove si trova il collo di bottiglia.

Ecco la funzione originale. È per un modello in cui le persone vivono su una griglia e la loro felicità dipende dal fatto che siano la stessa razza della maggior parte dei loro vicini. (È Schelling segregation model.) Quindi diamo una coordinata x, y per una persona e determiniamo la loro felicità controllando la razza di ciascuno dei loro vicini.

def is_unhappy(self, x, y): 

    race = self.agents[(x,y)] 
    count_similar = 0 
    count_different = 0 

    if x > 0 and y > 0 and (x-1, y-1) not in self.empty_houses: 
     if self.agents[(x-1, y-1)] == race: 
      count_similar += 1 
     else: 
      count_different += 1 
    if y > 0 and (x,y-1) not in self.empty_houses: 
     if self.agents[(x,y-1)] == race: 
      count_similar += 1 
     else: 
      count_different += 1 
    if x < (self.width-1) and y > 0 and (x+1,y-1) not in self.empty_houses: 
     if self.agents[(x+1,y-1)] == race: 
      count_similar += 1 
     else: 
      count_different += 1 
    if x > 0 and (x-1,y) not in self.empty_houses: 
     if self.agents[(x-1,y)] == race: 
      count_similar += 1 
     else: 
      count_different += 1   
    if x < (self.width-1) and (x+1,y) not in self.empty_houses: 
     if self.agents[(x+1,y)] == race: 
      count_similar += 1 
     else: 
      count_different += 1 
    if x > 0 and y < (self.height-1) and (x-1,y+1) not in self.empty_houses: 
     if self.agents[(x-1,y+1)] == race: 
      count_similar += 1 
     else: 
      count_different += 1   
    if x > 0 and y < (self.height-1) and (x,y+1) not in self.empty_houses: 
     if self.agents[(x,y+1)] == race: 
      count_similar += 1 
     else: 
      count_different += 1   
    if x < (self.width-1) and y < (self.height-1) and (x+1,y+1) not in self.empty_houses: 
     if self.agents[(x+1,y+1)] == race: 
      count_similar += 1 
     else: 
      count_different += 1 

    if (count_similar+count_different) == 0: 
     return False 
    else: 
     return float(count_similar)/(count_similar+count_different) < self.similarity_threshold 

E qui è il mio codice, che, come ho detto, è molto più lento. Volevo evitare di avere tutte le affermazioni di cui sopra solo creando un elenco di "offset" da aggiungere alle coordinate di ogni persona per determinare le posizioni dei possibili vicini, verificare se si tratta di una posizione valida e quindi controllare la gara del vicino.

def is_unhappy2(self, x, y): 
    thisRace = self.agents[(x,y)] 
    count_same = 0 
    count_other = 0 

    for xo, yo in list(itertools.product([-1,0,1],[-1,0,1])): 
     if xo==0 and yo==0: 
      # do nothing for case of no offset 
      next 
     else: 
      # check if there's a neighbor at the offset of (xo, yo) 
      neighbor = tuple(np.add((x,y), (xo,yo))) 
      if neighbor in self.agents.keys(): 
       if self.agents[neighbor] == thisRace: 
        count_same += 1 
       else: 
        count_other += 1 
    if count_same+count_other == 0: 
     return False 
    else: 
     return float(count_same)/(count_same + count_other) < self.similarity threshold 

(Il resto del codice che crea la classe è on the site dove l'esempio viene da.)

Ecco i risultati di cronometraggio:

%timeit s.is_unhappy2(49,42) 
100 loops, best of 3: 5.99 ms per loop 

%timeit s.is_unhappy(49,42) 
10000 loops, best of 3: 103 µs per loop 

Sto sperando che qualcuno con il pitone la conoscenza può vedere immediatamente quello che sto facendo male senza dover entrare nel nocciolo del resto del codice. Riesci a capire perché il mio codice è molto peggiore dell'originale?

+2

Il tuo è più lento? 100 loop a 5,99 ms per loop sono 599 ms totali, 10000 loop a 103 microsecondi sono 1030 ms totali. Forse sono confuso dai risultati dei tempi? – ryan

+1

@ryan Il tempo per ciclo è ciò che conta, non il tempo totale trascorso con il benchmark. Il numero di iterazioni ti dà solo un'idea di un intervallo di confidenza. –

+4

@TylerH Con 9 upvotes e nessun voto vicino fino ad ora, sembra che questa sia una domanda accettabile su Stack Overflow. Alcune domande possono essere in argomento su entrambi i siti. Inoltre, non è una buona domanda di revisione del codice in quanto include un po 'di comprensione di un altro codice. –

risposta

8

Non utilizzare np.add, basta usare neighbor = (x+xo, y+yo). Questo dovrebbe renderlo molto più veloce (10 volte più veloce nel mio piccolo test).

Puoi anche ...

  • chiedere if neighbor in self.agents: senza la .keys()
  • tralasciare il controllo list
  • xo or yo e non hanno un vuoto se-block
  • evitare la doppia ricerca del prossimo in auto-agenti

Risultato:

for xo, yo in itertools.product([-1,0,1],[-1,0,1]): 
    if xo or yo: 
     neighbor = self.agents.get((x+xo, y+yo)) 
     if neighbor is not None: 
      if neighbor == thisRace: 
       count_same += 1 
      else: 
       count_other += 1 
.210

E si può aggiungere

self.neighbor_deltas = tuple(set(itertools.product([-1,0,1],[-1,0,1])) - {(0, 0)}) 

alla inizializzatore classe e poi la funzione può semplicemente utilizzare i delta pre-calcolate:

for xo, yo in self.neighbor_deltas: 
    neighbor = self.agents.get((x+xo, y+yo)) 
    if neighbor is not None: 
     if neighbor == thisRace: 
      count_same += 1 
     else: 
      count_other += 1 

Congrats per decidere di migliorare il codice ridicolmente ripetitiva che dell'autore, btw .

3

Il colpevole sembra essere questa linea:

neighbor = tuple(np.add((x,y), (xo,yo))) 

La modifica a questa mostra un enorme aumento di velocità:

neighbor = (x + xo, y + yo) 
Problemi correlati