Cuánta huella agrega el manejo de excepciones C ++

Este problema es importante especialmente para el desarrollo integrado. El manejo de excepciones agrega cierta huella a la salida binaria generada. Por otro lado, sin excepciones, los errores deben manejarse de otra forma, lo que requiere código adicional, que eventualmente también aumenta el tamaño del binario.

Estoy interesado en tus experiencias, especialmente:

  1. ¿Cuál es la huella promedio agregada por su comstackdor para el manejo de excepciones (si tiene tales medidas)?
  2. ¿El manejo de excepciones es realmente más caro (muchos lo dicen), en términos de tamaño de salida binaria, que otras estrategias de manejo de errores?
  3. ¿Qué estrategia de manejo de errores sugerirías para el desarrollo integrado?

Por favor toma mis preguntas solo como una guía. Cualquier entrada es bienvenida.

Adición: ¿Alguien tiene un método / script / herramienta concretos que, para un objeto / ejecutable C ++ específico, mostrará el porcentaje de la huella de memoria cargada que está ocupada por el código generado por el comstackdor y las estructuras de datos dedicadas al manejo de excepciones?

Cuando se produce una excepción, habrá una sobrecarga de tiempo que dependerá de cómo implemente su manejo de excepciones. Pero, siendo anecdótico, la gravedad de un evento que debería causar una excepción tomará el mismo tiempo para manejarlo usando cualquier otro método. ¿Por qué no utilizar el método basado en el lenguaje altamente compatible para hacer frente a esos problemas?

El comstackdor GNU C ++ utiliza el modelo de costo cero de forma predeterminada, es decir, no hay sobrecarga de tiempo cuando no se producen excepciones.

Dado que la información sobre el código de manejo de excepciones y los desplazamientos de objetos locales se puede calcular una vez en tiempo de comstackción, dicha información puede mantenerse en un solo lugar asociado a cada función, pero no en cada ARI. Básicamente, eliminas la sobrecarga de excepción de cada ARI y, por lo tanto, evitas el tiempo adicional para insertarlos en la stack. Este enfoque se conoce como el modelo de costo cero del manejo de excepciones, y el almacenamiento optimizado mencionado anteriormente se conoce como la stack de sombras. – Bruce Eckel, Pensando en C ++ Volumen 2

La sobrecarga de complejidad de tamaño no es fácilmente cuantificable, pero Eckel declara un promedio de 5 y 15 por ciento. Esto dependerá del tamaño de su código de manejo de excepciones en proporción al tamaño del código de su aplicación. Si su progtwig es pequeño, las excepciones serán una gran parte del binario. Si usa un modelo de costo cero, las excepciones tomarán más espacio para eliminar el tiempo de sobrecarga, por lo tanto, si le preocupa el espacio y no el tiempo, no utilice la comstackción de costo cero.

Mi opinión es que la mayoría de los sistemas integrados tienen mucha memoria en la medida en que si su sistema tiene un comstackdor C ++ tiene suficiente espacio para incluir excepciones. La computadora PC / 104 que utiliza mi proyecto tiene varios GB de memoria secundaria, 512 MB de memoria principal, por lo tanto, no hay problema de espacio para excepciones, aunque nuestros microcontroladores están progtwigdos en C. Mi heurística es “si hay un comstackdor de C ++ convencional para usar excepciones, de lo contrario use C “.

Midiendo cosas, parte 2. Ahora tengo dos progtwigs. El primero está en C y está comstackdo con gcc -O2:

#include  #include  #define BIG 1000000 int f( int n ) { int r = 0, i = 0; for ( i = 0; i < 1000; i++ ) { r += i; if ( n == BIG - 1 ) { return -1; } } return r; } int main() { clock_t start = clock(); int i = 0, z = 0; for ( i = 0; i < BIG; i++ ) { if ( (z = f(i)) == -1 ) { break; } } double t = (double)(clock() - start) / CLOCKS_PER_SEC; printf( "%f\n", t ); printf( "%d\n", z ); } 

