2012-12-04 12 views
5

faccio (a mio avviso):Rubino .each efficienza

# myUser is a User in ActiveRecord with :has_many :posts 
myUser.posts.each do |post| 
end 

Se l'utente aveva dieci messaggi, questo sarebbe fare una chiamata al database dieci volte? Se questi cicli siano come (meno bella) ?:

myPosts = myUser.posts 
myPosts.each do |post| 
end 

Here è un bidone pasta di un file rubino che ho fatto alla prova. EDIT Modificato il contenitore della pasta.

Questo mi ricorda il codice in Java

for (int i = 0; i < someExpensiveFunction(); i++) 

che dovrebbe essere (a meno che l'array viene modificato)

for (int i = 0, len=someExpensiveFunction(); i < len; i++) 

mi sto perdendo qualcosa? Vedo un sacco di esempi di binari in cui le persone passano attraverso il campo has_many di un oggetto. Questo è importante per me poiché sto cercando di ottimizzare le prestazioni della mia applicazione.

risposta

10

Se l'utente aveva 10 post, questo sarebbe tecnicamente fare una chiamata di database 10 volte?

No.

myUser = User.find 123 
myUser.posts.each do |post| 
    puts post.title 
end 

Questo codice eseguirà 2 query. Il primo troverà un utente con il suo id, restituendo una singola riga. Il secondo eseguirà una query che richiede tutti i post che hanno un ID utente che corrisponde a myUser. Quindi il each utilizzerà il risultato di questo per scorrere.


Se si guarda il registro in modalità di sviluppo che ti dice le query è in esecuzione, e si dovrebbe vedere una singola query di ritorno tutti quei post.

Rails utilizza un oggetto proxy di associazione per contenere la query, eseguire la query quando sono necessari i record e quindi memorizza nella cache il risultato. È un bit di codice molto utile e, per la maggior parte, gestisce cose come questa per te.

Questa è una funzionalità di Rails, non rubino.


A livello rubino, each agisce semplicemente su collezioni.

def get_letters 
    puts 'executing "query"' 
    sleep(3) 
    ["a","b","c"] 
end 

get_letters.each do |item| 
    puts item 
end 

Questo dovrebbe essere stampato.

executing "query" 
a 
b 
c 

Procedimento get_letters esegue, restituisce una matrice, una tale matrice già pieno di dati è quello che chiamiamo il metodo each su, così possiamo elaborare ogni elemento. Recuperare la raccolta e iterare attraverso di essa sono 2 passaggi completamente separati.


Dal tuo pastebin:

# will this run forever? 
myArr = ["a","b","c"] 
myArr.each do |item| 
    myArr.push("x") 
end 

Sì lo farà, ma non perché la matrice è essere "recuperato" su un over.

Equivalente a javascript come questo, che lo spiega meglio.

myArr = ["a","b","c"]; 
for (var i = 0; i < myArr.length; i++) { 
    myArr.push('x'); 
} 

L'array originale, su ogni iterazione dei controlli di ciclo per vedere se è ancora finito. Ma poiché l'array si allunga di una unità ogni volta che avanza di un elemento, i < myArr.length sarà sempre true perché entrambi aumentano di uno su ogni iterazione. La stessa cosa sta succedendo in ruby ​​con il tuo ciclo each, per lo stesso motivo.

Viene eseguito per sempre, non perché si continua a eseguire codice per rigenerare l'array. Funziona all'infinito perché stai modificando artificialmente la matrice risultante.

+0

Grazie, ma domanda. Nel mio cestino della pasta come ultimo esempio, il ciclo gira per sempre ... Non capisco perché sarebbe se il risultato fosse "memorizzato nella cache" Ruby modificasse la cache in seguito? Cosa succederebbe se aggiungessi un post in un ciclo di post di .each? – K2xL

+0

@ K2xL Vedi la mia modifica, che spiega di più le cose sul livello base ruby ​​(non rails). Ma il tuo ciclo, come pubblicato, sicuramente non è "correre per sempre". Dopo 3 secondi, è fatto. –

+0

Vedere la mia modifica (ho accidentalmente inviato l'URL pastebin errato) – K2xL