2009-04-12 10 views
12

He escrito un convertidor que toma los archivos xml de openstreetmap y los convierte a un formato de renderizado de tiempo de ejecución binario que es típicamente alrededor del 10% del tamaño original. Los tamaños de los archivos de entrada suelen ser de 3gb o más. Los archivos de entrada no se cargan en la memoria de una sola vez, sino que se transmiten a medida que se recopilan puntos y polis, luego se ejecuta una bsp 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). Por lo general, mi programa usa de 1 gb a 1.2 gb de memoria RAM cuando esto sucede. Intenté aumentar la memoria virtual de 2 a 8 GB (en XP) pero este cambio no tuvo efecto. Además, dado que este código es de código abierto, me gustaría que funcione independientemente del RAM disponible (aunque sea más lento), que se ejecuta en Windows, Linux y Mac.¿Cómo evitar quedarse sin memoria en la aplicación de uso de memoria alta? C/C++

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

Respuesta

14

En primer lugar, en un sistema de 32 bits, siempre estará limitado a 4 GB de memoria, sin importar la configuración del archivo de paginación. (Y de esos, solo 2 GB estarán disponibles para su proceso en Windows. En Linux, normalmente tendrá alrededor de 3 GB disponibles)

Así que la primera solución obvia es cambiar a un sistema operativo de 64 bits y compilar su aplicación para 64 bits. Eso le brinda un gran espacio de memoria virtual para usar, y el sistema operativo intercambiará datos dentro y fuera del archivo de paginación según sea necesario para que todo funcione.

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

En tercer lugar, separe el problema. No procese todo el conjunto de datos de una vez, intente cargar y procesar solo una pequeña sección a la vez.

+0

Windows puede tener 3 GB de espacio virtual con/LARGEADDRESSAWARE –

+0

El indicador permite que el proceso use hasta 4 GB * si el sistema operativo puede proporcionarlo *. Normalmente, Windows todavía está configurado para dar solo 2GB a cada proceso. Eso también se puede cambiar, a riesgo de inestabilidad del controlador, para darte 3GB, sí. Con PAE, puede obtener aún más. Pero 64-bit es probablemente una mejor apuesta. – jalf

+1

Imho, la tercera opción es la más importante. Además de dar control sobre la memoria, también permite el procesamiento paralelo. – xtofl

4

Parece que ya está haciendo un enfoque basado en SAX para el procesamiento XML (cargando el XML sobre la marcha en lugar de todos a la vez).

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

En ocasiones, puede ampliar la memoria mediante el uso del disco duro cuando sea necesario en su algoritmo.

Si no puede dividir su algoritmo, probablemente desee algo como memory mapped files.

En el peor de los casos puede intentar utilizar algo como VirtualAlloc si está en un sistema de Windows. Si está en un sistema de 32 bits, puede intentar usar algo como Physical Address Extension (PAE).

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

4

¿Ha comprobado que no haya pérdidas de memoria en ninguna parte?

Como su programa es portátil para Linux, le sugiero que lo ejecute en Valgrind para estar seguro.

+0

Sí He comprobado si hay fugas, no hay ninguna. – KPexEA

0

Parece que está haciendo txt a una conversación binaria, entonces ¿por qué necesita tener todos los datos en la memoria ?.
¿No puedes leer una primitiva de txt (xml) y luego guardarla en binarystream?

2

Suponiendo que está utilizando Windows XP, si solo está por sobre su límite de memoria y no desea o no tiene el tiempo para volver a trabajar el código como se sugirió anteriormente, puede agregar el modificador/3GB a su archivo boot.ini y luego solo es cuestión de configurar un interruptor de engarce para obtener un extra de 1GB de memoria.

+0

No es tan fácil de usar 3GB. Debe asegurarse de que todas las operaciones aritméticas de su puntero sean seguras, de lo contrario se bloqueará cuando el uso de la memoria aumente. Consulte http://blogs.msdn.com/oldnewthing/archive/2004/08/12/213468.aspx para obtener más información. – eran

3

