c++ - proyectos - optimizacion de recursos en la construccion
¿Qué estrategias ha utilizado para mejorar los tiempos de construcción en grandes proyectos? (20)
Una vez trabajé en un proyecto de C ++ que tomó aproximadamente una hora y media para una reconstrucción completa. Pequeños ciclos de edición, compilación y prueba tomaron de 5 a 10 minutos. Fue una pesadilla improductiva.
¿Cuáles son los peores tiempos de construcción que has tenido que manejar?
¿Qué estrategias ha utilizado para mejorar los tiempos de construcción en grandes proyectos?
Actualizar:
¿Cuánto crees que el lenguaje usado es el culpable del problema? Creo que C ++ es propenso a dependencias masivas en proyectos grandes, lo que a menudo significa que incluso los cambios simples en el código fuente pueden resultar en una reconstrucción masiva. ¿Qué idioma crees que resuelve mejor los problemas de dependencia de grandes proyectos?
- Compilación multi core. Muy rápido con 8 núcleos compilando en el I7.
- Vinculación incremental
- Constantes externas
- Se eliminaron los métodos en línea en las clases de C ++.
Los dos últimos nos dieron un tiempo de enlace reducido de alrededor de 12 minutos a 1-2 minutos. Tenga en cuenta que esto solo es necesario si las cosas tienen una gran visibilidad, es decir, se ven "en todas partes" y si hay muchas constantes y clases diferentes.
Aclamaciones
- Declaración hacia adelante
- idioma de los pimpl
- Encabezados precompilados
- Compilación paralela (por ejemplo, complemento MPCL para Visual Studio).
- Compilación distribuida (por ejemplo, Incredibuild para Visual Studio).
- Construcción incremental
- Dividir compilación en varios "proyectos", así que no compile todo el código si no es necesario.
[Edición posterior] 8. Compre máquinas más rápidas.
- Jugar con las banderas de optimización del compilador,
- use la opción -j4 para gmake para compilación paralela (multinúcleo o núcleo único)
- si está usando clearmake, use guiño
- Podemos sacar las banderas de depuración ... en casos extremos.
- Utiliza algunos servidores potentes.
- Minimiza tu API pública
- Minimiza las funciones en línea en tu API. (Desafortunadamente, esto también aumenta los requisitos del enlazador).
- Maximizar las declaraciones a plazo.
- Reducir el acoplamiento entre código. Por ejemplo, pase dos enteros a una función, para coordenadas, en lugar de su clase de punto personalizada que tiene su propio archivo de encabezado.
- Utilice Incredibuild. Pero a veces tiene algunos problemas.
- NO coloque el código que se exporta desde dos módulos diferentes en el archivo de encabezado SAME.
- Utilice el idioma de Apple. Mencionado antes, pero vale la pena repetirlo.
- Utilice encabezados precompilados.
- Evite C ++ / CLI (es decir, c ++ administrado). Los tiempos del enlazador también se ven afectados.
- Evite utilizar un archivo de encabezado global que incluya "todo lo demás" en su API.
- No ponga una dependencia en un archivo lib si su código realmente no lo necesita.
- Conozca la diferencia entre incluir archivos con comillas y corchetes angulares.
Cree algunos proyectos de prueba unitaria para probar bibliotecas individuales, de modo que si necesita editar clases de bajo nivel que causen una gran reconstrucción, puede usar TDD para saber si su nuevo código funciona antes de reconstruir toda la aplicación. El libro de John Lakos como lo menciona Themis tiene algunos consejos muy prácticos para reestructurar sus bibliotecas para que esto sea posible.
Cătălin Pitiș cubrió muchas cosas buenas. Otros que hacemos:
- Tener una herramienta que genere archivos .sln de Visual Studio reducidos para personas que trabajan en una sub-área específica de un proyecto general muy grande
- Cache DLLs y pdbs de cuando están construidos en CI para distribución en máquinas de desarrollador
- Para CI, asegúrese de que la máquina de enlace en particular tenga mucha memoria y unidades de gama alta
- Almacene algunos archivos costosos para regenerar en el control de código fuente, aunque se puedan crear como parte de la compilación
- Reemplace la comprobación de Visual Studio de lo que debe volver a vincular con nuestro propio script adaptado a nuestras circunstancias
En Visual Studio, puede establecer el número de proyectos para compilar a la vez. Su valor por defecto es 2, incrementando eso se reduciría algún tiempo.
Esto ayudará si no quieres meterte con el código.
En general, los grandes proyectos de C ++ en los que he trabajado que tenían tiempos de construcción lentos eran bastante complicados, con muchas interdependencias dispersas a través del código (los mismos incluyen archivos utilizados en la mayoría de los cpps, interfaces grandes en lugar de los delgados). En esos casos, el lento tiempo de construcción fue solo un síntoma del problema mayor, y un síntoma menor. La refactorización para hacer interfaces más claras y dividir el código en bibliotecas mejoró la arquitectura, así como el tiempo de construcción. Cuando creas una biblioteca, te obliga a pensar qué es una interfaz y qué no, lo que en realidad (según mi experiencia) terminará mejorando la base del código. Si no hay una razón técnica para tener que dividir el código, algunos programadores a través del curso de mantenimiento simplemente lanzarán cualquier cosa en cualquier archivo de encabezado.
Es un motivo favorito para mí, así que aunque ya aceptaste una excelente respuesta, te pido que:
En C ++, es menos el lenguaje como tal, pero el modelo de compilación exigido por el lenguaje fue excelente en los años setenta, y las bibliotecas de encabezado pesado.
Lo único que está mal en la respuesta de Cătălin Pitiș: "compre máquinas más rápidas" debería ir primero. Es la forma más fácil con el menor impacto.
Lo peor fue aproximadamente 80 minutos en una máquina de compilación antigua que ejecuta VC6 en W2K Professional. El mismo proyecto (con toneladas de código nuevo) ahora toma menos de 6 minutos en una máquina con 4 núcleos con hipervínculo, 8G RAM Win 7 x64 y discos decentes. (Una máquina similar, aproximadamente 10 ... 20% menos de potencia de procesador, con 4G RAM y Vista x86 toma el doble de tiempo)
Curiosamente, las construcciones incrementales son la mayoría de las veces más lentas que las reconstrucciones completas ahora.
Esta es la lista de cosas que hicimos para un desarrollo bajo Linux:
- Como señaló Warrior, use construcciones paralelas (make -jN)
- Utilizamos compilaciones distribuidas (actualmente icecream que es muy fácil de configurar), con esto podemos tener decenas o procesadores en un momento dado. Esto también tiene la ventaja de proporcionar las compilaciones a las máquinas más potentes y menos cargadas.
- Usamos ccache para que cuando haga una limpieza, no tenga que volver a compilar realmente las fuentes que no cambiaron, se copie desde un caché.
- Tenga en cuenta también que las compilaciones de depuración suelen ser más rápidas de compilar ya que el compilador no tiene que realizar optimizaciones.
Este libro a gran escala C ++ Software Design tiene muy buenos consejos que he usado en proyectos anteriores.
Intentamos crear clases proxy una vez.
Estas son realmente una versión simplificada de una clase que solo incluye la interfaz pública, lo que reduce la cantidad de dependencias internas que deben estar expuestas en el archivo de encabezado. Sin embargo, tuvieron un alto precio al distribuir cada clase en varios archivos que todos debían actualizarse cuando se realizaron cambios en la interfaz de la clase.
La construcción completa es de aproximadamente 2 horas. Intento evitar realizar modificaciones en las clases básicas y, como mi trabajo se basa principalmente en la implementación de estas clases básicas, solo necesito crear componentes pequeños (un par de minutos).
La mejor sugerencia es crear archivos make que realmente entiendan las dependencias y no reconstruyan automáticamente el mundo para un pequeño cambio. Pero, si una reconstrucción completa toma 90 minutos, y una reconstrucción pequeña toma de 5 a 10 minutos, es muy probable que su sistema de compilación ya lo haga.
¿Se puede hacer la construcción en paralelo? ¿Ya sea con múltiples núcleos o con múltiples servidores?
Registre los bits precompilados para piezas que realmente son estáticas y no es necesario reconstruirlas cada vez. Las herramientas / bibliotecas de terceros que se utilizan, pero no se modifican, son un buen candidato para este tratamiento.
Limite la construcción a un solo ''flujo'' si corresponde. El ''producto completo'' puede incluir elementos como una versión de depuración, o versiones de 32 y 64 bits, o puede incluir archivos de ayuda o páginas de manual que se derivan / construyen cada vez. La eliminación de componentes que no son necesarios para el desarrollo puede reducir drásticamente el tiempo de construcción.
¿La compilación también empaqueta el producto? ¿Es realmente necesario para el desarrollo y las pruebas? ¿Incorpora la construcción algunas pruebas básicas de sanidad que se pueden omitir?
Finalmente, puede volver a factorizar la base del código para que sea más modular y tenga menos dependencias. El diseño de software C ++ a gran escala es una excelente referencia para aprender a separar productos de software grandes en algo que sea más fácil de mantener y más rápido de construir.
EDITAR: Construir en un sistema de archivos local a diferencia de un sistema de archivos montado en NFS también puede acelerar dramáticamente los tiempos de compilación.
La unidad construye
Incredibuild
Puntero a la implementación
declaraciones hacia adelante
compilación de secciones "terminadas" del proyecto en archivos dll
Mi estrategia es bastante simple: no hago grandes proyectos. Todo el empuje de la computación moderna está lejos de lo gigante y lo monolítico y hacia lo pequeño y lo componente. Entonces, cuando trabajo en proyectos, divido las cosas en bibliotecas y otros componentes que pueden construirse y probarse de forma independiente, y que tienen una mínima dependencia entre sí. Una "construcción completa" en este tipo de entorno nunca se lleva a cabo, por lo que no hay problema.
Potentes máquinas de compilación y compiladores paralelos. También nos aseguramos de que la compilación completa se necesite lo menos posible. No alteramos el código para hacerlo compilar más rápido.
La eficiencia y la corrección son más importantes que la velocidad de compilación.
Un truco que a veces ayuda es incluir todo en un archivo .cpp. Ya que las inclusiones se procesan una vez por archivo, esto le puede ahorrar mucho tiempo. (La desventaja de esto es que hace que sea imposible para el compilador paralelizar la compilación)
Debería poder especificar que múltiples archivos .cpp deben compilarse en paralelo (-j con make en linux, / MP en MSVC - MSVC también tiene una opción para compilar múltiples proyectos en paralelo. Estas son opciones separadas, y no hay razón para ello ¿Por qué no deberías usar ambos?
En la misma línea, las compilaciones distribuidas (Incredibuild, por ejemplo), pueden ayudar a quitar la carga de un solo sistema.
Se supone que los discos SSD son una gran ganancia, aunque yo mismo no lo he probado (pero una compilación de C ++ toca una gran cantidad de archivos, que pueden convertirse rápidamente en un cuello de botella).
Los encabezados precompilados también pueden ayudar, cuando se usan con cuidado. (También pueden hacerte daño, si tienen que recompilarse con demasiada frecuencia).
Y, por último, es importante tratar de minimizar las dependencias en el propio código. Use el lenguaje pImpl, use declaraciones hacia adelante, mantenga el código lo más modular posible. En algunos casos, el uso de plantillas puede ayudarlo a desacoplar las clases y minimizar las dependencias. (En otros casos, las plantillas pueden ralentizar significativamente la compilación, por supuesto)
Pero sí, tienes razón, esto es mucho una cuestión de lenguaje. No conozco otro idioma que sufra el problema en este sentido. La mayoría de los idiomas tienen un sistema de módulos que les permite eliminar los archivos de encabezado, lo que implica un gran factor. C tiene archivos de encabezado, pero es un lenguaje tan simple que los tiempos de compilación aún son manejables. C ++ recibe lo peor de ambos mundos. Un gran lenguaje complejo y un terrible mecanismo de construcción primitivo que requiere una gran cantidad de código para ser analizado una y otra vez.
ccache y distcc (para proyectos C / C ++) -
ccache almacena en caché la salida, utilizando el archivo preprocesado como la "clave" para encontrar la salida. Esto es genial porque el preprocesamiento es bastante rápido y, con frecuencia, los cambios que forzan la recompilación no cambian realmente la fuente de muchos archivos. Además, realmente acelera una compilación completa. También es agradable la instancia en la que puedes tener un caché compartido entre los miembros del equipo. Esto significa que solo el primer jugador que toma el último código compila cualquier cosa.
Distcc realiza compilación distribuida a través de una red de máquinas. Esto solo es bueno si TIENE una red de máquinas para usar en la compilación. Va bien con ccache, y solo mueve la fuente preprocesada, por lo que lo único de lo que tiene que preocuparse en los sistemas del motor del compilador es que tienen el compilador correcto (no es necesario que los encabezados o todo su árbol de fuentes sean visibles) ).