¿Por qué la comstackción de C ++ lleva tanto tiempo?

Comstackr un archivo C ++ lleva mucho tiempo en comparación con C # y Java. Lleva mucho más tiempo comstackr un archivo C ++ que ejecutar un script de Python de tamaño normal. Actualmente estoy usando VC ++, pero es lo mismo con cualquier comstackdor. ¿Por qué es esto?

Las dos razones por las que podía pensar eran cargar archivos de encabezado y ejecutar el preprocesador, pero eso no parece explicar por qué lleva tanto tiempo.

Muchas rasones:

  • Archivos de encabezado: cada unidad de comstackción requiere que cientos o incluso miles de encabezados sean 1: cargados y 2: comstackdos. Normalmente, cada uno de ellos debe recomstackrse para cada unidad de comstackción, ya que el preprocesador garantiza que el resultado de comstackr un encabezado puede variar entre cada unidad de comstackción. (Una macro puede definirse en una unidad de comstackción que cambia el contenido del encabezado).

    Esta es probablemente la razón principal, ya que requiere una gran cantidad de código para ser comstackdo para cada unidad de comstackción, y adicionalmente, cada encabezado debe ser comstackdo varias veces (una vez por cada unidad de comstackción que lo incluya)

  • Vinculación: Una vez comstackdos, todos los archivos de objetos deben estar vinculados entre sí. Esto es básicamente un proceso monolítico que no se puede paralelizar y tiene que procesar todo el proyecto.

  • Análisis: la syntax es extremadamente compleja de analizar, depende en gran medida del contexto y es muy difícil de desambiguar. Esto lleva mucho tiempo

  • Plantillas: en C #, List es el único tipo que se comstack, sin importar cuántas instancias de List tenga en su progtwig. En C ++, el vector es un tipo completamente separado del vector , y cada uno deberá comstackrse por separado.

    Agregue a esto que las plantillas constituyen un “sublengua” de turing completo que el comstackdor debe interpretar, y esto puede llegar a ser ridículamente complicado. Incluso el código de metaprogtwigción de plantillas relativamente simple puede definir plantillas recursivas que crean docenas y docenas de instancias de plantillas. Las plantillas también pueden dar como resultado tipos extremadamente complejos, con nombres ridículamente largos, que agregan mucho trabajo adicional al vinculador. (Tiene que comparar una gran cantidad de nombres de símbolos, y si estos nombres pueden crecer en muchos miles de caracteres, puede ser bastante caro).

    Y, por supuesto, exacerban los problemas con los archivos de encabezado, porque las plantillas generalmente tienen que definirse en encabezados, lo que significa que hay que analizar mucho más código y comstackrlo para cada unidad de comstackción. En el código simple C, un encabezado generalmente solo contiene declaraciones directas, pero muy poco código real. En C ++, no es raro que casi todo el código resida en archivos de encabezado.

  • Optimización: C ++ permite algunas optimizaciones muy dramáticas. C # o Java no permiten que las clases se eliminen por completo (tienen que estar allí para fines de reflexión), pero incluso un simple metaprogtwig de plantillas de C ++ puede generar fácilmente docenas o cientos de clases, todas las cuales están incluidas y eliminadas de nuevo en la optimización fase.

    Además, un progtwig C ++ debe ser optimizado por completo por el comstackdor. El progtwig AC # puede confiar en el comstackdor JIT para realizar optimizaciones adicionales en el tiempo de carga, C ++ no obtiene ninguna de esas “segundas oportunidades”. Lo que el comstackdor genera es lo más optimizado posible.

  • Código máquina: C ++ se comstack en código máquina, que puede ser algo más complicado que el uso de bytecode Java o .NET (especialmente en el caso de x86).
    (Esto se menciona fuera de compleción solo porque se mencionó en comentarios y similares. En la práctica, es poco probable que este paso lleve más de una pequeña fracción del tiempo total de comstackción).

La mayoría de estos factores se comparten con el código C, que en realidad se comstack de manera bastante eficiente. El paso de análisis sintáctico es mucho más complicado en C ++, y puede llevar mucho más tiempo, pero es probable que el delincuente principal sea una plantilla. Son útiles y hacen que C ++ sea un lenguaje mucho más poderoso, pero también pasan factura en términos de velocidad de comstackción.