Sospecho que sus problemas de memoria son de mantener el árbol BSP en la memoria. Así que mantenga el BSP en el disco y solo conserve 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 debería ser simple. Para ser eficiente y compatible con la memoria, podría tener un caché con bandera sucia, con el tamaño de caché establecido en la memoria disponible un poco menos para poder respirar.

1

Tiene que entender que la memoria virtual es diferente de "RAM" porque la cantidad de memoria virtual que está utilizando es la cantidad total que ha reservado, mientras que la memoria real (en Windows se llama conjunto de trabajo) es memoria que realmente has 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 solamente utilizar punteros sin signo.

Por lo tanto, obligar a los usuarios a 64 bits o monitorear su memoria virtual y limitar su tamaño de bloque máximo a algo que encaja 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 tengo experiencia en resolver estas limitaciones en Linux, así que solo he hablado sobre el lado Windows de las cosas.

1

En XP de 32 bits, su espacio máximo de direcciones de programa es de 2 GB. Luego tiene fragmentación debido a las DLL y los controladores que se cargan en su espacio de direcciones. Finalmente, tienes el problema de tu montón fragmentándose.

Su mejor jugada es solo terminarlo y ejecutarlo como un proceso de 64 bits (en un sistema de 64 bits). De repente, todos estos problemas desaparecen. Puede utilizar un montón más dinámico para mitigar los efectos de fragmentación del montón, y puede intentar usar VirtualAlloc para tomar su memoria en un gran bloque contiguo (¡y luego puede administrarlo desde allí!) Para evitar que los DLL/controladores lo fragmenten.

Finalmente, puede dividir su BSP en todos los procesos. Complicado y doloroso, y francamente simplemente ponerlo en el disco sería más fácil, pero en teoría podría obtener un mejor rendimiento al tener un grupo de procesos intercambiando información, si puede mantener todo residente (y suponiendo que puede ser más inteligente que la memoria que el sistema operativo puede manejar el búfer de archivos ... que es un gran si). Cada proceso necesitaría mucha menos memoria y, por lo tanto, no debería ejecutarse en el límite de espacio de direcciones de 2 GB. Por supuesto, quemará RAM/swap mucho más rápido.

Puede mitigar los efectos de la fragmentación del espacio de direcciones mediante la asignación de fragmentos más pequeños. Esto tendrá otros efectos secundarios desagradables, pero podría seguir una política de rechazo en la que atrape trozos de memoria cada vez más pequeños si no puede asignar correctamente. Con frecuencia, este simple enfoque le proporcionará un programa que funciona cuando de otro modo no funcionaría, pero el resto del tiempo funciona tan bien como podría.

Chico, ¿la computación de 64 bits no suena mucho mejor que las otras opciones?

1

¿Cómo asigna la memoria para los puntos? ¿Está asignando el punto uno a la vez (por ejemplo, pt = new Point). Luego, dependiendo del tamaño del punto, algunos recuerdos pueden desperdiciarse.Por ejemplo, en la memoria de Windows se asigna el múltiplo de 16 bytes, por lo que incluso si se solicita, intente asignar 1 byte, el sistema operativo realmente asignará 16 bytes.

Si este es el caso, usar un asignador de memoria puede ayudar. Puede hacer una comprobación rápida utilizando el asignador STL. (Sobrecargar el nuevo operador para la clase Point y usar el asignador STL para asignar memoria en lugar de 'malloc' u operador nuevo predeterminado).

+0

Estoy asignando puntos y polys usando un administrador de montón para que ocupen todo el espacio necesario y casi no los escuché desde que mi montón asignó (en este caso) 1mb fragmentos y distribuye solicitudes de cada fragmento – KPexEA

+0

Otra razón podría ser 'memoria' fragmentation '(es decir, la memoria está disponible en fragmentos pequeños, pero cuando pide' 1 Mb ', el fragmento contiguo de 1 MB no está disponible. ¿El analizador XML usa' heap manager '? Puede ser que el analizador XML esté utilizando asignación de memoria estándar y cause fragmentación? –

0

Si desea tener independencia del tamaño de la memoria, necesita un algoritmo independiente del tamaño. No importa qué tamaño tenga su memoria RAM, si no tiene el uso de memoria bajo control, se encontrará con el borde.

