2009-07-29 13 views
43

Suponiendo lo siguiente para ...
Salida:
Se abre el archivo ...
datos es 'largada' en el disco. Los datos en la memoria se encuentran en un gran buffer contiguo. Se escribe en el disco en su forma original directamente desde ese búfer. El tamaño del búfer es configurable, pero se fija durante la duración del flujo. Los búferes se escriben en el archivo, uno tras otro. No se realizan operaciones de búsqueda.
... el archivo está cerrado.¿Cuál es el método más rápido para la E/S de archivos secuenciales de alto rendimiento en C++?

de entrada:
un archivo grande (escrito secuencialmente como arriba) se lee desde el disco de principio a fin.


¿Hay pautas generalmente aceptadas para lograr el archivo más rápida posible secuencial de E/S en C++?

Algunas posibles consideraciones:

  • pautas para elegir el tamaño del búfer óptimo
  • Será una biblioteca portátil como impulso :: asio ser demasiado abstraído para exponer las complejidades de una plataforma específica, o pueden asumirse para ser óptimo?
  • ¿La E/S asíncrona siempre es preferible a la sincronización? ¿Qué ocurre si la aplicación no está unida a CPU?

Me doy cuenta de que esto tendrá consideraciones específicas de la plataforma. Agradezco las pautas generales, así como las de plataformas específicas.
(mi interés más inmediato en Win x64, pero estoy interesado en los comentarios sobre Solaris y Linux también)

+0

¿Desea volver a implementar 'cp'? Creo que me falta algo ... –

Respuesta

29

¿Existen pautas generalmente aceptadas para lograr la E/S de archivo secuencial más rápida posible en C++?

Regla 0: Medir. Use todas las herramientas de generación de perfiles disponibles y familiarícelas. Es casi un mandamiento en la programación que si no lo midió no sabe qué tan rápido es, y para E/S esto es aún más cierto. Asegúrese de probar bajo condiciones reales de trabajo si puede. Un proceso que no tiene competencia para el sistema de E/S puede ser optimizado en exceso, ajustado para condiciones que no existen bajo cargas reales.

  1. Utilice la memoria asignada en lugar de escribir en los archivos. Esto no siempre es más rápido, pero permite la oportunidad de optimizar la E/S de una manera específica para el sistema operativo pero relativamente portátil, evitando la copia innecesaria y aprovechando el conocimiento del sistema operativo sobre cómo se está utilizando el disco. ("Portátil" si usa un contenedor, no una llamada API específica del sistema operativo).

  2. Probar y linealizar su salida tanto como sea posible. Tener que saltar por la memoria para encontrar los búferes para escribir puede tener efectos notables en condiciones optimizadas, ya que las líneas de caché, la paginación y otros problemas del subsistema de memoria comenzarán a importar. Si tiene muchos buffers, busque soporte para scatter-gather I/O que intente hacer esa linealización por usted.

Algunas posibles consideraciones:

  • pautas para elegir el tamaño del búfer óptimo

Tamaño de la página para empezar, pero estar preparados para sintonizar a partir de ahí.

  • Será una biblioteca portátil como impulso :: asio ser demasiado abstraído para exponer las complejidades de una plataforma específica, o pueden ser asumidas como óptima?

No asuma que es óptimo. Depende de cuán exhaustivamente se ejercite la biblioteca en su plataforma, y ​​de cuánto esfuerzo pongan los desarrolladores en hacerlo rápido.Habiendo dicho que una biblioteca de E/S portátil puede ser muy rápido, porque existen abstracciones rápidas en la mayoría de los sistemas, y generalmente es posible obtener una API general que cubra muchas de las bases. Boost.Asio es, según mi leal saber y entender, bastante ajustado para la plataforma en la que se encuentra: hay toda una familia de API específicas de variante OS y OS para la E/S asíncrona rápida (por ej. epoll, /dev/epoll, kqueue, Windows overlapped I/O), y Asio los envuelve a todos.

  • Es E/S asíncrona siempre preferible a modo síncrono? ¿Qué ocurre si la aplicación no está unida a CPU?

E/S asíncrona no es más rápido en un sentido prima de E/S síncrona. Lo que hace la E/S asíncrona es asegurarse de que su código no esté perdiendo tiempo esperando que se complete la E/S. Es más rápido de una manera general que el otro método de no perder ese tiempo, es decir, usar hilos, porque volverá a llamar a su código cuando la E/S esté lista y no antes. No hay inicios en falso o preocupaciones con subprocesos inactivos que necesitan ser finalizados.

+1

excelente respuesta – user394460

3

Como se anotó anteriormente, todo depende de la bibliotecas máquina/instalación/que está utilizando. Una solución rápida en un sistema puede ser lenta en otro.

