Pregunta

Rubí realmente requiere mucha memoria, pero también vale cada pedacito.

¿Qué haces para mantener bajo el uso de la memoria?¿Evita cadenas grandes y utiliza matrices/hashes más pequeños en su lugar o no es un problema del que preocuparse y dejar que el recolector de basura haga el trabajo?

Editar:Encontré un buen artículo sobre este tema. aquí - viejo pero aún interesante.

¿Fue útil?

Solución

  1. Elija estructuras de fechas que sean representaciones eficientes, escalen bien y hagan lo que necesita.
  2. Use algoritmos que funcionen usando estructuras de datos eficientes en lugar de hinchadas, pero más fáciles.
  3. Mira a otro lado. Ruby tiene un puente C y es mucho más fácil ser consciente de la memoria en C que en Ruby.

Otros consejos

He encontrado la Edición Enterprise de Ruby de Phusion (una bifurcación de Ruby de línea principal con recolección de basura muy mejorada) para hacer una diferencia dramática en el uso de memoria ... Además, han hecho que sea extraordinariamente fácil de instalar (y quitar , si encuentra la necesidad).

Puede obtener más información y descargarla en su sitio web .

Realmente no creo que importe tanto. Hacer que su código sea menos legible para mejorar el consumo de memoria es algo que solo debe hacer si lo necesita . Y por necesidad, me refiero a tener un caso específico para el perfil de rendimiento y métricas específicas que indican que cualquier cambio resolverá el problema.

Si tiene una aplicación donde la memoria será el factor limitante, entonces Ruby puede no ser la mejor opción. Dicho esto, descubrí que mis aplicaciones Rails generalmente consumen aproximadamente 40-60mb de RAM por instancia de Mongrel. En el esquema de las cosas, esto no es mucho.

Es posible que pueda ejecutar su aplicación en la JVM con JRuby: la VM Ruby actualmente no es tan avanzada como la JVM para la administración de memoria y la recolección de basura. La versión 1.9 está agregando muchas mejoras y también hay VM alternativas en desarrollo.

Los desarrolladores de Ruby tienen mucha suerte ya que no tienen que administrar la memoria ellos mismos.

Tenga en cuenta que Ruby asigna objetos, por ejemplo algo tan simple como

100.times{ 'foo' }

Asigna 100 objetos de cadena (las cadenas son mutables y cada versión requiere su propia asignación de memoria).

Asegúrese de que si está utilizando una biblioteca que asigna muchos objetos, no haya otras alternativas disponibles y que valga la pena pagar el costo del recolector de basura por su elección.(Es posible que no tenga muchas solicitudes o que no le interesen unas pocas docenas de ms por solicitud).

Crear un objeto hash realmente asigna más que un objeto, por ejemplo

{'joe' => 'male', 'jane' => 'female'}

no asigna 1 objeto sino 7.(un hash, 4 cadenas + 2 cadenas clave)

Si puede usar claves de símbolos, ya que no se recolectarán como basura.Sin embargo, debido a que no se recolectarán basura, debes asegurarte de no usar claves totalmente dinámicas, como convertir el nombre de usuario en un símbolo; de lo contrario, "perderás" memoria.

Ejemplo: En algún lugar de tu aplicación, aplicas un to_sym al nombre de un usuario como:

hash[current_user.name.to_sym] = something

Cuando tienes cientos de usuarios, eso podría estar bien, pero ¿qué pasa si tienes un millón de usuarios?Aquí están los números:

ruby-1.9.2-head >
# Current memory usage : 6608K
# Now, add one million randomly generated short symbols
ruby-1.9.2-head > 1000000.times { (Time.now.to_f.to_s).to_sym }

# Current memory usage : 153M, even after a Garbage collector run.

# Now, imagine if symbols are just 20x longer than that ?
ruby-1.9.2-head > 1000000.times { (Time.now.to_f.to_s * 20).to_sym }
# Current memory usage : 501M

Tenga en cuenta que nunca debe convertir argumentos no controlados en símbolos ni comprobar argumentos antes, ya que esto puede provocar fácilmente una denegación de servicio.

Recuerde también evitar bucles anidados de más de tres niveles de profundidad porque dificulta el mantenimiento.Limitar el anidamiento de bucles y funciones a tres niveles o menos es una buena regla general para mantener el rendimiento del código.

Aquí hay algunos enlaces al respecto:

http://merbist.com