La ralentización no es necesariamente la misma con ningún comstackdor.

No he usado Delphi o Kylix, pero en los días de MS-DOS, un progtwig de Turbo Pascal se comstackría casi instantáneamente, mientras que el progtwig equivalente de Turbo C ++ simplemente se arrastraría.

Las dos diferencias principales eran un sistema de módulos muy fuerte y una syntax que permitía la comstackción de un solo pase.

Es posible que la velocidad de comstackción no haya sido una prioridad para los desarrolladores de comstackdores de C ++, pero también hay algunas complicaciones inherentes en la syntax de C / C ++ que hacen que sea más difícil de procesar. (No soy un experto en C, pero Walter Bright sí, y después de comstackr varios comstackdores C / C ++ comerciales, creó el lenguaje D. Uno de sus cambios fue aplicar una gramática libre de contexto para hacer que el lenguaje sea más fácil de analizar .)

Además, notará que, en general, los Makefiles están configurados para que cada archivo se compile por separado en C, de modo que si 10 archivos fuente utilizan el mismo archivo include, ese archivo incluido se procesa 10 veces.

El análisis y la generación de código son bastante rápidos. El verdadero problema es abrir y cerrar archivos. Recuerde, incluso con incluir guardias, el comstackdor aún debe abrir el archivo .H y leer cada línea (y luego ignorarla).

Un amigo una vez (mientras estaba aburrido en el trabajo), tomó la aplicación de su compañía y puso todo, todos los archivos de origen y fuente, en un archivo grande. El tiempo de comstackción se redujo de 3 horas a 7 minutos.

C ++ se comstack en código máquina. Entonces tiene el preprocesador, el comstackdor, el optimizador y finalmente el ensamblador, todos los cuales tienen que ejecutarse.

Java y C # se comstackn en byte-code / IL, y la máquina virtual Java / .NET Framework se ejecuta (o comstack JIT en código máquina) antes de la ejecución.

Python es un lenguaje interpretado que también se comstack en byte-code.

Estoy seguro de que hay otras razones para esto también, pero en general, no tener que comstackr con el lenguaje de máquina nativo ahorra tiempo.

Otra razón es el uso del preprocesador C para localizar declaraciones. Incluso con protectores de encabezado, .h todavía tienen que analizarse una y otra vez, cada vez que se incluyen. Algunos comstackdores admiten encabezados precomstackdos que pueden ayudar con esto, pero no siempre se usan.

Ver también: C ++ Respuestas a preguntas frecuentes

Los mayores problemas son:

1) El repaso de encabezado infinito. Ya mencionado. Las mitigaciones (como #pragma una vez) generalmente solo funcionan por unidad de comstackción, no por comstackción.

2) El hecho de que la cadena de herramientas a menudo está separada en varios binarios (make, preprocesador, comstackdor, ensamblador, archivador, impdef, linker y dlltool en casos extremos) que todos deben reinicializar y volver a cargar todo el estado para cada invocación ( comstackdor, ensamblador) o cada par de archivos (archiver, linker y dlltool).

Ver también este debate en comp.compilers: http://compilers.iecc.com/comparch/article/03-11-078 especialmente este:

http://compilers.iecc.com/comparch/article/02-07-128

Tenga en cuenta que John, el moderador de comp.compilers parece estar de acuerdo, y que esto significa que debería ser posible lograr velocidades similares para C también, si uno integra completamente la cadena de herramientas e implementa encabezados precomstackdos. Muchos comstackdores de C comerciales hacen esto hasta cierto punto.

Tenga en cuenta que el modelo de Unix de factorizar todo a un binario por separado es una especie del peor modelo de caso para Windows (con su lenta creación de procesos). Es muy notable cuando se comparan los tiempos de comstackción de GCC entre Windows y * nix, especialmente si el sistema make / configure también llama a algunos progtwigs solo para obtener información.

Building C / C ++: lo que realmente sucede y por qué tarda tanto

