2009-03-30 35 views
23

A mediados de julio de 2008, se agregó Memoization al núcleo de Rails. Una demostración del uso es here.Cuándo utilizar la memoria en Ruby on Rails

No he podido encontrar ningún buen ejemplo sobre cuándo se deben memorizar los métodos y las implicaciones de rendimiento de cada uno. This blog post, por ejemplo, sugiere que a menudo, la memorización no debe utilizarse en absoluto.

Por algo que podría tener enormes implicaciones de rendimiento, parece que hay pocos recursos que van más allá de proporcionar un tutorial simple.

¿Alguien ha visto la memorización utilizada en sus propios proyectos? ¿Qué factores te harían considerar memorizar un método?


Después de hacer algunas investigaciones más por mi cuenta, encontré que la memorización se usa una cantidad notable de veces dentro del núcleo de Rails.

Aquí hay un ejemplo: http://github.com/rails/rails/blob/1182658e767d2db4a46faed35f0b1075c5dd9a88/actionpack/lib/action_view/template.rb.

Este uso parece ir en contra de los hallazgos de la publicación de blog anterior que encontró que la memorización puede dañar el rendimiento.

Respuesta

33

Creo que muchos desarrolladores de Rails no entienden completamente lo que hace la memoria y cómo funciona. Lo he visto aplicado a métodos que devuelven colecciones cargadas de forma diferida (como un conjunto de datos de Sequel) o aplicadas a métodos que no toman argumentos sino que calculan algo en función de las variables de instancia. En el primer caso, la memoria no es más que una sobrecarga, y en el segundo es una fuente de errores desagradables y difíciles de rastrear.

Me no aplico memoization si

  • el valor devuelto no es más que un poco caro para el cálculo. Tendría que ser muy caro, y no más optimizable, para que valga la pena la memoria.
  • el valor devuelto es o podría ser vago
  • el método no es una función pura, es decir, se garantiza que devuelve exactamente el mismo valor para los mismos argumentos, y solo utiliza los argumentos para hacer su trabajo, o otras funciones puras El uso de variables de instancia o métodos de llamada que a su vez usan variables de instancia significa que el método podría devolver resultados diferentes para los mismos argumentos.

También existen otras situaciones en las que la memorización no es apropiada, como la de la pregunta y las respuestas anteriores, pero estas son tres que creo que no son tan obvias.

El último elemento es probablemente el más importante: memoization almacena en caché un resultado basado en los argumentos del método, si el método se parece a esto no se puede memoized:

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

def unmemoizable2 
    find_by_shoe_size(@size) 
end 

Ambos pueden, sin embargo, ser reescrita para tomar ventaja de memoization (aunque en estos dos casos se debe, obviamente, no se puede hacer por otras razones):

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 

(suponiendo que find_by_shoe_size no tenían, o confiado en, cualquier efecto secundario)

El truco consiste en extraer una función pura del método y aplicar la memoria a eso en su lugar.

+0

Cuando diga "es decir, se garantiza que devolverá exactamente el mismo valor para los mismos argumentos", ¿cómo se aplicaría eso a las consultas de Rails ActiveRecord? Digamos, por ejemplo, que memoricé un método que recupera todas las ciudades marcadas como activas dentro de un modelo de ciudad, por ejemplo: '' 'def self.active @active_cities = || where ("cities.active = (?)", true) end '' 'Cuando los administradores añaden nuevas ciudades ocasionalmente a la base de datos cuando la aplicación está activa, ¿debería reiniciar el servidor para anular esta variable de instancia memorable? ? ¿O una variable de instancia memorable se destruye y vuelve a crear después de cada solicitud? – Kelseydh

+1

En respuesta a lo anterior: Ahora me doy cuenta de que * la memorización solo persiste una variable de instancia durante el ciclo de vida de ** una sola solicitud. *** Esto es lo que me confundió. Para los principiantes, recomiendo que agregue este punto crítico a su definición. – Kelseydh

10

Cuando un método obtiene datos de varias tablas y realiza algunos cálculos antes de devolver el objeto resultante, y este método es varias veces en las solicitudes, la memorización puede tener sentido.

Recuerde que el almacenamiento en caché de consultas también está activo, por lo que solo memorice métodos que realizan cálculos en-Ruby, no recuperaciones de bases de datos puras.

+1

¿La construcción de objetos ActiveRecord cuenta como cálculo? Según tengo entendido, la caché de consultas solo almacena en caché el conjunto de resultados de mysql y no los objetos que se crean (donde el proceso de creación a menudo lleva más tiempo que la consulta misma). – Gdeglin

+1

Hasta donde yo sé, el caché de consultas almacena los objetos reales de ActiveRecord. –

2

Quizás mi experiencia sea un buen ejemplo de cuándo NO utilizar memoize. En mi modelo de Orden, memorizaba resultados de cálculos simples, es decir, subtotal de pedido, impuesto de pedido n.º. así como los objetos del modelo, es decir, la orden # most_recent_credit_card_used. En este último caso, al memorizar un método que devuelve un objeto CreditCard, obtendría errores de 'hash congelado' al intentar actualizar atributos en el objeto memoializado. Pedido # most_recent_credit_card_used.frozen? regresó verdadero cuando el método fue memorado, que por supuesto, no es lo que pretendía.

Mi take-away fue simple: utilice memoize para operaciones costosas que devuelvan tipos de datos simples (enteros, flotantes, etc.) pero no utilice memoize al devolver objetos complejos como modelos ActiveRecord, esp. si tiene la intención de actualizar esos objetos en la memoria.