¿Cómo evitar quedarse sin memoria en una aplicación con un uso elevado de memoria?C/C++

StackOverflow https://stackoverflow.com/questions/741834

  •  09-09-2019
  •  | 
  •  

Pregunta

He escrito un conversor que toma archivos xml de openstreetmap y los convierte a un formato de renderizado en tiempo de ejecución binario que suele ser aproximadamente el 10% del tamaño original.Los tamaños de los archivos de entrada suelen ser de 3 GB o más.Los archivos de entrada no se cargan en la memoria todos a la vez, sino que se transmiten a medida que se recopilan puntos y polis, luego se ejecuta un bsp en ellos y se genera el archivo.Recientemente, en archivos más grandes se queda sin memoria y muere (el que está en cuestión tiene 14 millones de puntos y 1 millón de polígonos).Normalmente, mi programa utiliza entre 1 y 1,2 GB de RAM cuando esto sucede.Intenté aumentar la memoria virtual de 2 a 8 GB (en XP) pero este cambio no tuvo ningún efecto.Además, dado que este código es de código abierto, me gustaría que funcione independientemente de la RAM disponible (aunque más lento), se ejecuta en Windows, Linux y Mac.

¿Qué técnicas puedo utilizar para evitar que se quede sin memoria?¿Procesar los datos en subconjuntos más pequeños y luego fusionar los resultados finales?¿Utilizando mi propio tipo de controlador de memoria virtual?¿Alguna otra idea?

¿Fue útil?

Solución

En primer lugar, en un sistema de 32 bits, que siempre será limitado a 4 GB de memoria, independientemente de la configuración del archivo de paginación. (Y de ellos, sólo 2 GB estará disponible para su proceso en Windows. En Linux, por lo general va tiene alrededor de 3 GB disponibles)

Así que la primera solución obvia es cambiar a un sistema operativo de 64 bits, y compilar la aplicación de 64 bits. Eso le da un gran espacio de memoria virtual para utilizar, y el sistema operativo va a intercambiar datos dentro y fuera del archivo de paginación si es necesario para mantener las cosas de trabajo.

En segundo lugar, la asignación de fragmentos más pequeños de memoria a la vez puede ayudar. A menudo es más fácil encontrar 4 trozos 256 MB de memoria libre de un trozo de 1 GB.

En tercer lugar, dividir el problema. No procesar todo el conjunto de datos a la vez, pero trate de cargar y único proceso una pequeña sección a la vez.

Otros consejos

¿Ha comprobado para asegurarse de que no tengan fugas de memoria en cualquier lugar?

Debido a que su programa es portátil para Linux, sugiero correr bajo Valgrind para asegurarse.

Parece que ya estás haciendo un SAXÓFONO enfoque basado en el procesamiento XML (cargar el XML sobre la marcha en lugar de todo a la vez).

La solución casi siempre es cambiar el algoritmo para dividir el problema en partes más pequeñas.Físicamente, no asigne tanta memoria a la vez, lea solo lo que necesita, procéselo y luego escríbalo.

A veces, puede ampliar la memoria utilizando el disco duro cuando sea necesario en su algoritmo.

Si no puedes dividir tu algoritmo, probablemente quieras algo como archivos mapeados en memoria.

En el peor de los casos, puedes intentar usar algo como Asignación virtual si está en un sistema Windows.Si está en un sistema de 32 bits, puede intentar usar algo como Extensión de dirección física (PAE).

También podrías considerar poner limitaciones de entrada para tu programa y tener una diferente para sistemas de 32 y 64 bits.

Sospecho sus problemas de memoria son de mantener el árbol BSP en la memoria. Por lo tanto mantener el BSP en el disco y sólo mantener algunos trozos en la memoria. Esto debería ser bastante fácil con BSP, ya que la estructura se presta más que otras estructuras de árbol, y la lógica debe ser simple. Para ser eficiente y amigable de memoria que podría tener un caché w / bandera sucia, con el tamaño de caché se establece en la memoria disponible menos un poco de espacio para respirar.

Suponiendo que está usando Windows XP, si sólo un poco más de su límite de memoria y no desear o tener el tiempo para reelaborar el código como se sugirió anteriormente, puede agregar el modificador / 3GB a su archivo boot.ini y entonces, sólo una cuestión de establecer un modificador de vinculador para obtener un adicional de 1 GB de la memoria.

