lenguaje historia funciones ejemplos descargar codigos caracteristicas basicos c++ performance compilation

funciones - historia de c++



¿Por qué la compilación de C++ toma tanto tiempo? (14)

Algunas razones son:

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

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

La compilación de un archivo C ++ lleva mucho tiempo en comparación con C # y Java. Se tarda mucho más en compilar un archivo C ++ que en ejecutar un script de Python de tamaño normal. Actualmente estoy usando VC ++ pero es lo mismo con cualquier compilador. ¿Por qué es esto?

Los dos motivos que se me ocurrieron fueron cargar archivos de encabezado y ejecutar el preprocesador, pero no parece que deba explicar por qué lleva tanto tiempo.


C ++ se compila en el código de máquina. Así que tienes el preprocesador, el compilador, el optimizador y, finalmente, el ensamblador, todos los cuales tienen que ejecutarse.

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

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

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


Como ya se comentó, el compilador pasa mucho tiempo creando instancias y creando instancias de nuevo sobre las plantillas. En tal medida 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 .


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 las guardas de inclusión, el compilador todavía tiene abierto el archivo .H, y lee cada línea (y luego ignórelo).

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


Hay dos problemas que se me ocurren que podrían estar afectando la velocidad a la que se están compilando sus programas en C ++.

POSIBLE EDICIÓN # 1: COMPILACIÓN DEL LÍDER: (Esto puede o no haber sido abordado por otra respuesta o comentario). Microsoft Visual C ++ (AKA VC ++) admite encabezados precompilados, que recomiendo altamente. Cuando cree un nuevo proyecto y seleccione el tipo de programa que está realizando, aparecerá una ventana del asistente de configuración en su pantalla. Si presiona el botón "Siguiente>" en la parte inferior de la misma, 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 "Encabezado precompilado" esté marcada. (NOTA: Esta ha sido mi experiencia con las aplicaciones de la consola Win32 en C ++, pero puede que este no sea el caso con todo tipo de programas en C ++).

POSIBLE EDICIÓN # 2 - LA UBICACIÓN QUE SE COMPILA CON: Este verano, tomé un curso de programación, y tuvimos que almacenar todos nuestros proyectos en unidades de memoria flash de 8GB, ya que las computadoras en el laboratorio que usábamos se borraban todas las noches a medianoche. Lo que habría borrado todo nuestro trabajo. Si está compilando en un dispositivo de almacenamiento externo en aras de la portabilidad / seguridad / etc., Puede llevar mucho tiempo (incluso con los encabezados precompilados que mencioné anteriormente) que su programa compile, especialmente si es bastante grande. programa. Mi consejo para usted en este caso sería crear y compilar programas en el disco duro de la computadora que está utilizando, y cuando quiera / necesite dejar de trabajar en su proyecto (s) por cualquier motivo, transfiéralos a su computadora externa. dispositivo de almacenamiento, y luego haga clic en el icono "Quitar hardware con seguridad y expulsar medios", 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; Déjame saber si lo hace! :)


La desaceleración no es necesariamente lo mismo con cualquier compilador.

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

Las dos diferencias principales eran un sistema de módulos muy fuerte y una sintaxis que permitía la compilación de un solo paso.

