2012-05-04 13 views
22

A continuación se muestran dos programas que escriben 50,000,000 de bytes en un archivo.¿La escritura de archivo C++ de corriente utiliza un búfer?

El primer programa, escrito en C, utiliza un búfer, que una vez rellenado con un valor arbitrario, escribe en el disco y luego repite ese proceso hasta que se escriben todos los 50,000,000 de bytes. Noté que a medida que aumentaba el tamaño del buffer, el programa tardaba menos tiempo en ejecutarse. Por ejemplo, en BUFFER_SIZE = 1, el programa tardó ~ 88.0463 segundos, mientras que en BUFFER_SIZE = 1024, el programa solo tardó ~ 1.7773 segundos. El mejor momento que grabé fue cuando BUFFER_SIZE = 131072. Como el BUFFER_SIZE aumentó más que eso, noté que comenzó a tomar un poco más de tiempo.

El segundo programa, escrito en C++, utiliza ofstream para escribir un byte a la vez. Para mi sorpresa, el programa solo tardó ~ 1.87 segundos en ejecutarse. Esperaba que tardara un minuto más o menos, como el programa C con BUFFER_SIZE = 1. Obviamente, C++ ofstream maneja la escritura de archivos de forma diferente a lo que pensaba. De acuerdo con mis datos, está funcionando de manera muy similar al archivo C con BUFFER_SIZE = 512. ¿Utiliza algún tipo de memoria intermedia detrás de escena?

Aquí es el programa C:

const int NVALUES = 50000000; //#values written to the file 
const char FILENAME[] = "/tmp/myfile"; 
const int BUFFER_SIZE = 8192; //# bytes to fill in buffer before writing 

main() 
{ 
    int fd; //File descriptor associated with output file 
    int i; 
    char writeval = '\0'; 
    char buffer[BUFFER_SIZE]; 

    //Open file for writing and associate it with the file descriptor 
    //Create file if it does not exist; if it does exist truncate its size to 0 
    fd = open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); 

    for(i=0;i<NVALUES;i++) 
    { 
     //Package bytes into BUFFER_SIZE chunks 
       //and write a chunk once it is filled 
     buffer[i%BUFFER_SIZE] = writeval; 
     if((i%BUFFER_SIZE == BUFFER_SIZE-1 || i == NVALUES-1)) 
      write(fd, buffer, i%BUFFER_SIZE+1); 

    } 

    fsync(fd); 

    close(fd); 
} 

Aquí es el programa en C++:

int main() 
{ 
    ofstream ofs("/tmp/iofile2"); 
    int i; 

    for(i=0; i<50000000; i++) 
     ofs << '\0'; 

    ofs.flush(); 
    ofs.close(); 

    return 0; 
} 

Gracias por su tiempo.

+4

Sí, está almacenado en segundo plano detrás de escena, como indica 'ofs.flush();'. –

+7

¡Oh! Me gusta mucho cuando los "recién llegados" hacen este tipo de preguntas: datos experimentales, hipótesis inteligentes ... ¡Señor, es un placer tenerte a bordo! –

+0

Siempre he escuchado que 4096 (tamaño de página del sistema operativo) u 8192 (tamaño de página del sistema operativo doble) eran el mejor número para apuntar. Los tamaños más grandes que esos pueden ser más rápidos, pero el aumento en la velocidad no valía el aumento en la memoria. –

Respuesta

8

Sí, ostreams usa un buffer de transmisión, alguna subclase de una instanciación de la plantilla basic_streambuf. La interfaz de basic_streambuf está diseñada para que una implementación pueda almacenar en búfer si hay una ventaja en eso.

Sin embargo, este es un problema de calidad de implementación. Las implementaciones no están obligadas a hacer esto, pero cualquier implementación competente lo hará.

Puede leer todos los detalles en el capítulo 27 de la norma ISO, aunque tal vez una fuente más legible es El ++ biblioteca estándar de C: A Tutorial y Referencia (google search).

2

Per this, ofstream tiene un puntero interno filebuf, se puede leer a través de la función rdbuf, que apunta a un objeto streambuf, que es la siguiente:

streambuf objetos se asocian generalmente con un carácter específico secuencia , desde el cual leen y escriben datos a través de un buffer de memoria interno. El búfer es una matriz en la memoria que se espera que se sincronice cuando sea necesario con el contenido físico de la secuencia de caracteres asociada .

En negrita los bits importantes, parece que hace uso de un búfer, pero no sé o no he descubierto qué tipo de búfer es.

+0

Gracias por eso, debo haberlo echado de menos cuando estaba viendo la página de referencia de C++. – TimeBomb006

+1

@ TimeBomb006: No es la página de referencia de C++. Es la página de referencia * a * C++, y si está buscando una respuesta definitiva, no es confiable. –

+0

Gracias @BenjaminLindley por esa aclaración. – TimeBomb006

9

Sí, todas las operaciones de flujo se almacenan en búfer, aunque por defecto la salida estándar de entrada, salida y error no es tal que las interacciones con el C IO sean menos sorprendentes.

Como ya se mencionó, hay una clase base streambuf que se utiliza detrás de escena. Se proporciona con su propio búfer, cuyo tamaño es un detalle de implementación.

Puede comprobar (experimentalmente) la cantidad de este buffer es mediante el uso de streambuf::in_avail, suponiendo que filestream de entrada y de salida filestream están configurados con el mismo tamaño de memoria intermedia ...

Hay otras dos operaciones que se pueden hacer aquí que pueden ser de interés:

  • puede cambiar el objeto streambuf utilizado por una corriente, para cambiar a una versión personalizada
  • puede cambiar el tampón utilizado por el objeto streambuf

tanto debería hacerse de forma correcta después de crear la corriente o después de un flush, para que no se pierde un poco de datos ...

Para ilustrar el cambio de tampón, echa un vistazo a streambuf::putsetbuf:

#include <fstream> 
#include <vector> 

int main() { 
    std::vector<char> vec(512); 

    std::fstream fs; 
    fs.rdbuf()->pubsetbuf(&vec.front(), vec.size()); 

    // operations with file stream here. 
    fs << "Hello, World!\n"; 

    // the stream is automatically closed when the scope ends, so fs.close() is optional 
    // the stream is automatically flushed when it is closed, so fs.flush() is optional 

    return 0; 
} 

Ahora puede repetir los experimentos que hizo en C para encontrar el punto óptimo :)

+0

No es cierto que todas las operaciones de flujo estén en búfer - https://connect.microsoft.com/VisualStudio/feedback/details/642876/std-wcout-is-ten-times-slower-than-wprintf-performance-bug- in-c-library – Sam

+0

@Sam: supongo que te refieres a * El problema es que al imprimir en la consola (en lugar de, digamos, ser redirigido a un archivo), ni nuestra C ni C++ I/O son almacenadas de forma predeterminada . * (de lo contrario, por favor precisa). Diría que mi primera oración termina con * aunque, por defecto, la entrada estándar, la salida y la salida de error no son tales que las interacciones con C IO sean menos sorprendentes *, que es exactamente lo mismo. –

Cuestiones relacionadas