2012-01-03 10 views
7

En mi programa estoy simulando un sistema de N-cuerpo para una gran cantidad de iteraciones. Para cada iteración, produzco un conjunto de coordenadas 6N que necesito adjuntar a un archivo y luego utilizar para ejecutar la siguiente iteración. El código está escrito en C++ y actualmente utiliza el método ofstreamwrite() para escribir los datos en formato binario en cada iteración.La manera más rápida de escribir datos mientras lo produce

No soy un experto en este campo, pero me gustaría mejorar esta parte del programa, ya que estoy en el proceso de optimizar todo el código. Siento que la latencia asociada con la escritura del resultado del cálculo en cada ciclo ralentiza significativamente el rendimiento del software.

Estoy confundido porque no tengo experiencia en programación paralela real y archivos de nivel bajo de E/S. Pensé en algunas técnicas abstractas que me imaginaba que podía poner en práctica, ya que soy de programación para los modernos (posiblemente varios núcleos) máquinas con Unix sistemas operativos:

  • escribir los datos en el archivo en trozos de n iteraciones (no parece para ser mejores maneras de proceder ...)
  • paralelización del código con OpenMP (en realidad la forma de implementar una memoria intermedia de manera que los hilos están sincronizados adecuadamente y no se superponen?)
  • Usando mmap (el tamaño del archivo podría ser enorme, en el orden de los GB, ¿este enfoque es lo suficientemente robusto?)

Sin embargo, no sé cómo implementarlos mejor y combinarlos apropiadamente.

+0

¿Cuál es tu pregunta? –

+6

"Siento que la latencia asociada con la escritura del resultado del cálculo en cada ciclo ralentiza significativamente el rendimiento del software". ¿Lo sientes o perfilaste tu código? –

+0

¿Ha perfilado el código para asegurarse de que sus sentimientos sobre la latencia sean correctos? – Grizzly

Respuesta

3

Por supuesto, escribir en un archivo en cada iteración es ineficiente y muy probablemente ralentice su computación. (como regla general, depende de su caso Actuel)

Tiene que utilizar un patrón de diseño producer ->consumer. Estarán unidos por una cola, como una cinta transportadora.

  • El productor intentará producir tan rápido como sea posible, solo desacelerándose si el consumidor no puede manejarlo.
  • El consumidor intentará "consumir" lo más rápido que pueda.

Al dividir los dos, puede aumentar el rendimiento más fácilmente porque cada proceso es más simple y tiene menos interferencias del otro.

  • Si el productor es más rápido, lo que necesita para mejorar el consumidor, en su caso, al escribir en el archivo de la manera más eficiente, trozo por trozo más probable (como usted ha dicho)
  • Si el consumidor es más rápido , necesitas mejorar al productor, lo más probable es que lo paralelice como dijiste.

No es necesario para optimizar ambos. Solo optimice el más lento (el cuello de botella).

Prácticamente, utiliza hilos y una cola sincronizada entre ellos. Para sugerencias de implementación, eche un vistazo a here, especialmente §18.12 "El patrón Productor-Consumidor".

Acerca de la gestión de flujo, tendrá que agregar un poco más de complejidad seleccionando un "tamaño máximo de cola" y haciendo que los productores esperen si la cola no tiene suficiente espacio. Tenga cuidado con los bloqueos, codifíquelo con cuidado. (Vea el enlace de wikipedia que di sobre eso)

Nota: Es una buena idea usar hilos de refuerzo porque los hilos no son muy portátiles. (bueno, lo son desde C++ 0x pero la disponibilidad de C++ 0x aún no es buena)

+0

Pero, dado el tamaño de los datos, ¿cómo manejar el caso donde el productor es tan rápido que produce algo que no puede almacenarse en la memoria antes de que el consumidor logre guardarlo en el disco? –

+2

@Fiat Lux Bueno, si produce datos más rápido de lo que se puede almacenar en su sistema de archivos, tiene un problema bastante básico completamente independiente de los trucos de software que haga. Si tu ancho de banda es demasiado pequeño, tarde o temprano te quedarás sin espacio en el búfer y tendrás que manejar ese caso de todos modos. – Voo

+1

@FiatLux es por eso que dije "procducer ... solo ralentizando si el consumidor no puede manejarlo". Lo edité para agregar un enlace a un ejemplo de implementación + mejoras sugeridas. Tendrás que hacer que los productores esperen si la memoria está llena. – Offirmo

1

Es mejor dividir la operación en dos procesos independientes: data-producing y file-writing. Data-producing usaría algún búfer para el paso de datos por iteración, y file-writing usaría una cola para almacenar solicitudes de escritura. Luego, data-producing publicará una solicitud de escritura y continuará, mientras que file-writing haría frente a la escritura en segundo plano.

Esencialmente, si los datos se producen mucho más rápido de lo que posiblemente pueda almacenarse, terminará rápidamente reteniendo la mayor parte de ellos en el búfer. En ese caso, su enfoque actual parece ser bastante razonable, ya que se puede hacer poco programáticamente para mejorar la situación.

+0

¿Por qué crees que puedes hacer un mejor trabajo de almacenamiento en memoria intermedia de lo que ya hace el caché de bloque del sistema operativo? –

+0

@SteveC Porque generalmente es mejor implementar el algoritmo de forma determinista, que confiar en las características dependientes de la implementación. Quiero decir, el bloque de memoria caché * es * bueno, pero podría o no ajustarse a la situación específica. Puede que no sea tan portátil, puede que no sea tan rápido, etc., y el OP no fue muy específico de todos modos :) – vines

+0

En ese caso, no debe pasar por dos capas de almacenamiento en búfer, sino simplemente omitir el sistema de archivos y escribir en el disco sin procesar Eso es "más determinista" y puedes "adaptarlo a la situación específica". Personalmente, dudo que puedas hacer un mejor trabajo que los autores del sistema de archivos. –

0

"Uso de mmap (el tamaño del archivo podría ser enorme, del orden de GBs, es este enfoque sólido suficiente?)"

mmap es el método de carga de programas, bibliotecas compartidas y del sistema operativo el archivo de página/intercambio - es tan robusto como cualquier otra E/S de archivos y generalmente tiene un mayor rendimiento.

PERO en la mayoría de los sistemas operativos es malo/difícil/imposible de ampliar el tamaño de un archivo asignado mientras se está utilizando. Entonces, si conoce el tamaño de los datos, o si solo está leyendo, es grandioso. Para un registro/volcado que usted agrega continuamente, es menos sutiable, a menos que conozca un tamaño máximo.

+0

Sé el tamaño de los datos, pero no habrá ningún problema con los sistemas de 32 bits si tengo un archivo de algunos gigabytes? –

+0

@Fiat: esa es la gran ventaja de mmap. Puede mapear múltiples vistas en el archivo con compensaciones para que pueda usar una ventana para acceder a cualquier parte de un archivo tan grande como lo permita su FS. Los detalles dependen de su OS –

1

Si no quieres jugar haciendo cosas en un subproceso diferente, puedes intentar usar aio_write(), lo que permite asincrónico escribe Básicamente le das al sistema operativo el búfer para escribir, y la función regresa inmediatamente, y termina la escritura mientras el programa continúa, puedes verificar más adelante para ver si la escritura se ha completado.

Esta solución aún adolece del problema productor/consumidor mencionado en otras respuestas, si su algoritmo está produciendo datos más rápido de lo que puede escribirse, eventualmente se quedará sin memoria para almacenar los resultados entre el algoritmo y la escritura , entonces tendrías que probarlo y ver cómo funciona.

Cuestiones relacionadas