c++ - read - std:: fstream buffering versus manual buffering(¿por qué 10x de ganancia con buffer manual)?
fstream example (3)
Esto se debe básicamente a la sobrecarga de llamada de función y la indirección. El método ofstream :: write () se hereda de ostream. Esa función no está en línea 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 de 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 coloca los caracteres en el búfer usted mismo, puede evitar esa sobrecarga.
He probado dos configuraciones de escritura:
1) almacenamiento intermedio Fstream:
// 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 en búfer manual:
// 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 almacenamiento en memoria intermedia manual mejora el rendimiento en un factor de 10 para escribir un archivo de 100 MB, y el almacenamiento en memoria intermedia de flujo no cambia nada en comparación con la situación normal (sin redefinir un almacenamiento intermedio).
¿Alguien tiene una explicación de esta situación?
EDITAR: Estas son las noticias: un punto de referencia que se acaba de hacer en una supercomputadora (arquitectura Linux de 64 bits, dura intel Xeon 8-core, sistema de archivos Lustre y ... con suerte compiladores bien configurados) (y no explico el motivo de la "resonancia" para un buffer manual de 1kB ...)
EDIT 2: Y la resonancia en 1024 B (si alguien tiene una idea al respecto, estoy interesado):
Me gustaría agregar a las respuestas existentes que este comportamiento de rendimiento (toda la sobrecarga del método virtual llama / indirectamente) normalmente no es un problema si se escriben grandes bloques de datos. Lo que parece haberse omitido de la pregunta y estas respuestas previas (aunque probablemente se entienden implícitamente) es que el código original estaba escribiendo una pequeña cantidad 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 que el almacenamiento en memoria intermedia manual tenga una diferencia de rendimiento significativa con el uso del almacenamiento en búfer de std::fstream
.
Me gustaría explicar cuál es la causa del pico en el segundo gráfico
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 de 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 las writev()
y write()
) y la implementación interna de std::filebuf
clase interna de std::ofstream
.
Para mostrar cómo influye la 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 se puede ver que cuanto menos memoria intermedia exista, menor será la velocidad de escritura y tanto tiempo dd
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 mayor velocidad se produjo cuando el tamaño del búfer manual era inferior a 1024 bytes en las pruebas del buffer manual del creador de temas? ¿Por qué fue casi constante?
La explicación se relaciona con la implementación std::ofstream
, especialmente con std::basic_filebuf
.
Por defecto usa un buffer de 1024 bytes (variable BUFSIZ). Entonces, cuando escribe sus datos usando paces menores a 1024, la llamada al sistema writev()
(no write()
) se llama al menos una vez para dos ofstream::write()
(las paces tienen un tamaño de 1023 <1024 - primero se escribe a la memoria intermedia, y las segundas fuerzas escribiendo de primera y segunda). En base a esto, podemos concluir que ofstream::write()
speed no depende del tamaño del buffer manual antes del pico ( write()
se llama al menos dos veces raramente).
Cuando intenta escribir mayor o igual a 1024 bytes de búfer a la vez usando la ofstream::write()
, se writev()
sistema writev()
para cada uno ofstream::write
. Entonces, ves que la velocidad aumenta cuando el buffer manual es mayor a 1024 (después del pico).
Además, si desea establecer std::ofstream
buffer std::ofstream
mayor que 1024 buffer (por ejemplo, 8192 bytes buffer) usando streambuf::pubsetbuf()
y llame a ostream::write()
para escribir datos usando paces de tamaño 1024, se sorprendería de que la velocidad de escritura sea la misma que usará el buffer 1024. Es porque la implementación de std::basic_filebuf
- la clase interna de std::ofstream
- está codificada para forzar el llamado del 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 problema abierto en la bugzilla de GCC que se informó en 2014-11-05 .
Entonces, la solución de este problema se puede hacer utilizando dos posibles casos:
- reemplace
std::filebuf
por su propia clase y redefinastd::ofstream
- devide un búfer, que debe pasarse a
ofstream::write()
, a las paces menos de 1024 y pasarlas aofstream::write()
una por una - no pase pequeñas cantidades de datos a
ofstream::write()
para evitar la disminución del rendimiento en las funciones virtuales destd::ofstream