Es posible que la velocidad de compilación no haya sido una prioridad para los desarrolladores de compiladores de C ++, pero también hay algunas complicaciones inherentes en la sintaxis de C / C ++ que hacen que sea más difícil de procesar. (No soy un experto en C, pero Walter Bright lo es, y después de crear varios compiladores comerciales de C / C ++, creó el lenguaje D. Uno de sus cambios fue imponer 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 se configuran de modo que cada archivo se compile por separado en C, por lo que si 10 archivos de origen utilizan el mismo archivo de inclusión, ese archivo se procesa 10 veces.


La mayoría de las respuestas son poco claras al mencionar que C # siempre se ejecutará más lento debido al costo de realizar acciones que en C ++ se realizan solo una vez en el momento de la compilación, este costo de rendimiento también se ve afectado debido a las dependencias de tiempo de ejecución (más cosas para cargar) para ejecutar), sin mencionar que los programas C # siempre tendrán mayor espacio de memoria, lo que resultará en que el rendimiento esté más relacionado con la capacidad del hardware disponible. Lo mismo se aplica a otros idiomas que se interpretan o dependen de una máquina virtual.


Lo que está obteniendo es que el programa se ejecuta un poquito más rápido. Esto puede ser un alivio para usted durante el desarrollo, pero puede ser muy importante una vez que el desarrollo esté completo y el programa esté siendo ejecutado por los usuarios.


Los mayores problemas son:

1) El encabezado infinito reparsing. Ya mencionado. Las mitigaciones (como #pragma una vez) por lo general solo funcionan por unidad de compilación, no por compilación.

2) El hecho de que la cadena de herramientas a menudo se divide en múltiples archivos binarios (make, preprocesador, compilador, ensamblador, archivador, impdef, enlazador y dlltool en casos extremos), todos tienen que reinicializar y recargar todo el estado todo el tiempo para cada invocación ( compilador, ensamblador) o cada par de archivos (archivador, enlazador y dlltool).

Véase también esta discusión 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 alcanzar velocidades similares para C también, si uno integra completamente la cadena de herramientas e implementa encabezados precompilados. Muchos compiladores comerciales de C hacen esto hasta cierto punto.

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


Muchas rasones

Archivos de encabezado

Cada unidad de compilación requiere cientos o incluso miles de encabezados para (1) cargar y (2) compilar. Normalmente, cada uno de ellos tiene que volver a compilarse para cada unidad de compilación, porque el preprocesador asegura que el resultado de compilar un encabezado puede variar entre cada unidad de compilación. (Se puede definir una macro en una unidad de compilación que cambia el contenido del encabezado).

Esta es probablemente la razón principal, ya que requiere que se compilen grandes cantidades de código para cada unidad de compilación, y además, cada encabezado debe compilarse varias veces (una vez por cada unidad de compilación que lo incluye).

Enlace

Una vez compilados, todos los archivos de objetos deben estar vinculados entre sí. Este es básicamente un proceso monolítico que no puede ser paralelizado, y tiene que procesar todo su proyecto.

Análisis

La sintaxis es extremadamente complicada de analizar, depende en gran medida del contexto y es muy difícil de desambiguar. Esto lleva mucho tiempo.

Plantillas

En C #, List<T> es el único tipo que se compila, sin importar cuántas instancias de List tenga en su programa. En C ++, vector<int> es un tipo completamente separado del vector<float> , y cada uno tendrá que compilarse por separado.

