2014-10-25 10 views
7

Questa domanda riguarda principalmente gli interni di Ruby, la velocità può essere misurata con un semplice punto di riferimento.memoizzazione rubino, efficienza

Qual è il modo più efficiente per memorizzare un valore restituito in ruby?

Ho sempre valori con memoized:

def method 
    @value ||= calculate_value 
end 

Ma poiché si espande tecnicamente a:

@value = @value || calculate_value 

mi chiedo circa l'efficienza di ri-eseguire la stessa assegnazione di volta in volta.

Sarebbe meglio?

def method 
    @value ? @value : (@value = calculate_value) 
end 

Inoltre, cambia in diversi interpreti? MRI, Rubinius, ecc

+1

Se calculate_value è costoso e può restituire falso o nullo, è necessario pensare in qualcosa del tipo 'defined? @valore? @value: (@value = calculate_value) '. –

+0

Una domanda interessante, la cui risposta sono lieto di sapere. Non dimenticare di selezionare una risposta, se disponibile. –

risposta

6

Il vostro esempio

@value ||= calculate_value 

equivale a

@value || @value = calculate_value 

ed è non equivalente a

@value = @value || calculate_value 

Quindi la risposta è: E ' molto efficiente. Non viene nuovamente assegnato ogni volta.

+0

puoi provarlo in qualche modo?Per esempio. citazioni di codice sorgente rubino o qualcosa? Un semplice benchmark mostra che (valore || = calculate_value) funziona il 30% più lentamente di (valore || valore = valore_calcio). –

+3

Certo, potresti voler leggere questo articolo di Peter Cooper (http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html) o alcuni thread nel Rubyforum (https://www.ruby-forum.com/topic/151660/) – spickermann

+1

Ho paura che ti sbagli. 'a + = 1' è equivalente a' a = a + 1'. La stessa regola si applica ad altri operatori: 'a || = 1' è espanso a' a = a || 1'. – tompave

0

@spickermann, @tompave, scusa per averlo postato come risposta, ma ho bisogno di una formattazione del codice per pubblicare risultati di benchmark per diversi casi di utilizzo. Sembra che modo esplicito è il più veloce @a || @a = calculated_value

> n = 10000000; 
> a = 1; 
> puts Benchmark.measure { n.times { a ||= 1 } } 
    0.570000 0.000000 0.570000 ( 0.569977) 
> puts Benchmark.measure { n.times { a ? a : a = 1 } } 
    0.560000 0.000000 0.560000 ( 0.562855) 
> puts Benchmark.measure { n.times { a || a = 1 } } 
    0.530000 0.000000 0.530000 ( 0.532715) 
> @a = 1; 
> puts Benchmark.measure { n.times { @a ||= 1 } } 
    0.870000 0.000000 0.870000 ( 0.869489) 
> puts Benchmark.measure { n.times { @a ? @a : @a = 1 } } 
    0.670000 0.000000 0.670000 ( 0.668910) 
> puts Benchmark.measure { n.times { @a || @a = 1 } } 
    0.610000 0.000000 0.610000 ( 0.613978) 

rubino 2.1.2p95 (2014/05/08 revisione 45877) [x86_64-darwin13.0]

Tutti 3 notazioni si comportano allo stesso dal punto di risultato vista, tuttavia l'ultimo è il digiuno dal punto di vista delle prestazioni. Sfortunatamente è tutto empirico e non posso collegare nessun codice sorgente di ruby ​​qui.

+0

Siamo spiacenti, il mio commento non è accurato Stai considerando solo un caso delle alternative: – sawa

0

Penso che qualsiasi differenza tra ||= e ... = ... || ... dovrebbe essere sottile.

Forse, un modo molto più efficiente è quello di cambiare il metodo quando il valore è impostato. Questo passaggio avviene una sola volta e da lì in poi la chiamata al metodo diventa solo un riferimento alla variabile, quindi dovrebbe essere veloce e il vantaggio aumenta con l'aumentare del numero di volte che il metodo viene chiamato.

def method 
    alias method memoized_value 
    @value ||= calculate_value 
end 

def memoized_value; @value end 

Ciò presuppone che calculate_value restituisce sempre un valore truthy, e non v'è nessuna altra parte nel codice che modifica il valore in modo che una volta method si chiama, il valore della variabile rimane truthy.

+0

interessante, ma per esempio i metodi 'alias' verrebbero chiamati almeno una volta per ogni istanza, e IIRC salterà la cache dei metodi di Ruby. – tompave

+0

@tompave Intendevo che i due metodi fossero coppie: hai bisogno di queste coppie quante variabili vuoi fare la memoizzazione: – sawa

+0

sì, ma cancellerà la cache del metodo ogni volta che viene creato un alias. – tompave