2012-02-24 7 views
9

Necesito escribir un programa que escribirá muchos caracteres en un archivo de salida. Mi programa también necesitará escribir nueva línea para un mejor formateo. Entiendo que ofstream es un flujo de almacenamiento intermedio y si usamos un flujo de almacenamiento intermedio para el archivo io, obtenemos rendimiento. Sin embargo, si usamos std::endl, la salida se vaciará y perderemos cualquier ganancia de rendimiento potencial debido a la salida amortiguada.Utilizando ofstream para salida con buffer para obtener rendimiento

Supongo que si uso '\n' para una nueva línea, la salida solo se enjuagará cuando lo hagamos std::endl. ¿Es esto correcto? ¿Y hay algún truco que pueda usarse para obtener un aumento en el rendimiento durante la salida del archivo?

Nota: Quiero vaciar la salida del búfer al finalizar las operaciones de escritura del archivo. Creo que de esta forma puedo minimizar la E/S de archivos y, por lo tanto, puedo obtener rendimiento.

+0

¿Necesita enjuagar el archivo o no? No veo cómo puede vaciar el archivo y también obtener las ventajas de rendimiento de no descargar el archivo. (Si desea un rendimiento óptimo de escritura de archivos, la mejor opción es ir con un archivo mapeado en la memoria). – Mankarse

+0

Si le preocupa el rendimiento, no debe utilizar los operadores '<<'. debe adherirse a los métodos de lectura y escritura con un tamaño de búfer suficiente o al mapeo de memoria de su archivo (debe hacerse de forma inteligente). Estás en lo cierto con la comprensión de '\ n' y std :: endl :) – Arunmu

Respuesta

17

En general, el usuario de las clases de flujo no debería interferir con el enjuague de la secuencia si se desea el máximo rendimiento: las corrientes descargan internamente su búfer cuando está lleno. En realidad, esto es más eficiente que esperar hasta que todos los resultados estén listos, especialmente con archivos de gran tamaño: los datos almacenados en el búfer se escriben mientras todavía es probable que estén en la memoria. Si crea un gran buffer y solo lo escribe una vez que el sistema de memoria virtual haya puesto partes de los datos en el disco pero no en el archivo. Necesitaría ser leído del disco y escrito de nuevo.

El punto principal con respecto a std::endl es que las personas abusan de un final de línea que hace que el búfer se descargue y no están conscientes de las implicaciones de rendimiento. La intención de std::endl es que a las personas se les dé el control para descargar archivos en puntos razonables. Para que esto sea efectivo, necesitan saber lo que están haciendo. Lamentablemente, había demasiada gente ignorante de lo que hace std::endl que anunciaba su uso como un final de línea, de modo que se usa en muchos lugares donde está completamente equivocado.

Dicho esto, a continuación se detallan una serie de cosas que quizás desee intentar para mejorar el rendimiento. Supongo que necesita una salida formateada (que el uso de std::ofstream::write() no le proporcionará).

  • Obviamente, no use std::endl a menos que tenga que hacerlo. Si el código de escritura ya existe y usa std::endl en muchos lugares, algunos de los cuales posiblemente estén fuera de su control, puede usar un buffer de flujo de filtrado que usa su buffer interno de tamaño razonable y que no reenvía llamadas a su función sync() al subyacente buffer de transmisión Aunque esto implica una copia adicional, esto es mejor que algunos fallos espúreos, ya que estos son órdenes de magnitud más caros.
  • Aunque no debería tener un efecto en std::ofstream s, llamar al std::ios_base::sync_with_stdio(false) se usa para afectar el rendimiento en algunas implementaciones.Te recomendamos que utilices una implementación diferente de IOstream si esto tiene un efecto porque probablemente haya más cosas incorrectas con respecto al rendimiento.
  • Asegúrese de estar utilizando un std::locale cuyo std::codecvt<...> devuelve true al llamar a su always_noconv(). Esto se puede verificar fácilmente usando std::use_facet<std::codecvt<char, char, stdd::mbstate_t> >(out.get_loc()).always_noconv(). Puede usar std::locale("C") para obtener un std::locale para lo cual esto debería ser cierto.
  • Algunas implementaciones de entornos locales utilizan implementaciones muy ineficientes de sus facetas numéricas e, incluso si son razonablemente buenas, la implementación predeterminada de la faceta std::num_put<char> aún puede hacer cosas que realmente no necesita. Especialmente si su formateo numérico es razonablemente simple, es decir, no sigue cambiando los indicadores de formato, no ha reemplazado el mapeo de caracteres (es decir, no usa una faceta std::ctype<char> graciosa), etc., puede ser razonable usar una función personalizada std::num_put<char> faceta: es bastante fácil crear una función de formateo rápida pero simple para tipos enteros y una función de buen formato para puntos flotantes que no utiliza snprintf() internamente.

Algunas personas han sugerido el uso de archivos mapeados en memoria, pero esto solo funciona razonablemente cuando se conoce con antelación el tamaño del archivo de destino. Si este es el caso, esta es una buena forma de mejorar el rendimiento, de lo contrario no vale la pena molestarse. Tenga en cuenta que puede usar el formato de flujo con los archivos asignados de memoria (o, más generalmente, con cualquier tipo de interfaz de salida) creando un std::streambuf personalizado que utiliza la interfaz de mapeo de memoria. Encuentro que el mapeo de memoria a veces es efectivo cuando se usan con std::istream s. En muchos casos, las diferencias realmente no importan mucho.

Hace mucho tiempo escribí mi propia implementación IOStreams and locales que no sufre de algunos de los problemas de rendimiento mencionados anteriormente (está disponible en my site pero está un poco pasada de moda y no la he tocado por casi 10 años ahora). Todavía hay muchas cosas que se pueden mejorar en esta implementación, pero no tengo una implementación actualizada que estaría listo para publicar en alguna parte. Pronto, con suerte, algo que sigo pensando desde hace casi 10 años ...

+0

@Kuhl muchas gracias por una buena explicación. Como el uso de std :: endl ahora está bajo mi control, no usaré std :: endl.Usaré \ n para una nueva línea. Espero que esto me brinde un mejor rendimiento. Y, tiene razón en cuanto a mi necesidad de salida formateada. – learningstack

3

La impresión de un \n no (necesariamente) purgará la salida, mientras que la impresión std::endl o std::flush lo hará.

Si desea escribir rápidamente y no le importa si los datos están ahí hasta que esté completamente terminado, escriba todos con \n y no se preocupe (ya que closing the file también vaciará la corriente) .

Si aún no obtiene el rendimiento que desea, puede usar fstream::read(char*, int) - le permite leer datos en bloques de cualquier tamaño que desee (intente con bloques más grandes y vea si ayudan).

0

Sí, endl vacía la corriente. No lo use para archivos grandes.

También asegúrese de establecer buffer de secuencia. Al menos la implementación de MSVC copia 1 char a la vez al filebuf cuando no se establece ningún búfer (consulte streambuf::xsputn). Esto puede hacer que su aplicación se vincule a la CPU, lo que dará como resultado tasas de E/S más bajas.

Por lo tanto, añadir algo como esto a su código antes de hacer la escritura:

char buf[256 * 1024]; 
mystream.rdbuf()->pubsetbuf(buf, sizeof(buf)); 

NB: Se puede encontrar un ejemplo de aplicación completa here.

Cuestiones relacionadas