tipos - Medición de la sobrecarga de manejo de excepciones en C++
try catch en c (8)
¿Cuál es la mejor manera de medir la sobrecarga / el rendimiento de la gestión de excepciones en C ++?
Por favor, da muestras de código independientes.
Me estoy enfocando en Microsoft Visual C ++ 2008 y gcc.
Necesito obtener resultados de los siguientes casos:
- Sobrecarga cuando no hay bloques de prueba / captura
- Gastos generales cuando hay bloques try / catch pero no se lanzan excepciones
- Gastos generales cuando se lanzan excepciones
Los detalles completos sobre cómo g ++ maneja las excepciones se muestran aquí . Lo describe como la arquitectura Itanium, sin embargo, las técnicas generales utilizadas son las mismas. No le indicará los gastos generales exactos en términos de tiempo, sin embargo, puede determinar cuál será la sobrecarga del código aproximado.
¿La respuesta no dependerá de qué limpieza tiene que suceder como resultado de un lanzamiento? Si se lanza un excpetion que hace que toda una carga de objetos salga del alcance de la pila, eso aumentará la sobrecarga.
En otras palabras, no estoy seguro de si hay una respuesta a la tercera pregunta que sea independiente de los detalles del código.
Aquí está el código de medición que se me ocurrió. ¿Ves algún problema con eso?
Funciona en Linux y Windows hasta ahora, compila con:
g++ exception_handling.cpp -o exception_handling [ -O2 ]
o por ejemplo Visual C ++ Express .
Para obtener el caso base ("soporte de excepción eliminado del idioma por completo"), use:
g++ exception_handling.cpp -o exception_handling [ -O2 ] -fno-exceptions -DNO_EXCEPTIONS
o configuraciones similares en MSVC.
Algunos resultados preliminares aquí . Es probable que todos sean aburridos debido a la carga variable de la máquina, pero dan alguna idea sobre la relativa sobrecarga de manejo de excepciones. (Resumen ejecutivo: ninguno o poco cuando no se arrojan excepciones, enorme cuando realmente se arrojan).
#include <stdio.h>
// Timer code
#if defined(__linux__)
#include <sys/time.h>
#include <time.h>
double time()
{
timeval tv;
gettimeofday(&tv, 0);
return 1.0 * tv.tv_sec + 0.000001 * tv.tv_usec;
}
#elif defined(_WIN32)
#include <windows.h>
double get_performance_frequency()
{
unsigned _int64 frequency;
QueryPerformanceFrequency((LARGE_INTEGER*) &frequency); // just assume it works
return double(frequency);
}
double performance_frequency = get_performance_frequency();
double time()
{
unsigned _int64 counter;
QueryPerformanceCounter((LARGE_INTEGER*) &counter);
return double(counter) / performance_frequency;
}
#else
# error time() not implemented for your platform
#endif
// How many times to repeat the whole test
const int repeats = 10;
// How many times to iterate one case
const int times = 1000000;
// Trick optimizer to not remove code
int result = 0;
// Case 1. No exception thrown nor handled.
void do_something()
{
++result;
}
void case1()
{
do_something();
}
// Case 2. No exception thrown, but handler installed
#ifndef NO_EXCEPTIONS
void do_something_else()
{
--result;
}
void case2()
{
try
{
do_something();
}
catch (int exception)
{
do_something_else();
}
}
// Case 3. Exception thrown and caught
void do_something_and_throw()
{
throw ++result;
}
void case3()
{
try
{
do_something_and_throw();
}
catch (int exception)
{
result = exception;
}
}
#endif // !NO_EXCEPTIONS
void (*tests[])() =
{
case1,
#ifndef NO_EXCEPTIONS
case2,
case3
#endif // !NO_EXCEPTIONS
};
int main()
{
#ifdef NO_EXCEPTIONS
printf("case0/n");
#else
printf("case1/tcase2/tcase3/n");
#endif
for (int repeat = 0; repeat < repeats; ++repeat)
{
for (int test = 0; test < sizeof(tests)/sizeof(tests[0]); ++test)
{
double start = time();
for (int i = 0; i < times; ++i)
tests[test]();
double end = time();
printf("%f/t", (end - start) * 1000000.0 / times);
}
printf("/n");
}
return result; // optimizer is happy - we produce a result
}
Como sugerencia: no te preocupes demasiado por los gastos generales cuando se lanzan excepciones. Las implementaciones de manejo de excepciones generalmente no hacen que el lanzamiento sea rápido y lento. Está bien, ya que esos casos son, bueno, excepcionales.
Carl
La Sección 5.4 del borrador del Informe técnico sobre el rendimiento de C ++ está completamente dedicada a la sobrecarga de excepciones.
No hay una forma realmente buena de medir eso en el código. Necesitarías usar un generador de perfiles.
Esto no le mostrará directamente cuánto tiempo se dedica al manejo de excepciones, pero con un poco de investigación descubrirá cuáles de los métodos de tiempo de ejecución se ocupan de las excepciones (por ejemplo, para VC ++ .NET es __cxx_exc [...]).
Sume sus tiempos y usted tiene la sobrecarga. En nuestro proyecto, utilizamos vTunes de Intel que funciona tanto con Visual C ++ como con gcc.
Editar: Bueno, si solo necesitas un número genérico que funcione. Pensé que tenías una aplicación real para crear un perfil donde no puedes simplemente desactivar las excepciones.
Otra nota sobre el rendimiento de manejo de excepciones: las pruebas simples no tienen en cuenta el almacenamiento en caché. El código de prueba y el código de captura son tan pequeños que todo encaja en las cachés de datos e instrucciones. Pero los compiladores pueden tratar de mover el código de captura lejos del código de prueba, lo que reduce la cantidad de código que se debe mantener en la memoria caché normalmente, mejorando así el rendimiento.
Si compara el manejo de excepciones con la verificación de valor de retorno estilo C tradicional, este efecto de almacenamiento en caché también debe tenerse en cuenta (la pregunta generalmente se ignora en las discusiones).
Carl
Kevin Frei habla sobre el costo de rendimiento del manejo de excepciones en su discurso " El costo de la gestión de excepciones de C ++ en Windows ". (En "Resumen y conclusiones", hay un elemento de la lista que dice "[El costo de rendimiento del manejo de excepciones no es] mensurable").