std:: fstream buffering vs buffering manual (¿por qué ganancia de 10x con buffering manual)?


He probado dos configuraciones de escritura:

1) Fstream buffering:

// Initialization
const unsigned int length = 8192;
char buffer[length];
std::ofstream stream;
stream.rdbuf()->pubsetbuf(buffer, length);
stream.open("test.dat", std::ios::binary | std::ios::trunc)

// To write I use :
stream.write(reinterpret_cast<char*>(&x), sizeof(x));

2) Almacenamiento manual en búfer :

// Initialization
const unsigned int length = 8192;
char buffer[length];
std::ofstream stream("test.dat", std::ios::binary | std::ios::trunc);

// Then I put manually the data in the buffer

// To write I use :
stream.write(buffer, length);

Esperaba el mismo resultado...

Pero mi buffering manual mejora el rendimiento en un factor de 10 para escribir un archivo de 100MB, y el buffering fstream no cambia nada en comparación con la situación normal (sin redefinir un buffer).

¿alguien tiene una explicación de esta situación ?

EDITAR : Aquí están las noticias: a benchmark acaba de hacer en una supercomputadora (linux arquitectura de 64 bits, dura intel Xeon 8-core, sistema de archivos Lustre y ... esperemos que compiladores bien configurados) Benchmark (y no explico la razón de la" resonancia " para un buffer manual de 1kB...)

[2] EDITAR 2 : Y la resonancia en 1024 B (si alguien tiene una idea sobre eso, estoy interesado) : introduzca la descripción de la imagen aquí
Author: Vincent, 2012-10-21

3 answers

Esto se debe básicamente a la sobrecarga de llamadas a funciones y a la indirección. El método ofstream::write() se hereda de ostream. Esa función no es insertados en libstdc++, que es la primera fuente de sobrecarga. Entonces ostream:: write () tiene que llamar a rdbuf ()->sputn () para hacer la escritura real, que es una llamada a función virtual.

Además de eso, libstdc++ redirige sputn() a otra función virtual xsputn() que agrega otra llamada a función virtual.

Si pones los caracteres en el búfer usted mismo, puede evitar esa sobrecarga.

 23
Author: Vaughn Cato,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-21 18:06:15

Me gustaría explicar cuál es la causa del pico en la segunda carta

De hecho, las funciones virtuales utilizadas por std::ofstream conducen a la disminución del rendimiento como vemos en la primera imagen, pero no da una respuesta por qué el rendimiento más alto fue cuando el tamaño del búfer manual era inferior a 1024 bytes.

El problema se relaciona con el alto costo de writev() y write()) llamada al sistema e implementación interna de std::filebuf clase interna de std::ofstream.

Para mostrar el cómo influye write() en el rendimiento Hice una prueba simple usando la herramienta dd en mi máquina Linux para copiar un archivo de 10MB con diferentes tamaños de búfer (opción bs):

test@test$ time dd if=/dev/zero of=zero bs=256 count=40000
40000+0 records in
40000+0 records out
10240000 bytes (10 MB) copied, 2.36589 s, 4.3 MB/s

real    0m2.370s
user    0m0.000s
sys     0m0.952s
test$test: time dd if=/dev/zero of=zero bs=512 count=20000
20000+0 records in
20000+0 records out
10240000 bytes (10 MB) copied, 1.31708 s, 7.8 MB/s

real    0m1.324s
user    0m0.000s
sys     0m0.476s

test@test: time dd if=/dev/zero of=zero bs=1024 count=10000
10000+0 records in
10000+0 records out
10240000 bytes (10 MB) copied, 0.792634 s, 12.9 MB/s

real    0m0.798s
user    0m0.008s
sys     0m0.236s

test@test: time dd if=/dev/zero of=zero bs=4096 count=2500
2500+0 records in
2500+0 records out
10240000 bytes (10 MB) copied, 0.274074 s, 37.4 MB/s

real    0m0.293s
user    0m0.000s
sys     0m0.064s

Como puede ver, cuanto menos buffer es, menos velocidad de escritura es y mucho tiempo dd pasa en el espacio del sistema. Por lo tanto, la velocidad de lectura/escritura disminuye cuando disminuye el tamaño del búfer.

Pero, ¿por qué la velocidad más alta fue cuando el tamaño del búfer manual era inferior a 1024 bytes en las pruebas de búfer manual del creador de temas? Por qué fue casi constante?

La explicación se refiere a la aplicación std::ofstream, especialmente a la std::basic_filebuf.

Por defecto usa 1024 bytes buffer (variable BUFSIZ). Por lo tanto, cuando escribe sus datos usando peaces menos de 1024, writev() (no write()) la llamada al sistema se llama al menos una vez para dos operaciones ofstream::write() (las peaces tienen un tamaño de 1023 ofstream::write() la velocidad no depende de el tamaño del búfer manual antes del pico (write() se llama al menos dos veces rara vez).

Cuando intenta escribir un búfer mayor o igual a 1024 bytes a la vez usando ofstream::write() call, writev() se llama al sistema para cada ofstream::write. Por lo tanto, verá que la velocidad aumenta cuando el búfer manual es mayor que 1024 (después del pico).

Además, si desea establecer std::ofstream buffer mayor que 1024 buffer (por ejemplo, 8192 bytes buffer) utilizando streambuf::pubsetbuf() y llamar a ostream::write() para escribir datos utilizando paz de 1024 tamaño, te sorprendería que la velocidad de escritura será la misma que usarás 1024 buffer. Es porque implementación de std::basic_filebuf - la clase interna de std::ofstream - es codificada para forzar llamando al sistema writev() para cada llamada ofstream::write() cuando el búfer pasado es mayor o igual a 1024 bytes (ver basic_filebuf::xsputn() código fuente). También hay un tema abierto en el GCC bugzilla que se informó en 2014-11-05.

Así, el la solución de este problema se puede hacer usando dos casos posibles:

  • reemplaza std::filebuf por tu propia clase y redefine std::ofstream
  • divida un amortiguador, que tiene que ser pasado a la ofstream::write(), a las paces menos de 1024 y pasarlas a la ofstream::write() uno por uno
  • no pase pequeñas cantidades de datos a ofstream::write() para evitar disminuir el rendimiento en las funciones virtuales de std::ofstream
 1
Author: nomad85,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-02 15:52:10

Me gustaría añadir a las respuestas existentes que este comportamiento de rendimiento (toda la sobrecarga de las llamadas al método virtual/indirección) no suele ser un problema si se escriben grandes bloques de datos. Lo que parece haber sido omitido de la pregunta y estas respuestas previas (aunque probablemente implícitamente entendidas) es que el código original estaba escribiendo un pequeño número de bytes cada vez. Solo para aclarar a los demás: si está escribiendo grandes bloques de datos (~kB+), no hay razón para esperar el almacenamiento en búfer manual tendrá una diferencia de rendimiento significativa con respecto al uso del almacenamiento en búfer de std::fstream.

 0
Author: wolf1oo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-19 16:56:48