Una parte relativamente grande del tiempo de desarrollo de software no se gasta en escribir, ejecutar, depurar o incluso diseñar código, sino esperar a que termine la comstackción. Para hacer las cosas rápido, primero tenemos que entender qué está sucediendo cuando se comstack el software C / C ++. Los pasos son aproximadamente los siguientes:

  • Configuración
  • Iniciar la herramienta de comstackción
  • Comprobación de dependencia
  • Comstackcion
  • Enlace

Ahora veremos cada paso con más detalle, centrándonos en cómo pueden hacerse más rápido.

Configuración

Este es el primer paso cuando se comienza a construir. Por lo general, significa ejecutar un script de configuración o CMake, Gyp, SCons o alguna otra herramienta. Esto puede llevar de un segundo a varios minutos para scripts de configuración basados ​​en Autotools muy grandes.

Este paso ocurre relativamente raramente. Solo necesita ejecutarse al cambiar configuraciones o cambiar la configuración de comstackción. A menos que se modifiquen los sistemas de comstackción, no hay mucho por hacer para acelerar este paso.

Iniciar la herramienta de comstackción

Esto es lo que sucede cuando ejecuta make o hace clic en el ícono de comstackción en un IDE (que generalmente es un alias para make). El binario de la herramienta de comstackción se inicia y lee sus archivos de configuración, así como la configuración de comstackción, que generalmente son la misma cosa.

Dependiendo de la complejidad y el tamaño de comstackción, esto puede llevar desde una fracción de segundo hasta varios segundos. Por sí mismo esto no sería tan malo. Desafortunadamente, la mayoría de los sistemas de comstackción basados ​​en make hacen que se invoca decenas a cientos de veces para cada comstackción. Usualmente esto es causado por el uso recursivo de make (que es malo).

Cabe señalar que el motivo por el que Make es tan lento no es un error de implementación. La syntax de Makefiles tiene algunas peculiaridades que hacen que una implementación realmente rápida sea prácticamente imposible. Este problema es aún más notable cuando se combina con el siguiente paso.

Comprobación de dependencia

Una vez que la herramienta de comstackción ha leído su configuración, debe determinar qué archivos han cambiado y cuáles deben volver a comstackrse. Los archivos de configuración contienen un gráfico acíclico dirigido que describe las dependencias de comstackción. Este gráfico generalmente se genera durante el paso de configuración. El tiempo de inicio de la herramienta de comstackción y el escáner de dependencia se ejecutan en cada comstackción. Su tiempo de ejecución combinado determina el límite inferior en el ciclo edit-compile-debug. Para proyectos pequeños, este tiempo suele ser de unos pocos segundos más o menos. Esto es tolerable. Hay alternativas para hacer. El más rápido de ellos es Ninja, que fue creado por los ingenieros de Google para Chromium. Si está utilizando CMake o Gyp para comstackr, simplemente cambie a sus backends Ninja. No tiene que cambiar nada en los archivos de comstackción, simplemente disfrute del aumento de velocidad. Ninja no está empaquetado en la mayoría de las distribuciones, por lo que es posible que tengas que instalarlo tú mismo.

Comstackcion

En este punto, finalmente llamamos al comstackdor. Cortando algunas esquinas, aquí están los pasos aproximados tomados.

  • La fusión incluye
  • Analizando el código
  • Generación / optimización de código

Contrariamente a la creencia popular, comstackr C ++ no es realmente tan lento. El STL es lento y la mayoría de las herramientas de comstackción utilizadas para comstackr C ++ son lentas. Sin embargo, hay herramientas más rápidas y formas de mitigar las partes lentas del lenguaje.

Usarlos requiere un poco de esfuerzo, pero los beneficios son innegables. Tiempos de construcción más rápidos conducen a desarrolladores más felices, más agilidad y, finalmente, mejor código.

Un lenguaje comstackdo siempre va a requerir una sobrecarga inicial más grande que un lenguaje interpretado. Además, quizás usted no haya estructurado su código C ++ muy bien. Por ejemplo:

 #include "BigClass.h" class SmallClass { BigClass m_bigClass; } 

Comstack mucho más lento que:

 class BigClass; class SmallClass { BigClass* m_bigClass; } 