Agregue a esto que las plantillas forman un "sub-lenguaje" completo de Turing completo que el compilador debe interpretar, y esto puede ser ridículamente complicado. Incluso un código de metaprogramación de plantillas relativamente simple puede definir plantillas recursivas que crean docenas y docenas de instancias de plantillas. Las plantillas también pueden resultar en tipos extremadamente complejos, con nombres ridículamente largos, que agregan mucho trabajo adicional al vinculador. (Tiene que comparar muchos nombres de símbolos, y si estos nombres pueden convertirse en muchos miles de caracteres, eso 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 se debe analizar y compilar mucho más código para cada unidad de compilación. En el código C simple, un encabezado normalmente solo contiene declaraciones hacia adelante, pero muy poco código real. En C ++, no es infrecuente que casi todo el código resida en archivos de encabezado.

Mejoramiento

C ++ permite algunas optimizaciones muy dramáticas. C # o Java no permiten que las clases se eliminen por completo (tienen que estar allí para propósitos de reflexión), pero incluso un simple metaprograma de plantilla de C ++ puede generar fácilmente docenas o cientos de clases, todas las cuales están integradas y eliminadas nuevamente en la optimización fase.

Además, un programa de C ++ debe estar completamente optimizado por el compilador. El programa AC # puede confiar en el compilador JIT para realizar optimizaciones adicionales en el tiempo de carga, C ++ no obtiene tales "segundas oportunidades". Lo que genera el compilador está tan optimizado como se va a obtener.

Máquina

C ++ se compila a un código de máquina que puede ser algo más complicado que el uso del código de bytes Java o .NET (especialmente en el caso de x86). (Esto se menciona por completo solo porque se mencionó en los 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 compilación).

Conclusión

La mayoría de estos factores son compartidos por el código C, que en realidad compila de manera bastante eficiente. El paso de análisis es mucho más complicado en C ++, y puede llevar mucho más tiempo, pero el delincuente principal es probablemente las plantillas. Son útiles y hacen de C ++ un lenguaje mucho más poderoso, pero también cobran su precio en términos de velocidad de compilación.


Otra razón es el uso del preprocesador de C para localizar declaraciones. Incluso con los guardias de cabecera, .h todavía tiene que ser analizado una y otra vez, cada vez que están incluidos. Algunos compiladores admiten encabezados precompilados que pueden ayudar con esto, pero no siempre se utilizan.

Ver también: C ++ Preguntas frecuentes


Un lenguaje compilado siempre requerirá una sobrecarga inicial mayor que un lenguaje interpretado. Además, quizás no hayas estructurado muy bien tu código C ++. Por ejemplo:

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

Compila mucho más lento que:

class BigClass; class SmallClass { BigClass* m_bigClass; }


Una forma fácil de reducir el tiempo de compilación en proyectos más grandes de C ++ es hacer que un archivo. * Ppp incluya todos los archivos cpp en su proyecto y compile eso. Esto reduce el problema de explosión de cabecera a una vez. La ventaja de esto es que los errores de compilación seguirán haciendo referencia al archivo correcto.

Por ejemplo, suponga que tiene a.cpp, b.cpp y c.cpp ... cree un archivo: everything.cpp:

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

Luego compila el proyecto haciendo todo .cpp


Edificio C / C ++: lo que realmente sucede y por qué lleva tanto tiempo

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 de compilar. Para hacer las cosas más rápido, primero tenemos que entender lo que sucede cuando se compila el software C / C ++. Los pasos son aproximadamente los siguientes:

  • Configuración
  • Inicio de la herramienta de construcción
  • Comprobación de dependencia
  • Compilacion
  • Enlace

Ahora veremos cada paso con más detalle, enfocándonos en cómo se pueden hacer más rápido.

Configuración

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

Este paso ocurre relativamente rara vez. Solo se debe ejecutar cuando se cambian las configuraciones o se cambia la configuración de compilación. Aparte de cambiar los sistemas de compilación, no hay mucho que hacer para acelerar este paso.

Inicio de la herramienta de construcción

Esto es lo que sucede cuando ejecuta make o hace clic en el icono de compilación en un IDE (que generalmente es un alias para make). La herramienta de compilación se inicia y lee los archivos de configuración, así como la configuración de la compilación, que suelen ser la misma cosa.

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

Cabe señalar que la razón por la que Make es tan lento no es un error de implementación. La sintaxis de Makefiles tiene algunas peculiaridades que hacen que una implementación realmente rápida sea casi 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 construcción ha leído su configuración, tiene que determinar qué archivos han cambiado y cuáles deben ser recompilados. Los archivos de configuración contienen un gráfico acíclico dirigido que describe las dependencias de compilación. Este gráfico generalmente se construye durante el paso de configuración. El tiempo de inicio de la herramienta de compilación y el escáner de dependencia se ejecutan en cada compilación individual. Su tiempo de ejecución combinado determina el límite inferior en el ciclo de edición-compilación-depuración. Para proyectos pequeños, este tiempo suele ser de unos pocos segundos. Esto es tolerable. Hay alternativas para hacer. El más rápido de ellos es Ninja, que fue construido por los ingenieros de Google para Chromium. Si está utilizando CMake o Gyp para compilar, simplemente cambie a sus backends Ninja. No tiene que cambiar nada en los archivos de compilación en sí mismos, solo disfrute el aumento de velocidad. Sin embargo, Ninja no está empaquetado en la mayoría de las distribuciones, por lo que es posible que tenga que instalarlo usted mismo.

Compilacion

En este punto finalmente invocamos el compilador. Cortando algunas esquinas, aquí están los pasos aproximados tomados.

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

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

Su uso requiere un poco de esfuerzo, pero los beneficios son innegables. Los tiempos de construcción más rápidos llevan a desarrolladores más felices, más agilidad y, eventualmente, un mejor código.