http://blog.monitis.com

  1. Al implementar una aplicación web Rails / Rack, use REE u otro intérprete amigable para copiar y escribir.
  2. Ajustar el recolector de basura (ver https://www.engineyard.com/blog/tuning-the-garbage-collector-with-ruby-1-9-2 por ejemplo)
  3. Intente reducir la cantidad de bibliotecas / gemas externas que usa, ya que el código adicional usa memoria.
  4. Si tiene una parte de su aplicación que realmente consume mucha memoria, entonces tal vez valga la pena volver a escribirla en una extensión C o completarla invocando otros programas / más rápidos / mejor optimizados (si tiene que procesar grandes cantidades de datos de texto , tal vez pueda reemplazar ese código con llamadas a grep, awk, sed, etc.)

No soy un desarrollador de ruby, pero creo que algunas técnicas y métodos son ciertos para cualquier lenguaje:

Utilice la variable de tamaño mínimo adecuada para el trabajo
Destruya y cierre variables y conexiones cuando no esté en uso
Sin embargo, si tiene un objeto que deberá usar muchas veces, considere mantenerlo dentro del alcance Cualquier bucle con manipulaciones de una cadena grande dp funciona en una cadena más pequeña y luego se agrega a una cadena más grande

Use un manejo de errores decente (intente capturar finalmente) para asegurarse de que los objetos y las conexiones estén cerradas

Cuando se trata de conjuntos de datos solo devuelve el mínimo necesario

Aparte de en casos extremos, el uso de memoria no es algo de qué preocuparse. El tiempo que pasa tratando de reducir el uso de memoria comprará MUCHO gigabytes.

Eche un vistazo a Software de memoria pequeña: patrones para sistemas con memoria limitada . No especifica qué tipo de restricción de memoria, pero supongo que RAM. Si bien no es específico de Ruby, creo que encontrará algunas ideas útiles en este libro: los patrones cubren RAM, ROM y almacenamiento secundario, y se dividen en técnicas principales de pequeñas estructuras de datos, asignación de memoria, compresión, almacenamiento secundario y pequeños arquitectura.

Lo único que hemos tenido que merece la pena preocuparse es RMagick.

La solución es asegurarse de que está utilizando RMagick versión 2 y llamar a Image#destroy! cuando haya terminado de usar su imagen

Evite código como este:

str = ''
veryLargeArray.each do |foo|
  str += foo
  # but str << foo is fine (read update below)
end

que creará cada valor de cadena intermedio como un objeto de cadena y luego eliminará su única referencia en la próxima iteración. Esto desecha la memoria con toneladas de cadenas cada vez más largas que tienen que ser recolectadas de basura.

En su lugar, use Array#join:

str = veryLargeArray.join('')

Esto se implementa en C de manera muy eficiente y no incurre en la sobrecarga de la creación de cadenas.

ACTUALIZACIÓN: Jonas tiene razón en el comentario a continuación. Mi advertencia es válida para += pero no <<.

Soy bastante nuevo en Ruby, pero hasta ahora no he considerado necesario hacer nada especial a este respecto (es decir, más allá de lo que suelo hacer como programador en general). Tal vez esto se deba a que la memoria es más barata que el tiempo que llevaría optimizarla seriamente (mi código Ruby se ejecuta en máquinas con 4-12 GB de RAM). También podría deberse a que los trabajos para los que lo uso no son de larga duración (es decir, dependerá de su aplicación).

Estoy usando Python, pero supongo que las estrategias son similares.

Intento utilizar pequeñas funciones / métodos, para que las variables locales se recojan automáticamente cuando vuelves a la persona que llama.

En funciones / métodos más grandes, elimino explícitamente objetos temporales grandes (como listas) cuando ya no son necesarios. Cerrar recursos lo antes posible también podría ayudar.

Algo a tener en cuenta es el ciclo de vida de sus objetos. Si sus objetos no se pasan tanto, el recolector de basura eventualmente los pateará y los liberará. Sin embargo, si sigue haciendo referencia a ellos, puede requerir algunos ciclos para que el recolector de basura los libere. Esto es particularmente cierto en Ruby 1.8, donde el recolector de basura utiliza una implementación deficiente de la técnica de marca y barrido.

Puede encontrarse con esta situación cuando intenta aplicar algunos " patrones de diseño " como decorador que mantiene los objetos en la memoria durante mucho tiempo. Puede que no sea obvio cuando se trata de un ejemplo aislado, pero en aplicaciones del mundo real donde se crean miles de objetos al mismo tiempo, el costo del crecimiento de la memoria será significativo.

Cuando sea posible, use matrices en lugar de otras estructuras de datos. Trate de no usar flotadores cuando los enteros sean suficientes.

Tenga cuidado al usar los métodos de gema / biblioteca. Es posible que no estén optimizados para la memoria. Por ejemplo, la clase Ruby PG :: Result tiene un método 'valores' que no está optimizado. Utilizará mucha memoria extra. Todavía tengo que informar esto.

Reemplazar la implementación de malloc (3) a jemalloc disminuirá inmediatamente el consumo de memoria hasta un 30%. He creado la gema 'jemalloc' para lograr esto al instante.

Intento mantener las matrices & amp; listas & amp; conjuntos de datos lo más pequeños posible. El objeto individual no importa mucho, ya que la creación y la recolección de basura es bastante rápida en la mayoría de los idiomas modernos.

En los casos en que tenga que leer algún tipo de gran conjunto de datos de la base de datos, asegúrese de leer de manera directa / única y procesarlo en pequeños bits en lugar de cargar todo en la memoria primero.

no use muchos símbolos, permanecen en la memoria hasta que se cancela el proceso ... esto porque los símbolos nunca se recolectan basura.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top