El segundo es C ++, con manejo de excepciones, comstackdo con g ++ -O2:

 #include  #include  #define BIG 1000000 int f( int n ) { int r = 0, i = 0; for ( i = 0; i < 1000; i++ ) { r += i; if ( n == BIG - 1 ) { throw -1; } } return r; } int main() { clock_t start = clock(); int i = 0, z = 0; for ( i = 0; i < BIG; i++ ) { try { z += f(i); } catch( ... ) { break; } } double t = (double)(clock() - start) / CLOCKS_PER_SEC; printf( "%f\n", t ); printf( "%d\n", z ); } 

Creo que estos responden a todas las críticas hechas en mi última publicación.

Resultado: los tiempos de ejecución dan a la versión C una ventaja del 0.5% sobre la versión C ++ con excepciones, no el 10% del que otros han hablado (pero no demostrado)

Estaría muy agradecido si otros pudieran intentar comstackr y ejecutar el código (solo debería tomar unos minutos) para comprobar que no cometí un error obvio y horrible en ninguna parte. ¡Esto se conoce como "el método científico"!

Trabajo en un entorno de baja latencia (Sub 300 microsegundos para mi aplicación en la “cadena” de producción) El manejo de excepciones, en mi experiencia, agrega un 5-25% de tiempo de ejecución, ¡dependiendo de la cantidad que usted haga!

En general, no nos importa la hinchazón binaria, pero si se hincha demasiado, se revienta como un loco, por lo que debe tener cuidado.

Simplemente mantenga el binario razonable (depende de su configuración).

Hago perfiles bastante extensos de mis sistemas.
Otras áreas desagradables:

Explotación florestal

Persistiendo (simplemente no hacemos esto, o si lo hacemos está en paralelo)

Supongo que dependería del hardware y el puerto de la cadena de herramientas para esa plataforma específica.

No tengo las figuras. Sin embargo, para el desarrollo más incrustado, he visto gente tirando dos cosas (para la cadena de herramientas VxWorks / GCC):

  • Plantillas
  • RTTI

El manejo de excepciones hace uso de ambos en la mayoría de los casos, por lo que hay una tendencia a descartarlo también.

En los casos en que realmente queremos acercarnos al metal, se utilizan setjmp / longjmp . Tenga en cuenta que esta no es la mejor solución posible (o muy potente) probablemente, pero eso es lo que usamos.

Puede ejecutar pruebas simples en su escritorio con dos versiones de un conjunto de evaluación comparativa con / sin manejo de excepciones y obtener los datos en los que puede confiar más.

Otra cosa sobre el desarrollo integrado: las plantillas se evitan como la peste: causan demasiada hinchazón. Las excepciones se etiquetan a lo largo de plantillas y RTTI explicadas por Johann Gerell en los comentarios (asumí que esto se entendía bien).

De nuevo, esto es solo lo que hacemos. ¿Qué pasa con todo el downvoting?

Una cosa a tener en cuenta: si está trabajando en un entorno incrustado, desea que la aplicación sea lo más pequeña posible. Microsoft C Runtime agrega bastante sobrecarga a los progtwigs. Al eliminar el tiempo de ejecución de C como requisito, pude obtener un progtwig simple para ser un archivo exe de 2 KB en lugar de un archivo de kilobyte de 70 y tantos, y eso con todas las optimizaciones para el tamaño activado.

El manejo de excepciones C ++ requiere soporte del comstackdor, que es proporcionado por el tiempo de ejecución de C. Los detalles están envueltos en misterio y no están documentados en absoluto. Al evitar las excepciones de C ++ pude cortar toda la biblioteca C runtime.

Se podría argumentar que solo se vincula dinámicamente, pero en mi caso eso no fue práctico.

Otra preocupación es que las excepciones de C ++ necesitan RTTI limitado (información del tipo de tiempo de ejecución) al menos en MSVC, lo que significa que los nombres de tipo de sus excepciones se almacenan en el archivo ejecutable. Desde el punto de vista del espacio, no es un problema, pero me parece ‘más limpio’ no tener esta información en el archivo.

