Hemos estado desarrollando una gran aplicación financiera en un banco. Comenzó siendo 150k líneas de código realmente malo. Hace 1 mes, había bajado a poco más de la mitad, pero el tamaño del ejecutable aún era enorme. Esperaba que como estábamos haciendo que el código fuera más legible, pero el código de plantilla todavía generaba un gran código objeto, solo estábamos siendo más eficientes con nuestro esfuerzo.enormes ejecutables debido a los símbolos de depuración, ¿por qué?
La aplicación se divide en aproximadamente 5 objetos compartidos y una principal. Uno de los objetos compartidos más grandes era 40Mb y creció a 50 incluso mientras el código se reducía.
No me sorprendió por completo que el código comenzara a crecer, porque después de todo estamos agregando alguna funcionalidad. Pero me sorprendió que creciera un 20%. Ciertamente, nadie estuvo cerca de escribir el 20% del código, por lo que es difícil para mí imaginar cómo creció tanto. Ese módulo me resulta difícil de analizar, pero el viernes tengo nuevos puntos de datos que arrojan algo de luz.
Hay tal vez 10 feeds para servidores SOAP. El código es autogenerado, mal. Cada servicio tenía una clase analizador con exactamente el mismo código, algo así como:
#include <boost/shared_ptr.hpp>
#include <xercesstuff...>
class ParserService1 {
public:
void parse() {
try {
Service1ContentHandler*p = new Service1ContentHandler(...);
parser->setContentHandler(p);
parser->parser();
} catch (SAX ...) {
...
}
}
};
Estas clases eran completamente innecesario, una sola función funciona. Cada clase de ContentHandler se había generado automáticamente con las mismas 7 u 8 variables, que pude compartir con la herencia.
Así que estaba esperando que el tamaño del código baje cuando eliminé las clases de analizador y todo desde el código. Pero con solo 10 servicios, no esperaba que bajara de 38Mb a 36Mb. Esa es una cantidad escandalosa de símbolos.
Lo único que se me ocurre es que cada analizador incluía boost :: shared_ptr, algunas cosas del analizador Xerces, y que de alguna manera, el compilador y el enlazador están almacenando todos esos símbolos repetidamente para cada archivo. Tengo curiosidad por saberlo en cualquier caso.
Entonces, ¿alguien puede sugerir cómo iría a rastrear por qué una modificación simple como esta debería tener tanto impacto? Puedo usar nm en un módulo para mirar los símbolos que hay adentro, pero eso generará una gran cantidad dolorosa de material semi legible.
Además, cuando un colega ejecutó su código con mi nueva biblioteca, el tiempo del usuario pasó de 1m55 segundos a 1m25 segundos. El tiempo real es muy variable, porque estamos esperando en servidores SOAP lentos (en mi humilde opinión, SOAP es un reemplazo increíblemente pobre para CORBA ...) pero el tiempo de CPU es bastante estable. Hubiera esperado un pequeño impulso por reducir tanto el tamaño del código, pero en resumidas cuentas, en un servidor con memoria masiva, realmente me sorprendió que la velocidad se viera tan afectada, considerando que no cambié la arquitectura del Procesamiento XML en sí.
Voy a llevarlo mucho más lejos el martes, y con suerte obtendré más información, pero si alguien tiene alguna idea de cómo podría obtener esta gran mejora, me gustaría saberlo.
Actualización: Comprobé que, de hecho, tener símbolos de depuración en la tarea no parece cambiar el tiempo de ejecución en absoluto. Hice esto creando un archivo de encabezado que incluía muchas cosas, incluidas las dos que tuvieron el efecto aquí: impulsar punteros compartidos y algunos del analizador XML xerces. Parece que no hay ningún golpe de rendimiento en el tiempo de ejecución (lo verifiqué porque había diferencias de opinión entre dos respuestas). Sin embargo, también verifiqué que incluir archivos de encabezado crea símbolos de depuración para cada instancia, incluso si el tamaño del binario eliminado no se modifica. Por lo tanto, si incluye un archivo dado, incluso si ni siquiera lo usa, existe un número fijo de símbolos objetados en ese objeto que no se pliegan juntos en el tiempo del enlace, aunque presumiblemente sean idénticos.
Mi código es el siguiente:
#include "includetorture.h"
void f1()
{
f2(); // call the function in the next file
}
El tamaño con mi particular, incluyen archivos estaba a punto 100k por fuente de archivo. Presumiblemente, si hubiera incluido más, sería mayor. El ejecutable total con las inclusiones era ~ 600k, sin aproximadamente 9k. Verifiqué que el crecimiento es lineal con la cantidad de archivos que lo incluyen, pero el código eliminado es del mismo tamaño independientemente, como debería ser.
Claramente, me equivoqué al pensar que esta era la razón del aumento de rendimiento. Creo que he explicado eso ahora. A pesar de que no eliminé muchos códigos, simplifiqué un gran procesamiento de cadenas de caracteres xml y reduje considerablemente el camino a través del código, y esa es, presumiblemente, la razón.
gracias por la edición Magnus! – Dov
En su título menciona los símbolos de depuración, pero no en el resto de su publicación. ¿Me estoy perdiendo de algo? – Bart
@Bart El bloat se debe a todos los símbolos de depuración en los archivos ejecutables. Si quita las bibliotecas, el código es aproximadamente el 10% del tamaño. – Dov