2009-03-30 8 views
23

A metà luglio 2008 Memoization è stato aggiunto al core di Rails. Una dimostrazione dell'uso è here.Quando utilizzare la memoizzazione in Ruby on Rails

Non sono stato in grado di trovare alcun buon esempio su quando i metodi dovrebbero essere memoizzati e le implicazioni sulle prestazioni di ciascuno. This blog post, ad esempio, suggerisce che spesso la memoizzazione non dovrebbe essere utilizzata affatto.

Per qualcosa che potrebbe potenzialmente avere enormi implicazioni sulle prestazioni, sembrano esserci poche risorse che vanno al di là di fornire un semplice tutorial.

Qualcuno ha visto la memoizzazione utilizzata nei propri progetti? Quali fattori ti farebbero considerare la memorizzazione di un metodo?


Dopo aver fatto qualche ricerca per conto mio ho trovato che Memoizzazione viene usato un notevole numero di volte all'interno del nucleo Rails.

Ecco un esempio: http://github.com/rails/rails/blob/1182658e767d2db4a46faed35f0b1075c5dd9a88/actionpack/lib/action_view/template.rb.

Questo utilizzo sembra andare contro i risultati del post di blog di cui sopra che la memoizzazione può danneggiare le prestazioni.

risposta

33

Penso che molti sviluppatori di Rails non capiscano appieno cosa fa la memoizzazione e come funziona. L'ho visto applicato a metodi che restituiscono raccolte lazy-load (come un set di dati Sequel) o applicato a metodi che non accettano argomenti ma calcolano qualcosa basato su variabili di istanza. Nel primo caso la memoizzazione non è altro che un sovraccarico, e nel secondo è una fonte di cattivi e difficili da rintracciare bug.

avrei non applico Memoizzazione se

  • il valore restituito è semplicemente un po 'costoso per il calcolo. Dovrebbe essere molto costoso, e non ulteriormente ottimizzabile, perché valga la pena di essere memorizzato.
  • il valore restituito è o potrebbe essere pigro caricato
  • il metodo non è una funzione pura, vale a dire che è garantito restituire esattamente lo stesso valore per gli stessi argomenti - e utilizza solo gli argomenti per farlo funzionare, oppure altre pure funzioni. L'utilizzo di variabili di istanza o metodi di chiamata che a loro volta utilizzano variabili di istanza significa che il metodo potrebbe restituire risultati diversi per gli stessi argomenti.

Ci sono altre situazioni in cui la memoizzazione non è appropriata, come quella nella domanda e le risposte di cui sopra, ma queste sono tre che penso non siano così ovvie.

L'ultimo elemento è probabilmente il più importante: Memoizzazione memorizza nella cache il risultato sulla base degli argomenti al metodo, se il metodo si presenta così non può essere memoized:

def unmemoizable1(name) 
    "%s was here %s" % name, Time.now.strftime('%Y-%m-%d') 
end 

def unmemoizable2 
    find_by_shoe_size(@size) 
end 

Entrambi possono, tuttavia, essere riscritta sfruttare memoization (sebbene in questi due casi dovrebbe ovviamente non essere fatto per altri motivi):

def unmemoizable1(name) 
    memoizable1(name, Time.now.strftime('%Y-%m-%d')) 
end 

def memoizable1(name, time) 
    "#{name} was here #{time}" 
end 
memoize :memoizable1 

def unmemoizable2 
    memoizable2(@size) 
end 

def memoizable2(size) 
    find_by_shoe_size(size) 
end 
memoize :memoizable2 

(supponendo che find_by_shoe_size non hanno, o invocato, effetti collaterali)

Il trucco è estrarre una funzione pura dal metodo e applicare la memoizzazione a quella.

+0

Quando si dice "vale a dire che è garantito restituire esattamente lo stesso valore per gli stessi argomenti", come si applicherebbe alle query di Rails ActiveRecord? Supponiamo ad esempio di aver memorizzato un metodo che recupera tutte le città contrassegnate come attive all'interno di un modello di City, ad esempio: '' 'def self.active @active_cities = || dove ("cities.active = (?)", vero) fine '' 'Quando nuove città vengono aggiunte occasionalmente al database dagli amministratori quando l'app è attiva, è necessario riavviare il server per sovrascrivere questa variabile di istanza memoized ? O una variabile di istanza memoized viene distrutta e ricreata dopo ogni richiesta? – Kelseydh

+1

In risposta a quanto sopra: Mi rendo conto ora che * la memoizzazione persiste solo per variabili di istanza durante il ciclo di vita di ** una singola richiesta. *** Questo è ciò che mi ha confuso. Per i principianti ti consiglio di aggiungere questo punto critico alla tua definizione. – Kelseydh

10

Quando un metodo recupera dati da più tabelle ed esegue alcuni calcoli prima di restituire l'oggetto risultante e questo metodo è più volte richiesto, la memoizzazione potrebbe avere senso.

Ricordare che il caching delle query è anche attivo, quindi solo i metodi memoize che eseguono i calcoli in-Ruby, non i recuperi di database puri.

+1

La creazione di oggetti ActiveRecord conteggi come calcolo? A quanto ho capito, query cache memorizza nella cache solo il set di risultati mysql e non gli oggetti creati (in cui il processo di creazione richiede spesso più tempo della query stessa). – Gdeglin

+1

Per quanto ne so, la cache della query memorizza gli oggetti ActiveRecord effettivi. –

2

Forse la mia esperienza è un buon esempio di quando NON usare memoize. Nel mio modello di ordine, stavo memorizzando sia semplici risultati di calcolo, cioè numero di subtotale dell'ordine n. Ordine n. D'ordine; così come gli oggetti del modello, ad esempio Order # most_recent_credit_card_used. In quest'ultimo caso, durante la memoizzazione di un metodo che restituisce un oggetto CreditCard, ricevo gli errori di "congelamento hash" quando si tenta di aggiornare gli attributi sull'oggetto memoized. Ordine # most_recent_credit_card_used.frozen? restituito vero quando il metodo è stato memoized, che ovviamente non è quello che intendevo.

My take-away era semplice: utilizzare Memoize per le operazioni costose che restituiscono i tipi di dati semplici (interi, galleggianti, etc.) ma non usare Memoize al ritorno oggetti complessi come i modelli di ActiveRecord, esp. se intendi aggiornare tali oggetti in memoria.