En mi opinión, el manejo de excepciones no es algo generalmente aceptable para el desarrollo integrado.

Ni GCC ni Microsoft tienen manejo de excepción de “cero gastos generales”. Ambos comstackdores insertan declaraciones de prólogo y epílogo en cada función que rastrea el scope de la ejecución. Esto conduce a un aumento mensurable en el rendimiento y la huella de memoria.

La diferencia de rendimiento es algo así como un 10% en mi experiencia, que para mi área de trabajo (gráficos en tiempo real) es una gran cantidad. La sobrecarga de la memoria fue mucho menor, pero aún significativa: no recuerdo la cifra de forma directa, pero con GCC / MSVC es fácil comstackr el progtwig de ambas maneras y medir la diferencia.

He visto a algunas personas hablar sobre el manejo de excepciones como un costo “solo si lo usa”. Basado en lo que he observado, esto no es verdad. Cuando habilita el manejo de excepciones, afecta a todos los códigos, ya sea que una ruta de código pueda arrojar excepciones o no (lo cual tiene mucho sentido cuando se considera cómo funciona un comstackdor).

También me mantendría alejado de RTTI para desarrollo incrustado, aunque lo usamos en comstackciones de depuración para comprobar con cordura los resultados de downcasting.

Es fácil ver el impacto en el tamaño binario, simplemente apaga el RTTI y las excepciones en tu comstackdor. Obtendrá quejas sobre dynamic_cast <>, si lo está usando … pero generalmente evitamos usar código que depende de dynamic_cast <> en nuestros entornos.

Siempre hemos encontrado que es una victoria desactivar el manejo de excepciones y RTTI en términos de tamaño binario. He visto muchos métodos diferentes de manejo de errores a falta de manejo de excepciones. El más popular parece estar pasando los códigos de falla por la stack de llamadas. En nuestro proyecto actual usamos setjmp / longjmp, pero desaconsejaría esto en un proyecto de C ++, ya que no ejecutarán destructores al salir de un scope en muchas implementaciones. Si soy sincero, creo que esta fue una mala elección hecha por los arquitectos originales del código, especialmente teniendo en cuenta que nuestro proyecto es C ++.

Definir ‘incrustado’. En un procesador de 8 bits, ciertamente no funcionaría con excepciones (ciertamente no trabajaría con C ++ en un procesador de 8 bits). Si está trabajando con una placa tipo PC104 que es lo suficientemente potente como para haber sido el escritorio de alguien hace unos años, puede salirse con la suya. Pero tengo que preguntar: ¿por qué hay excepciones? Por lo general, en las aplicaciones integradas, cualquier cosa como una excepción que se produce es impensable: ¿por qué no se solucionó ese problema en las pruebas?

Por ejemplo, ¿esto es en un dispositivo médico? El software Sloppy en dispositivos médicos ha matado a personas. No es aceptable que ocurra algo no planificado, punto. Todos los modos de falla se deben tener en cuenta y, como dijo Joel Spolsky, las excepciones son como las declaraciones GOTO, excepto que no se sabe de dónde se llaman. Entonces, cuando maneja su excepción, ¿qué falló y en qué estado se encuentra su dispositivo? Debido a su excepción, ¿su máquina de radioterapia está completamente LLENA y está cocinando a alguien con vida (esto ha sucedido con IRL)? ¿En qué momento ocurrió la excepción en sus más de 10.000 líneas de código? Claro que tal vez puedas reducir eso a quizás 100 líneas de código pero, ¿sabes cuál es el significado de cada una de esas líneas que causan una excepción?

Sin más información, diría que NO planifique excepciones en su sistema integrado. Si los agrega, prepárese para planificar los modos de falla de CADA LÍNEA DE CÓDIGO que podría causar una excepción. Si estás fabricando un dispositivo médico, las personas mueren si no lo haces. Si estás haciendo un reproductor de DVD portátil, bueno, has hecho un reproductor de DVD portátil malo. ¿Cuál es?