Hay que entender que la memoria virtual es diferente de "RAM" en el que la cantidad de memoria virtual que está utilizando es la cantidad total que usted ha reservado, mientras que la memoria real (en Windows que su llamado conjunto de trabajo) es la memoria que en realidad se ha modificado o bloqueado.

Como lo indicó otra persona, en plataformas Windows de 32 bits el límite de memoria virtual es 2 gigabytes menos que establezca la bandera especial para 3 gigabytes y puede asegurarse de que todos los punteros tanto en su código y las bibliotecas que utilizan un solo uso punteros sin firmar.

Así que, o forzar a los usuarios a 64 bits o el control de su memoria virtual y tapado el tamaño de bloque máximo a algo que se ajusta cómodamente dentro de los límites impuestos por los sistemas operativos de 32 bits sería mi consejo.

He chocado contra la pared de 32 bits en Windows, pero no tienen experiencia con el trabajo en torno a estas limitaciones en Linux, así que sólo he hablado de las ventanas laterales de las cosas.

En XP de 32 bits a su máximo espacio de direcciones del programa es de 2 GB. Entonces usted tiene la fragmentación debido a la DLL y los controladores de carga para arriba en su espacio de direcciones. Por último, usted tiene el problema de la fragmentación del montón.

Su mejor movimiento es sólo para acabar de una vez y se ejecuta como un proceso de 64 bits (en un sistema de 64 bits). De repente todos estos problemas desaparecen. Se puede utilizar una mejor montón para mitigar los efectos montón de fragmentación, y puede probar a usar VirtualAlloc para apoderarse de su memoria en una gran parte contigua (y entonces llegar a administrarlo desde allí!) Para desalentar / drivers de DLL de fragmentarlo.

Por último, se puede dividir el BSP través de procesos. Complicado y doloroso, y, francamente, sólo poner en el disco sería más fácil, pero en teoría se podría obtener un mejor rendimiento por tener un grupo de procesos de intercambio de información, si se puede mantener todo residente (y suponiendo que usted puede ser más inteligente que la memoria que el OS puede manejar archivos de amortiguación ... que es un gran si). Cada proceso necesitaría mucha menos memoria y por lo tanto no se debe ejecutar en el límite de espacio de direcciones de 2 GB. Por supuesto, usted quema a través de RAM / cambiar mucho más rápido.

Puede mitigar los efectos de la fragmentación del espacio de direcciones mediante la asignación de partes más pequeñas. Esto tendrá otros efectos secundarios desagradables, pero podría seguir una política de retroceso, donde agarrar trozos más pequeños de la memoria si no asignar correctamente. Con frecuencia, este enfoque simple le conseguirá un programa que funciona cuando de otro modo no lo haría, pero el resto del tiempo funciona tan bien como podría.

Boy, no de 64 bits solo suena mucho mejor que las otras opciones?

¿Cómo está la asignación de memoria para los puntos? ¿Está asignando un punto a la vez (por ejemplo, pt = new Point). A continuación, dependiendo del tamaño del punto, algo de memoria puede quedar desperdiciado. Por ejemplo, en ventanas de memoria se asigna en los múltiplos de 16 bytes, por lo que incluso si usted pregunta intenta asignar 1 byte, OS realmente destinar 16 bytes.

Si este es el caso, utilizando un asignador de memoria puede ayudar. Se puede realizar una comprobación rápida usando asignador STL. (Más de cargar el nuevo operador para la clase Point y utilizar el asignador de STL para asignar memoria en lugar de 'malloc' o por defecto nuevo operador).

No se le puede asignar y desasignar la memoria de una manera óptima. Como otros han señalado, es posible que haya un escape de memoria y no saberlo. Depuración y optimización de la asignación de memoria tomarán tiempo.

Si no desea pasar tiempo optimizar el uso de memoria, ¿por qué no probar el conservador del colector de basura ? Es un reemplazo plug-in para malloc () / nuevo y libre (). De hecho, free () es un no-op, lo que sólo puede eliminar esas llamadas de su programa. Si, en cambio, a mano optimizar su programa y gestionar una reserva de memoria como se sugirió anteriormente, que va a terminar haciendo un montón de trabajo que la CGC ya lo hace por usted.

Es necesario para transmitir la salida, así como su entrada. Si no está orientado a flujo formato de salida, considere hacer segunda pasada. Por ejemplo, si el archivo de salida se inicia con la suma de comprobación / tamaño de los datos, deje espacio en la primera pasada y buscar / escritura a ese espacio más adelante.

