mezclando cout y printf para una producción más rápida

Después de realizar algunas pruebas noté que printf es mucho más rápido que cout . Sé que depende de la implementación, pero en mi cuadro de Linux printf es 8 veces más rápido. Así que mi idea es mezclar los dos métodos de impresión: quiero usar cout para impresiones simples, y planeo usar printf para producir grandes resultados (normalmente en un bucle). Creo que es seguro hacerlo siempre y cuando no olvide descargar antes de cambiar al otro método:

 cout << "Hello" << endl; cout.flush(); for (int i=0; i<1000000; ++i) { printf("World!\n"); } fflush(stdout); cout << "last line" << endl; cout << flush; 

¿Está bien así?

Actualización: Gracias por todas las valiosas opiniones. Resumen de las respuestas: si quiere evitar soluciones complicadas, simplemente no use endl con cout ya que vacía el buffer implícitamente. Use "\n" lugar. Puede ser interesante si produces grandes resultados.

La respuesta directa es que sí, está bien.

Mucha gente ha lanzado varias ideas sobre cómo mejorar la velocidad, pero parece que hay un gran desacuerdo sobre cuál es la más efectiva. Decidí escribir un progtwig de prueba rápido para obtener al menos una idea de qué técnicas hicieron qué.

 #include  #include  #include  #include  

Ejecuté esto en Windows después de comstackr con VC ++ 2013 (versiones x86 y x64). La salida de una ejecución (con salida redirigida a un archivo de disco) se veía así:

  Time using printf: 0.953 Time using puts: 0.567 Time using cout (synced): 0.736 Time using cout (un-synced): 0.714 Time using stringstream: 0.725 Time using endl: 20.097 Time using fill_n: 0.749 Time using write: 0.499 

Como era de esperar, los resultados varían, pero hay algunos puntos que me parecieron interesantes:

  1. printf / puts es mucho más rápido que cout cuando escribe en el dispositivo NUL
    • pero cout se mantiene bastante bien al escribir en un archivo real
  2. Un buen número de optimizaciones propuestas logran poco
    • En mi prueba, fill_n es casi tan rápido como cualquier otra cosa
  3. Con mucho, la mayor optimización es evitar endl
  4. cout.write dio el mejor tiempo (aunque probablemente no por un margen significativo)

Recientemente edité el código para forzar una llamada a printf . Anders Kaseorg tuvo la amabilidad de señalar: que g++ reconoce la secuencia específica printf("%s\n", foo); es equivalente a puts(foo); , y genera código en consecuencia (es decir, genera código para llamar a puts lugar de printf ). Mover la cadena de formato a una matriz global y pasarla a medida que la cadena de formato produce una salida idéntica, pero obliga a que se produzca a través de printf lugar de puts . Por supuesto, es posible que también optimicen esto algún día, pero al menos por ahora (g ++ 5.1) una prueba con g++ -O3 -S confirma que en realidad está llamando a printf (donde el código anterior comstackdo a una llamada a puts ) .

Enviar std::endl a la secuencia agrega una newline y vacía la transmisión. La invocación posterior de cout.flush() es superflua. Si esto se hizo al momento de cout vs. printf entonces no estabas comparando manzanas con manzanas.

De forma predeterminada, las transmisiones de salida estándar de C y C ++ están sincronizadas, de modo que la escritura en una causa una descarga de la otra, por lo que no se necesitan volcados explícitos.

Además, tenga en cuenta que la secuencia C ++ se sincroniza con la secuencia C.
Por lo tanto, hace un trabajo extra para mantenerse sincronizado.

Otra cosa a tener en cuenta es asegurarte de tirar las streams en la misma cantidad. Si enjuaga continuamente la stream en un sistema y no en el otro, eso definitivamente afectará la velocidad de las pruebas.

Antes de asumir que uno es más rápido que el otro, debe:

  • Desincronice la E / S C ++ desde CI / O (vea sync_with_stdio ()).
  • Asegúrese de que la cantidad de descargas sea comparable.

Puede mejorar aún más el rendimiento de printf al boost el tamaño del búfer para stdout :

 setvbuf (stdout, NULL, _IOFBF, 32768); // any value larger than 512 and also a // a multiple of the system i/o buffer size is an improvement 

El número de llamadas al sistema operativo para realizar E / S casi siempre es el componente más caro y el limitador de rendimiento.

Por supuesto, si la salida cout está entremezclada con stdout , las descargas de buffer derrotan el propósito de un tamaño de buffer aumentado.

Puede usar sync_with_stdio para hacer que C ++ IO sea más rápido.

 cout.sync_with_stdio(false); 

Debería mejorar su rendimiento de salida con cout .

No se preocupe por el rendimiento entre printf y cout . Si desea obtener rendimiento, separe la salida formateada de la salida no formateada.

puts("Hello World\n") es mucho más rápido que printf("%s", "Hellow World\n") . (Principalmente debido a la sobrecarga de formato). Una vez que haya aislado el formato de texto plano, puede hacer trucos como:

 const char hello[] = "Hello World\n"; cout.write(hello, sizeof(hello) - sizeof('\0')); 

Para acelerar la salida formateada, el truco es realizar todo el formateo en una cadena, luego usar la salida de bloque con la cadena (o el búfer):

 const unsigned int MAX_BUFFER_SIZE = 256; char buffer[MAX_BUFFER_SIZE]; sprintf(buffer, "%d times is a charm.\n", 5); unsigned int text_length = strlen(buffer) - sizeof('\0'); fwrite(buffer, 1, text_length, stdout); 

Para mejorar aún más el rendimiento de su progtwig, reduzca la cantidad de salida. Cuantas menos cosas entregues, más rápido será tu progtwig. Un efecto secundario será que el tamaño de tu ejecutable se reducirá también.

Bueno, no puedo pensar en ninguna razón para usar realmente cout para ser honesto. Es completamente loco tener una gran plantilla voluminosa para hacer algo tan simple que estará en cada archivo. Además, es como si estuviera diseñado para ser tan lento de escribir como sea posible y después de la millonésima vez de escribir < <<< y luego escribir el valor entre y obtener algo lik> variableName >>> por accidente, no quiero volver a hacerlo nunca más. .

Por no mencionar que si incluyes el espacio de nombres estándar, el mundo finalmente implosionará, y si no lo haces, tu carga de tipeo se vuelve aún más ridícula.

Sin embargo, tampoco me gusta imprimir mucho. Para mí, la solución es crear mi propia clase concreta y luego llamar a cualquier cosa que sea necesaria dentro de eso. Entonces puede tener io realmente simple de la manera que desee y con la implementación que desee, el formato que desee, etc. (generalmente desea que los flotadores siempre sean una forma, por ejemplo, de no formatearlos en 800 formas sin ninguna razón, por lo que en el formateo con cada llamada es una broma).

Así que todo lo que escribo es algo así como dout + “Esto es más sensato que” + cPlusPlusMethod + “de” + debugIoType + “. IMO al menos”; dout ++;

pero puedes tener lo que quieras. Con muchos archivos, sorprende lo mucho que esto también mejora el tiempo de comstackción.

Además, no hay nada de malo en mezclar C y C ++, solo se debe hacer de manera justa y si estás usando las cosas que causan los problemas con el uso de C en primer lugar, es seguro decir que la menor de tus preocupaciones es un problema al mezclar C y C ++.

La combinación de C ++ y C iomethods fue recomendada por mis libros de C ++, FYI. Estoy bastante seguro de que las funciones C pisotean el estado esperado / mantenido por C ++.