Una manera fácil de reducir el tiempo de comstackción en proyectos más grandes de C ++ es hacer que un * .cpp incluya un archivo que incluya todos los archivos cpp en su proyecto y comstackr eso. Esto reduce el problema de explosión del encabezado a una vez. La ventaja de esto es que los errores de comstackción aún harán referencia al archivo correcto.

Por ejemplo, supongamos que tiene a.cpp, b.cpp y c.cpp .. crea un archivo: everything.cpp:

 #include "a.cpp" #include "b.cpp" #include "c.cpp" 

Luego comstack el proyecto simplemente haciendo todo.cpp

Algunas razones son:

1) La gramática de C ++ es más compleja que C # o Java y toma más tiempo analizar.

2) (Más importante) El comstackdor de C ++ produce código de máquina y realiza todas las optimizaciones durante la comstackción. C # y Java van a mitad de camino y dejan estos pasos en JIT.

La compensación que está obteniendo es que el progtwig se ejecuta un poco más rápido. Eso puede ser una gran comodidad para usted durante el desarrollo, pero podría importar mucho una vez que se complete el desarrollo, y el progtwig está siendo ejecutado por los usuarios.

La mayoría de las respuestas no están claras al mencionar que C # siempre será más lento debido al costo de realizar acciones que en C ++ se realizan solo una vez en tiempo de comstackción, este costo de rendimiento también se ve afectado por las dependencias de tiempo de ejecución (más cosas para cargar para poder para ejecutar), sin mencionar que los progtwigs C # siempre tendrán una mayor huella de memoria, todo lo cual hace que el rendimiento esté más relacionado con la capacidad del hardware disponible. Lo mismo es cierto para otros lenguajes que se interpretan o dependen de una VM.

Hay dos problemas que puedo pensar que podrían estar afectando la velocidad a la que comstackn sus progtwigs en C ++.

POSIBLE EDICIÓN # 1 – COMPILACIÓN DEL ENCABEZADO: (Esto puede o no haber sido ya abordado por otra respuesta o comentario). Microsoft Visual C ++ (AKA VC ++) admite encabezados precomstackdos, que recomiendo encarecidamente. Cuando crea un nuevo proyecto y selecciona el tipo de progtwig que está haciendo, debe aparecer una ventana de asistente de configuración en su pantalla. Si presiona el botón “Siguiente>” en la parte inferior, la ventana lo llevará a una página que tiene varias listas de características; asegúrese de que la casilla junto a la opción “Comando precomstackdo” esté marcada. (NOTA: Esta ha sido mi experiencia con las aplicaciones de consola Win32 en C ++, pero este puede no ser el caso con todo tipo de progtwigs en C ++).

POSIBLE EDICIÓN # 2 – LA UBICACIÓN SE COMPILA CON: Este verano, tomé un curso de progtwigción, y tuvimos que almacenar todos nuestros proyectos en unidades de memoria flash de 8GB, ya que las computadoras en el laboratorio que estábamos usando se borraron todas las noches a la medianoche, que habría borrado todo nuestro trabajo. Si está comstackndo en un dispositivo de almacenamiento externo por razones de portabilidad / seguridad / etc., puede llevar mucho tiempo (incluso con los encabezados precomstackdos que mencioné anteriormente) que compile su progtwig, especialmente si es bastante grande. progtwig. Mi consejo para usted en este caso sería crear y comstackr progtwigs en el disco duro de la computadora que está utilizando, y siempre que quiera / necesite dejar de trabajar en su proyecto (s) por cualquier razón, transfiéralos a su computadora externa. dispositivo de almacenamiento, y luego haga clic en el ícono “Quitar hardware con seguridad y expulsar el medio”, que debería aparecer como una pequeña unidad flash detrás de un pequeño círculo verde con una marca de verificación blanca, para desconectarlo.

Espero que esto te ayude; ¡hágamelo saber si lo hace! 🙂

Como ya se comentó, el comstackdor dedica mucho tiempo a crear instancias e instancias de nuevo las plantillas. Hasta tal punto que hay proyectos que se centran en ese elemento en particular, y reclaman una aceleración observable de 30x en algunos casos realmente favorables. Ver http://www.zapcc.com .