suena como que está haciendo TXT para la conversación binaria entonces ¿por qué es necesario tener todos los datos en la memoria ?.
¿No puedes leer una primitiva de txt (XML) a continuación, guardar BinaryStream?

Si quieres ser de tamaño de memoria independiente, se necesita un algoritmo independiente del tamaño. No importa el tamaño de la memoria RAM es, si usted no tiene uso de memoria bajo control, vas a topar en la frontera.

Tome un vistazo al menos parte de la información que posiblemente puede utilizar para producir un poco de salida. Entonces pensar en una manera de dividir la entrada en trozos de este tamaño.

Ahora que suena fácil, ¿verdad? (Me alegro de no tener que hacerlo :))

No es necesario cambiar a equipos de 64 bits, ni necesita la mayor parte de las 1000 cosas sugeridas por otros. Lo que necesita es un algoritmo más reflexivo.

Aquí hay algunas cosas que puede hacer para ayudar con esta situación:

  • Si estás en Windows, utilizar Mapas de archivos ( código de ejemplo ). Esto le dará acceso al archivo a través de un único puntero de memoria intermedia como si usted lee todo el archivo en la memoria, pero sin realmente hacer eso. Las versiones recientes de Linux Kernel tienen un mecanismo similar.
  • Si es posible, y parece que se podía, escanear el archivo secuencial y evitar la creación de un DOM en memoria. Esto reducirá en gran medida el tiempo de carga, así como los requisitos de memoria.
  • Uso de Fondos Comunes de memoria! Es probable que tenga muchos objetos pequeños, tales como nodos, puntos y otras cosas. Utilice una memoria combinada para ayudar a cabo (estoy asumiendo que usted está usando un lenguaje no administrado. Búsqueda de asignación agruparon y grupos de memoria).
  • Si estás utilizando un lenguaje administrado, al menos mover esta parte particular, en un lenguaje no administrado y tomar el control de la lectura de la memoria y el archivo. lenguajes administrados tienen una sobrecarga no trivial, tanto en consumo de memoria y rendimiento. (Sí, sé que esto está marcado "C ++" ...)
  • intento de diseñar un algoritmo en el lugar, donde leer y procesar sólo la cantidad mínima de datos a la vez, por lo que sus requisitos de memoria bajarían.

Por último, quiero señalar que las tareas complejas requieren medidas complejas. Si cree que puede permitirse una máquina de 64 bits con 8 GB de RAM, entonces sólo tiene que utilizar "leer el archivo en la memoria, los datos del proceso, escribir la salida" algoritmo, incluso si se necesita un día para terminar.

hay una buena técnica para eso, es almacenar algunos casos en archivos, y después de conseguir que cuando necesite usarlos.

esta técnica es utilizada por muchos programas de código abierto como Doxygen para ser escalable cuando se necesita una gran cantidad de memoria.

Esta es una vieja pregunta, pero, desde que he hecho recientemente lo mismo ....

No hay una respuesta sencilla. En un mundo ideal que tendría que utilizar una máquina con un enorme espacio de direcciones (es decir, 64 bits), y grandes cantidades de memoria física. enorme espacio de direcciones por sí sola no es suficiente o sólo va a thrash. En ese caso, analizar el archivo XML en una base de datos, y con las consultas apropiadas, sacar lo que necesita. Muy probablemente esto es lo que hace la OSM en sí (creo que el mundo está a punto 330GB).

En realidad, todavía estoy usando XP de 32 bits por razones de conveniencia.

Es una solución de compromiso entre el espacio y la velocidad. Se puede hacer casi cualquier cosa en cualquier cantidad de memoria siempre y cuando no importa el tiempo que tarda. El uso de estructuras STL puede analizar cualquier cosa que desee, pero que pronto se quede sin memoria. Puede definir sus propios asignadores que intercambian, pero de nuevo, que va a ser ineficaz debido a que los mapas, vectores, juegos, etc, no se sabe muy bien lo que está haciendo.

La única manera que encontré para hacer que todo funcione en una pequeña huella en una máquina de 32 bits era pensar muy cuidadosamente acerca de lo que estaba haciendo y lo que se necesita cuando y romper la tarea en trozos. Eficiente de la memoria (nunca usa más de ~ 100 MB), pero no de forma masiva rápida, pero entonces no importa - ¿Con qué frecuencia tiene uno que analizan los datos XML

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