Eche un vistazo a la menor cantidad de información que pueda utilizar para producir un poco de salida. Luego piense en una forma de dividir la entrada en pedazos de este tamaño.

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

1

Es posible que no esté asignando y desasignando la memoria de manera óptima. Como otros han señalado, es posible que esté perdiendo memoria y no lo sepa. La depuración y optimización de la asignación de memoria llevará tiempo.

Si no quiere perder tiempo optimizando el uso de la memoria, ¿por qué no prueba el Conservative Garbage Collector? Es un reemplazo de plug-in para malloc()/new y free(). De hecho, free() no es operativo, por lo que puede eliminar esas llamadas de su programa. Si, en cambio, optimiza a mano su programa y administra un grupo de memoria como se sugirió anteriormente, terminará haciendo gran parte del trabajo que el CGC ya hace por usted.

1

Necesita transmitir su salida así como su entrada. Si su formato de salida no está orientado a flujo, considere hacer segundo pase. Por ejemplo, si el archivo de salida comienza con la suma/tamaño de verificación de los datos, deje espacio en la primera pasada y luego busque/escriba en ese espacio.

0

No necesita cambiar a las máquinas de 64 bits, ni necesita la mayoría de las 1000 cosas sugeridas por otros. Lo que necesitas 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 (sample code). Esto le dará acceso al archivo a través de un único puntero de buffer como si usted leyera todo el archivo en la memoria, pero sin hacerlo realmente. Las versiones recientes de Linux Kernel tienen un mecanismo similar.
  • Si puede, y parece que podría, escanee el archivo secuencialmente y evite crear un DOM en memoria. Esto disminuirá en gran medida el tiempo de carga y los requisitos de memoria.
  • ¡Utilice la memoria combinada! Probablemente tengas muchos objetos pequeños, como nodos, puntos y otras cosas. Use una memoria combinada para ayudar (supongo que está usando un lenguaje no administrado. Busque agrupaciones de memoria y asignación agrupadas).
  • Si está utilizando un lenguaje administrado, al menos mueva esta parte en particular a un lenguaje no administrado y tome el control de la memoria y la lectura de archivos. Los idiomas administrados tienen una sobrecarga no trivial tanto en la huella de la memoria como en el rendimiento. (Sí, sé que esto está etiquetado como "C++" ...)
  • Intente diseñar un algoritmo in situ, donde lea y procese solo la cantidad mínima de datos a la vez, para que sus requisitos de memoria disminuyan.

Finalmente, permítanme 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, simplemente utilice el algoritmo "leer archivo en memoria, procesar datos, escribir salida", incluso si tarda un día en finalizar.

0

hay una buena técnica para eso, es almacenar algunas instancias en archivos, y después de obtenerlos cuando necesite usarlos.

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

0

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

No hay una respuesta sencilla. En un mundo ideal, utilizaría una máquina con gran espacio de direcciones (es decir, 64 bits) y cantidades masivas de memoria física. Enorme espacio de direcciones por sí solo no es suficiente o simplemente se sacudirá. En ese caso, analice el archivo XML en una base de datos y, con las consultas adecuadas, extraiga lo que necesita. Es muy probable que esto sea lo que hace OSM (creo que el mundo tiene alrededor de 330 GB).

En realidad sigo usando XP 32bit por razones de conveniencia.

Es una compensación entre el espacio y la velocidad. Puede hacer casi cualquier cosa en cualquier cantidad de memoria siempre que no le importe cuánto tiempo lleva. Usando estructuras STL puedes analizar todo lo que quieras, pero pronto te quedará sin memoria. Puede definir sus propios asignadores que intercambien, pero de nuevo, será ineficiente porque los mapas, vectores, conjuntos, etc., realmente no saben 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 que pensar muy cuidadosamente acerca de lo que estaba haciendo y lo que se necesita cuando y romper la tarea en trozos. La memoria es eficiente (nunca usa más de ~ 100MB) pero no es masivamente rápida, pero no importa, ¿con qué frecuencia se deben analizar los datos XML?

Cuestiones relacionadas