language benchmarksgame alioth c++ c performance

c++ - benchmarksgame - ¿Por qué snprintf es más rápido que ostringstream o no?



benchmarksgame alioth debian (9)

Leí en alguna parte que snprintf es más rápido que ostringstream. Alguien tiene alguna experiencia con eso? Si es así, ¿por qué es más rápido?


Sí, si ejecuta la función siguiente en unos pocos millones de números con Visual C ++ 5.0, la primera versión tarda aproximadamente el doble que la segunda y produce el mismo resultado.

La compilación de loops ajustados en un .exe y la ejecución de Windows timethis something.exe'' or the Linux hora de timethis something.exe'' or the Linux algo'' es la forma en que investigo la mayoría de mis curiosidades de rendimiento. (`timethis ''está disponible en la web en algún lugar)

void Hex32Bit(unsigned int n, string &result) { #if 0 stringstream ss; ss << hex << setfill(''0'') << "0x" << setw(8) << n ; result = ss.str(); #else const size_t len = 11; char temp[len]; _snprintf(temp, len, "0x%08x", n); temp[len - 1] = ''/0''; result = temp; #endif }


Absolutamente esto es específico de la implementación.

Pero si realmente quieres saber, escribe dos pequeños programas y compáralos. Tendría que incluir el uso típico para lo que tiene en mente, los dos programas necesitarían generar la misma cadena, y usaría un generador de perfiles para mirar la información de tiempo.

Entonces lo sabrías.


Algunos chicos posiblemente te dirán que las funciones no pueden ser más rápidas que las demás, pero su implementación sí puede. Así es, creo que estaría de acuerdo.

Es poco probable que notes una diferencia en otros puntos que no sean puntos de referencia. La razón por la cual las secuencias c ++ generalmente tienden a ser más lentas es que son mucho más flexibles. La flexibilidad generalmente se produce a costa del crecimiento del tiempo o del código.

En este caso, las transmisiones en C ++ se basan en búferes de flujo. En sí mismo, las transmisiones son solo el casco que mantiene el formato y los indicadores de error en su lugar, y llama a las facetas de E / S correctas de la biblioteca estándar de C ++ (por ejemplo, num_put para imprimir números), que imprimen los valores, bien formateados, en el subyacente buffer de flujo conectado a la secuencia c ++.

Todos estos mecanismos, las facetas y los buffers, se implementan mediante funciones virtuales. Si bien no hay ninguna nota de marca , esas funciones deben implementarse para ser más lentas que las de c stdio, hecho que las hará algo más lentas que usar las funciones de c stdio normalmente (hice una evaluación comparativa que hace algún tiempo atrás con gcc / libstdc ++ y de hecho noté una desaceleración, pero que apenas notamos en el uso diario).


Reemplazamos algunos stringstreams en bucles internos con sprintf (usando búferes asignados estáticamente), y esto hizo una gran diferencia, tanto en msvc como en gcc. Me imagino que la gestión de la memoria dinámica de este código:

{ char buf[100]; int i = 100; sprintf(buf, "%d", i); // do something with buf }

es mucho más simple que

{ std::stringstream ss; int i = 100; ss << i; std::string s = ss.str(); // do something with s }

pero estoy muy contento con el rendimiento general de las cadenas de caracteres.


Un problema sería probablemente que la seguridad de tipo agregada por ostringstream conlleva gastos adicionales. Aunque no he hecho ninguna medida.


Una razón por la que sé que la familia de funciones printf es más rápida que las funciones C ++ correspondientes (cout, cin y otras secuencias) es que estas últimas realizan la verificación de tipos. Como esto generalmente involucra algunas solicitudes a operadores sobrecargados, puede llevar algo de tiempo.

De hecho, en las competiciones de programación a menudo se recomienda que use printf et al en lugar de cout / cin precisamente por esta razón.


Es bastante posible porque sprintf es parte del CRT que está escrito en el ensamblaje. El ostringstream es parte del STL, y probablemente un poco más genéricamente escrito, y tiene un código OOP / overhead para manejar.


std::ostringstream no es necesario que sea más lento, pero generalmente es más lento cuando se implementa. El sitio web de FastFormat tiene algunos puntos de referencia .

El diseño de la biblioteca estándar para transmisiones admite mucho más que snprintf . El diseño debe ser extensible e incluye métodos virtual protected que son llamados por los métodos expuestos públicamente. Esto le permite derivar de una de las clases de flujo, con la garantía de que si sobrecarga el método protect , obtendrá el comportamiento que desea. Creo que un compilador podría evitar la sobrecarga de la llamada a la función virtual , pero no conozco ningún compilador que lo haga.

Además, las operaciones de flujo a menudo usan búferes creíbles internamente; lo que implica asignaciones de memoria relativamente lentas.


Como dijo litb , las transmisiones estándar admiten muchas cosas que no siempre necesitamos. La implementación de algunas secuencias elimina esta flexibilidad que nunca se usó, consulte FAStream, por ejemplo.