2012-10-01 20 views
9

Estoy sincronizando la diferencia entre varias formas de imprimir texto en salida estándar. Estoy probando cout, printf y ostringstream usando \n y std::endl. Esperaba que std::endl marcara la diferencia con cout (y lo hizo), pero no esperaba que ralentizara la salida con ostringstream. Pensé que al usar std::endl se escribiría un \n en la transmisión y solo se enjuagaría una vez. ¿Que está pasando aqui? Aquí está todo mi código:¿Por qué usar std :: endl con ostringstream afecta la velocidad de salida?

// cout.cpp 
#include <iostream> 

using namespace std; 

int main() { 
    for (int i = 0; i < 10000000; i++) { 
    cout << "Hello World!\n"; 
    } 
    return 0; 
} 

// printf.cpp 
#include <stdio.h> 

int main() { 
    for (int i = 0; i < 10000000; i++) { 
    printf("Hello World!\n"); 
    } 
    return 0; 
} 

// stream.cpp 
#include <iostream> 
#include <sstream> 

using namespace std; 

int main() { 
    ostringstream ss; 
    for (int i = 0; i < 10000000; i++) { 
    ss << "stream" << endl; 
    } 
    cout << ss.str(); 
} 

// streamn.cpp 
#include <iostream> 
#include <sstream> 

using namespace std; 

int main() { 
    ostringstream ss; 
    for (int i = 0; i < 10000000; i++) { 
    ss << "stream\n"; 
    } 
    cout << ss.str(); 
} 

y mi Makefile

SHELL:=/bin/bash 

all: cout.cpp printf.cpp 
    g++ cout.cpp -o cout.out 
    g++ printf.cpp -o printf.out 
    g++ stream.cpp -o stream.out 
    g++ streamn.cpp -o streamn.out 
time: 
    time ./cout.out > output.txt 
    time ./printf.out > output.txt 
    time ./stream.out > output.txt 
    time ./streamn.out > output.txt 

aquí Aquí está lo que me pasa cuando corro make seguido por make time

time ./cout.out > output.txt 

real 0m1.771s 
user 0m0.616s 
sys 0m0.148s 
time ./printf.out > output.txt 

real 0m2.411s 
user 0m0.392s 
sys 0m0.172s 
time ./stream.out > output.txt 

real 0m2.048s 
user 0m0.632s 
sys 0m0.220s 
time ./streamn.out > output.txt 

real 0m1.742s 
user 0m0.404s 
sys 0m0.200s 

Estos resultados son consistentes.

+0

Esto probablemente responda a su pregunta: http://stackoverflow.com/questions/213907/c-stdendl-vs-n En resumen: sí, std :: endl vacía la salida – stefan

+2

No sé si leo esta pregunta está equivocada, pero supuse que estaba preguntando por qué enrojecer la secuencia de cadenas tendría un problema de rendimiento. ¿Qué trabajo adicional hace el enjuague de una cadena de caracteres que no se puede dejar solo a la llamada a '.str()'? Para 'cout', la renderización en el terminal llevará tiempo, ¿hay algún efecto secundario visible de enjuagar un stringstream? – loganfsmyth

+1

@stefan Realmente he leído esa pregunta, pero en parte me pregunto qué significa tirar un hilo de cadena. Pensé que esencialmente no haría nada, pero aparentemente estoy equivocado. – gsingh2011

Respuesta

14

std::endl desencadena un flujo de la corriente, lo que ralentiza la impresión mucho. Consulte http://en.cppreference.com/w/cpp/io/manip/endl

A menudo se recomienda no utilizar std::endl a menos que realmente desee que se descargue la corriente. Si esto es realmente importante para usted, depende de su caso de uso.

En cuanto a por qué flush tiene un impacto en el rendimiento incluso en un ostringstream (donde no debe producirse un enjuague): parece que se necesita una implementación para al menos construir los objetos centinela. Esos deben verificar good y tie del ostream. La llamada al pubsync debería poder optimizarse. Esto se basa en mi lectura de libcpp y libstdC++.

Después de leer un poco la pregunta interesante parece ser esta: ¿Es realmente necesaria una implementación de basic_ostringstream::flush para construir el objeto centinela? Si no, esto me parece una cuestión de "calidad de implementación". Pero en realidad creo que es necesario porque incluso un basic_stringbug puede cambiar para tener su conjunto badbit.

+2

De hecho, lo sabía, pero no sabía que afectaba el rendimiento del stringstream. Flushing cout significa dar salida a todo lo que está en el búfer de salida, ¿qué significa tirar cadena de corriente? ¿Cuál es el resultado que arroja el texto? – gsingh2011

+2

@ gsingh2011 Eso es lo que yo pensaría. Pero 'basic_ostringstream' tiene de hecho' basic_stringbuf' adjunto y se requiere para construir el centinela mencionado en la documentación de 'flush'. – pmr

0

Usando std::endl es equivalente a escribir

stream << "\n"; 
stream.flush(); 

No utilice std::endl a menos que realmente quiere desencadenar enrojecimiento y/o no se preocupan por el rendimiento de salida.

Además, no se preocupe por las diferentes terminaciones de línea en diferentes plataformas, su implementación traducirá "\n" a la línea que finaliza apropiada para su plataforma.

1

Cada operación de salida en una corriente Dors múltiples pasos:

  • Se comprueba si la corriente está en buena forma.
  • Comprueba si alguna otra secuencia necesita enjuagarse.
  • Se produce la secuencia de caracteres.
  • Comprueba si hay suficiente espacio para los caracteres escritos.
  • El uso de endl llama a una función virtual adicional en el buffer de transmisión.

Personalmente, espero que la llamada a la función virtual adicional realmente tenga un impacto relativamente pequeño en comparación con las otras operaciones. Usted puede verificar esta suposición también por los perfiles de esta salida:

out << "stream" << '\n'; 

... o incluso

out << "stream" << out.widen('\n'); 

Dicho esto, hay una serie de mejoras que una implementación corriente puede aplicar para reducir en cheques . Si esto se hace dependerá de la implementación, por supuesto.

Cuestiones relacionadas