Sin embargo, una directriz general sería escribir en la mayor cantidad de fragmentos posible.
Normalmente escribir un byte a la vez es el más lento.

La mejor manera de saberlo con certeza es codificar algunas formas diferentes y perfilarlas.

+0

+1 para medir primero! –

5

Para Windows, asegúrese de utilizar el FILE_FLAG_SEQUENTIAL_SCAN en su llamada a CreateFile(), si opta por utilizar la API de Windows específica de la plataforma. Esto optimizará el almacenamiento en caché para la E/S. En lo que respecta al tamaño del búfer, normalmente se recomienda un tamaño de búfer que sea un múltiplo del tamaño del sector del disco. 8K es un buen punto de partida con poco que ganar al ir más grande.

En este artículo se analiza la comparación entre async y sincronización en Windows.

http://msdn.microsoft.com/en-us/library/aa365683(VS.85).aspx

10

Un consejo general es a su vez el uso del búfer y leer/escribir en trozos grandes (pero no demasiado grande, entonces usted va a perder demasiado tiempo de espera para toda la E/S para completar, donde de lo contrario podría comienza a masticar ya en el primer megabyte. Es trivial encontrar el punto óptimo con este algoritmo, solo hay un botón para activar: el tamaño del fragmento).

Más allá de eso, para la entrada mmap() ing el archivo compartido y de solo lectura es (si no el más rápido, entonces) la forma más eficiente. Llame al madvise() si su plataforma lo tiene, para decirle al núcleo cómo va a atravesar el archivo, para que pueda volver a leer y arrojar las páginas de nuevo rápidamente.

Para la salida, si ya tiene un búfer, considere respaldarlo con un archivo (también con mmap()), por lo que no tiene que copiar los datos en el espacio de usuario.

Si mmap() no es de tu agrado, entonces está fadvise() y, para los realmente difíciles, la E/S de archivo asíncrono.

(Todo lo anterior es POSIX, los nombres de Windows pueden ser diferentes).

+1

Solución: fadvise (2) y madvise (2). También las versiones posix se llaman posix_fadvise y posix_madvise – osgx

2

Ha preguntado acerca de C++, pero parece que ya pasó y está listo para ser un poco específico de la plataforma.

En Windows, FILE_FLAG_SEQUENTIAL_SCAN con una asignación de archivos es probablemente la manera más rápida. De hecho, su proceso puede salir antes de que el archivo realmente entre en el disco. Sin una operación de limpieza explícitamente bloqueada, Windows puede tardar hasta 5 minutos en comenzar a escribir esas páginas.

Debe tener cuidado si los archivos no están en dispositivos locales sino en una unidad de red. Los errores de red aparecerán como errores SEH, que deberá estar preparado para manejar.

En * nixes, puede obtener un rendimiento un poco mayor escribiendo secuencialmente en un dispositivo de disco sin formato. Esto también es posible en Windows, pero no tan bien soportado por las API. Esto evitará un poco de sobrecarga del sistema de archivos, pero puede no ser suficiente para ser útil.

Hablando en términos generales, la RAM es 1000 o más veces más rápida que los discos, y la CPU es aún más rápida. Probablemente no haya muchas optimizaciones lógicas que ayudarán, excepto evitar los movimientos de las cabezas de disco (buscar) siempre que sea posible. Un disco dedicado solo para este archivo puede ayudar significativamente aquí.

+1

posix tienen una llamada posix_fadvise compatible con POSIX_FADV_SEQUENTIAL. – osgx

2

Obtendrá el rendimiento más rápido absoluto utilizando CreateFile y ReadFile. Abra el archivo con FILE_FLAG_SEQUENTIAL_SCAN.

Lea con un tamaño de búfer que es una potencia de dos. Solo el benchmarking puede determinar este número. Lo he visto 8K una vez. ¡Otra vez descubrí que era 8 millones! Esto varía salvajemente

Depende del tamaño de la memoria caché de la CPU, de la eficacia de la lectura anticipada del sistema operativo y de la sobrecarga asociada a la realización de muchas escrituras pequeñas.

La asignación de memoria es no la manera más rápida. Tiene más sobrecarga porque no puede controlar el tamaño del bloque y el sistema operativo necesita fallas en todas las páginas.

1

En Linux, buffer de lectura y escritura a acelerar las cosas mucho más arriba, cada vez más con el aumento de tampones tamaños, pero los rendimientos son disminuyendo y por lo general quieren usar BUFSIZ (definido por stdio.h) como grandes tamaños de búfer no ayudará mucho .

mmap ing proporciona el acceso más rápido a los archivos, pero la llamada mmap en sí es bastante costosa. Para archivos pequeños (16KiB) read y write, las llamadas al sistema ganan (consulte https://stackoverflow.com/a/39196499/1084774 para obtener los números al leer read y mmap).

Cuestiones relacionadas