2010-06-05 7 views
18

me encontré con este artículo aquí:La cuantificación del rendimiento de la recolección de elementos vs. Gestión de memoria explícita

La cuantificación del rendimiento de la recolección de elementos vs. Gestión de memoria explícita

http://www.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf

En el sección de conclusión, dice:

Al comparar el tiempo de ejecución, el consumo de espacio, y las huellas de la memoria virtual en un rango de puntos de referencia , se muestra que el rendimiento tiempo de ejecución del mejor rendimiento del colector de basura es competitivo con la memoria explícita gestión cuando se le da suficiente memoria. En particular, cuando la recolección de basura tiene cinco veces más memoria que requerido, sus tiempo de ejecución de rendimiento fósforos o ligeramente superior al de gestión de memoria explícita. Sin embargo, el rendimiento de la recolección de basura se degrada sustancialmente cuando debe usar montones más pequeños. Con tres veces más de memoria, funciona un 17% más lento en promedio, y con el doble de memoria , funciona un 70% más lento. La recolección de basura también es más susceptible a paginación cuando la memoria física es escasa. En tales condiciones, todos los colectores de basura que examinamos aquí sufren rendimiento de orden de magnitud penalizaciones relativas a la gestión explícita de la memoria .

Por lo tanto, si mi interpretación es correcta: si tengo una aplicación escrita en C++ nativo requiere 100 MB de memoria, para lograr el mismo rendimiento con un lenguaje "administrado" (es decir, recolector de basura con base) (por ejemplo, Java, C#), la aplicación debería requerir 5 * 100 MB = 500 MB? (¿Y con 2 * 100 MB = 200 MB, la aplicación administrada funcionaría un 70% más lenta que la aplicación nativa?)

¿Sabes si los recolectores de basura actuales (es decir, los últimos VM de Java y .NET 4.0) sufren los mismos problemas descritos en el artículo antes mencionado? ¿Ha mejorado el rendimiento de los recolectores de basura modernos?

Gracias.

+1

El recolector de basura utiliza un algoritmo de "mejor estimación" para determinar cuándo deshacerse de la memoria. Los algoritmos que utiliza no están necesariamente sintonizados con lo que sucede dentro de su programa específico. Cuando maneja la memoria usted mismo, debe ser más selectivo e inteligente sobre cómo se recopila la memoria, pero eso requiere más habilidad y esfuerzo, y corre el riesgo de pérdidas de memoria si no se realiza correctamente. –

+3

@Robert Harvey: De hecho, creo que en C++, gracias a los destructores, RAII y punteros inteligentes, es difícil filtrar algo (ya sean recursos de memoria o recursos de otro tipo). De hecho, mientras que en Java se puede perder la liberación de un recurso en un intento ... finalmente bloquear, en C++ los destructores de las variables asignadas en la pila (o de los punteros inteligentes) se llaman automáticamente. (Por supuesto, esto supone que las clases están diseñadas correctamente, y cada clase libera sus propios recursos en su destructor). – EmbeddedProg

+1

Así mi declaración, "... si no se hace correctamente". –

Respuesta

7

usted parece estar haciendo dos cosas:

  • tienen de GC mejorado desde que se realizó la investigación, y
  • puedo utilizar las conclusiones del documento como una fórmula para predecir la memoria requerida.

La respuesta a la primera es que no se han producido grandes avances en los algoritmos de GC que pudieran invalidar las conclusiones generales:

  • gestión de memoria GC'ed todavía requiere significativamente (por ejemplo, de 3 a 5 veces) más memoria virtual.
  • Si intenta restringir el tamaño del almacenamiento dinámico, el rendimiento del GC disminuye significativamente.
  • Si la memoria real está restringida, el enfoque de administración de memoria con GC resulta en un rendimiento sustancialmente peor debido a los gastos generales de paginación.

Sin embargo, las conclusiones no pueden realmente ser utilizado como una fórmula:

  • El estudio original fue hecho con JikesRVM en lugar de una JVM solar.
  • Los recolectores de basura de Sun JVM han mejorado en los ~ 5 años desde el estudio.
  • El estudio no parece tener en cuenta que las estructuras de datos de Java toman más espacio que las estructuras de datos de C++ equivalentes por razones que no están relacionadas con GC.

En el último punto, he visto una presentación de alguien que habla sobre los gastos generales de la memoria Java. Por ejemplo, descubrió que el tamaño mínimo de representación de una cadena Java es algo así como 48 bytes. (Una Cadena consiste en dos objetos primitivos, uno es un Objeto con 4 campos de tamaño de palabra y el otro una matriz con un mínimo de 1 palabra de contenido. Cada objeto primitivo también tiene 3 o 4 palabras de sobrecarga). usa mucha más memoria de la que la gente piensa.

Estos gastos generales no están relacionados con GC per se. Más bien son consecuencias directas e indirectas de las decisiones de diseño en el lenguaje Java, JVM y bibliotecas de clase. Por ejemplo:

  • cada objeto primitivo de Java cabecera reservas para el valor de una palabra "código hash de identidad" del objeto, y una o más palabras para la representación de la cerradura objeto.
  • La representación de una Cadena tiene que usar una "matriz de caracteres" separada debido a las limitaciones de la JVM. Dos de los otros tres campos son un intento de hacer que la operación substring requiera menos memoria.
  • Los tipos de colección Java usan mucha memoria porque los elementos de colección no pueden encadenarse directamente. Entonces, por ejemplo, los gastos generales de una clase de colección de listas enlazadas (hipotéticamente) en Java serían 6 palabras por elemento de lista. Por el contrario, una lista óptima de enlaces C/C++ (es decir, con cada elemento que tiene un puntero "siguiente") tiene una sobrecarga de una palabra por elemento de lista.

1 - De hecho, los gastos generales son menos que esto, en promedio. La JVM solo "infla" un bloqueo después de usar el argumento &, y se usan trucos similares para el código hash de identidad. La sobrecarga fija es solo unos pocos bits. Sin embargo, estos bits se suman a un encabezado de objeto mensurablemente más grande ... que es el verdadero punto aquí.

+0

Las cadenas Java pueden tener sus matrices de caracteres asignadas de maneras "interesantes" (por ejemplo, literales). Están sucediendo muchas cosas en segundo plano que oculta la API. –

+0

@Donal - cierto, pero mi punto es que las diferencias en los requisitos de memoria Java vs C++ no * son * atribuibles a la forma en que se realiza la gestión de la memoria. –

+0

Estoy de acuerdo con eso. Es un área donde la diferencia entre un código bueno y un código pobre es bastante sutil, y eso es cierto en muchos idiomas. –

9

si tiene una aplicación escrita en C++ nativo requiere 100 MB de memoria, para lograr el mismo rendimiento con un "administrado" (es decir, recolector de basura con base) idioma (por ejemplo, Java, C#), la aplicación debería requerir 5 * 100 MB = 500 MB? (Y con 2 * 100 MB = 200 MB, el logrado aplicación correría el 70% más lenta que la aplicación nativa ?)

Sólo si la aplicación está en un cuello de botella asignar y desasignar memoria. Tenga en cuenta que el documento habla exclusivamente sobre el rendimiento del recolector de basura .

+2

Por contexto, la mayoría de las grandes aplicaciones en tiempo real en las que he trabajado pasaron entre el 10 y el 15% de su tiempo administrando la memoria cuando dependían del OS o de los asignadores del tiempo de ejecución estándar. He visto que ese número cae a alrededor del 5% cuando se usan montones fijos, asignadores de bloques, una variedad de optimizaciones difíciles. – Crashworks

4

Michael Borgwardt tiene razón acerca de si la aplicación tiene un cuello de botella en la asignación de memoria. Esto es de acuerdo con la ley de Amdahl.

Sin embargo, he usado C++, Java y VB .NET. En C++ existen potentes técnicas disponibles que asignan memoria en la pila en lugar de en el montón. La asignación de la pila es fácilmente cientos de veces más rápida que la asignación de la pila. Yo diría que el uso de estas técnicas podría eliminar tal vez una asignación en ocho, y el uso de cadenas grabables una asignación en cuatro.

No es ninguna broma cuando las personas afirman que el código C++ altamente optimizado puede derrotar al mejor código Java posible. Es la verdad absoluta.

Microsoft afirma que la sobrecarga en el uso de cualquiera de la familia de idiomas .NET sobre C++ es aproximadamente de dos a uno. Creo que ese número es perfecto para la mayoría de las cosas. No obstante, los entornos administrados tienen un beneficio particular ya que cuando se trata de programadores inferiores no tiene que preocuparse de que un módulo destruya la memoria de otro módulo y el fallo resultante se culpe al desarrollador incorrecto y el error sea difícil de encontrar.

+0

Por lo que recuerdo, la asignación en C# es muy, muy rápida. Son los procesos de recolección y compactación GC los que duelen. –

+6

En casi cualquier recolector de basura a medio camino, la asignación de montón simplemente está golpeando un puntero, es decir, es * exactamente * lo mismo que la asignación de pila. Lo que generalmente es costoso es la recolección real de basura real, o más específicamente, la colección real de basura * vieja * real. Es por eso que los GC funcionan mejor con pilas grandes: simplemente les gusta evitar recolectar. –

+0

@ Jörg: Lo sé. En la asignación de la pila, tanto asignar como libre es una instrucción para todos los objetos en el marco de la pila. – Joshua

3

Al menos mientras lo leo, su verdadera pregunta es si ha habido avances significativos en la recolección de basura o la gestión manual de la memoria desde que se publicó ese documento que invalidaría sus resultados. La respuesta a eso es algo mezclada. Por un lado, los vendedores que brindan recolectores de basura los sintonizan para que su desempeño mejore con el tiempo. Por otro lado, no ha habido nada como avances importantes, como los principales algoritmos de recolección de basura.

Los administradores de montones manuales generalmente también mejoran con el tiempo. Dudo que la mayoría estén sintonizados con la regularidad de los recolectores de basura, pero en el transcurso de 5 años, probablemente la mayoría haya realizado al menos un poco de trabajo.

En resumen, ambos han mejorado indudablemente al menos un poco, pero en ninguno de los casos ha habido nuevos algoritmos importantes que cambien el panorama fundamental. Es dudoso que las implementaciones actuales den una diferencia de exactamente el 17% según lo citado en el artículo, pero hay una posibilidad muy buena de que si repitiera las pruebas hoy, todavía tendría una diferencia en algún lugar alrededor del 15-20% más o menos. Las diferencias entre entonces y ahora son probablemente más pequeñas que las diferencias entre algunos de los diferentes algoritmos que probaron en ese momento.

+0

¿Qué son los "gestores de montón manuales"? ¿La "gestión manual del montón" no implica la ausencia de un administrador, en otras palabras, que el programador maneja el montón, independientemente de si tienen treinta años de experiencia o son principiantes? – Holger

+0

@Holger: un gestor de montón manual es (en términos simples) la implementación de 'new' y' delete' en C++. El usuario decide qué asignar y liberar (y cuándo hacerlo, etc.), pero hay algún código para realizar un seguimiento de qué bloques de memoria son libres, qué están en uso, te encuentra un bloque de tamaño suficiente cuando haces una asignación, etc. –

3

No estoy seguro de cuán revivir su pregunta todavía es hoy. Una aplicación crítica para el rendimiento no debe pasar una parte significativa de su tiempo realizando creación de objetos (como es muy probable que lo haga el microensayo) y es más probable que el rendimiento en los sistemas modernos esté determinado por cuán bien encaja la aplicación en las CPU. caché, en lugar de la cantidad de memoria principal que utiliza.

BTW: Hay muchos tics que puede hacer en C++ que soportan esto que no están disponibles en Java.

Si le preocupa el costo de la creación de GC o objetos, puede tomar medidas para minimizar la cantidad de objetos que crea. En general, esta es una buena idea cuando el rendimiento es crítico en cualquier idioma.

El costo de la memoria principal no es un problema tan grave como el que yo solía tener.Una máquina con 48 GB es relativamente barata en estos días. Se puede alquilar un servidor de 8 núcleos con 48 GB de memoria principal por £ 9/día. Intente contratar a un desarrollador por £ 9/d. ;) Sin embargo, lo que todavía es relativamente costoso es la memoria caché de la CPU. Es bastante difícil encontrar un sistema con más de 16 MB de caché de CPU. c.f. 48,000 MB de memoria principal. Un sistema funciona mucho mejor cuando una aplicación utiliza su caché de CPU y esta es la cantidad de memoria que se debe considerar si el rendimiento es crítico.

Cuestiones relacionadas