¿Por qué no marcar todo en línea?


En primer lugar, estoy no buscando una manera de forzar al compilador a insertar la implementación de cada función.

Para reducir el nivel de respuestas equivocadas, asegúrate de entender lo que la palabra clave inline realmente significa. Aquí está una buena descripción, inline vs static vs extern.

Así que mi pregunta, ¿por qué no marcar cada definición de función inline? ie Idealmente, la única unidad de compilación sería main.cpp. O posiblemente algunos más para las funciones que no pueden ser definido en un archivo de encabezado (pimpl idiom, etc.).

La teoría detrás de esta extraña solicitud es que le daría al optimizador la máxima información con la que trabajar. Podría implementaciones de funciones en línea, por supuesto, pero también podría hacer optimización "cross-module", ya que solo hay un módulo. Hay otras ventajas?

¿Alguien ha probado esto con una aplicación real? ¿Aumentó el rendimiento? disminuir?!?

¿Cuáles son las desventajas de marcar todas las definiciones de función inline?

  • La compilación puede ser más lenta y consumir mucha más memoria.
  • Las compilaciones iterativas están rotas, toda la aplicación tendrá que ser reconstruida después de cada cambio.
  • Los tiempos de enlace podrían ser astronómicos

Todas estas desventajas solo afectan al desarrollador. ¿Cuáles son las desventajas del tiempo de ejecución?

Author: Community, 2010-10-22

11 answers

¿Realmente querías decir #include todo? Eso le daría un solo módulo y le permitiría al optimizador ver todo el programa a la vez.

En realidad, Visual C++ de Microsoft hace exactamente esto cuando se utiliza el /GL (Whole Program Optimization) switch , en realidad no compila nada hasta que el enlazador se ejecuta y tiene acceso a todo el código. Otros compiladores tienen opciones similares.

 22
Author: Ben Voigt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-22 18:35:23

Sqlite usa esta idea. Durante el desarrollo utiliza una estructura de fuente tradicional. Pero para su uso real hay un enorme archivo c (112k líneas). Lo hacen para una optimización máxima. Reclamar alrededor de 5-10% de mejora del rendimiento

Http://www.sqlite.org/amalgamation.html

 15
Author: pm100,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-22 19:11:40

Nosotros (y algunas otras compañías de juegos) lo intentamos haciendo un uber-.CPP que #includeed todos los demás; es una técnica conocida. En nuestro caso, no parecía afectar mucho el tiempo de ejecución, pero las desventajas en tiempo de compilación que mencionas resultaron ser completamente paralizantes. Con una media hora de compilación después de cada cambio, se vuelve imposible iterar de manera efectiva. (Y esto es con la aplicación dividida en más de una docena de bibliotecas diferentes.)

Intentamos hacer una configuración diferente tal que tendríamos múltiples .objs durante la depuración y luego tener el uber-CPP solo en versiones opt, pero luego se encontró con el problema de que el compilador simplemente se queda sin memoria. Para una aplicación suficientemente grande, las herramientas simplemente no están a la altura de compilar un archivo cpp de varios millones de líneas.

También probamos LTCG, y eso proporcionó un pequeño pero agradable impulso de tiempo de ejecución, en los raros casos en los que no simplemente se bloqueó durante la fase de enlace.

 9
Author: Crashworks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-22 22:43:46

Esto es semi-relacionado, pero tenga en cuenta que Visual C++ tiene la capacidad de hacer optimización entre módulos, incluyendo en línea a través de módulos. Véase http://msdn.microsoft.com/en-us/library/0zza0de8%28VS.80%29.aspx para información.

Para agregar una respuesta a su pregunta original, no creo que haya un inconveniente en el tiempo de ejecución, suponiendo que el optimizador sea lo suficientemente inteligente (de ahí por qué se agregó como una opción de optimización en Visual Studio). Solo tiene que utilizar un compilador lo suficientemente inteligente para hacerlo automáticamente, sin crear todos los problemas que mencionas. :)

 7
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-22 18:33:01

Pregunta Interesante! Sin duda tiene razón en que todas las desventajas enumeradas son específicas para el desarrollador. Sin embargo, sugeriría que un desarrollador desfavorecido es mucho menos probable que produzca un producto de calidad. Puede que no haya desventajas en tiempo de ejecución, pero imagine cuán reacio será un desarrollador a hacer pequeños cambios si cada compilación tarda horas (o incluso días) en completarse.

Miraría esto desde un ángulo de "optimización prematura": el código modular en varios archivos hace que la vida es más fácil para el programador, por lo que hay un beneficio obvio de hacer las cosas de esta manera. Solo si una aplicación específica resulta correr demasiado lento, y se puede demostrar que la inserción de todo hace una mejora medida, yo incluso considerar incomodar a los desarrolladores. Incluso entonces, sería después de que la mayoría del desarrollo se haya hecho (para que pueda medirse) y probablemente solo se haría para construcciones de producción.

 7
Author: e.James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-22 18:36:47

Ya se hace en algunos casos. Es muy similar a la idea de unity builds , y las ventajas y desventajas no son fa de lo que se descibe:

  • más potencial para que el compilador optimice
  • el tiempo de enlace básicamente desaparece (si todo está en una sola unidad de traducción, realmente no hay nada que vincular)
  • el tiempo de compilación va, bueno, de una manera u otra. Las construcciones incrementales se vuelven imposibles, como mencionaste. Por otro lado, una completa la compilación va a ser más rápida de lo que sería de otra manera (ya que cada línea de código se compila exactamente una vez. En una compilación regular, el código en los encabezados termina siendo compilado en cada unidad de traducción donde se incluye el encabezado)

Pero en los casos en los que ya tiene una gran cantidad de código de solo encabezado (por ejemplo, si utiliza una gran cantidad de Boost), podría ser una optimización muy valiosa, tanto en términos de tiempo de compilación y el rendimiento del ejecutable.

Como siempre, cuando el rendimiento está involucrado, depende. No es una mala idea, pero tampoco es universalmente aplicable.

En cuanto al tiempo de buld, básicamente tienes dos formas de optimizarlo:

  • minimizar el número de unidades de traducción (por lo que sus encabezados se incluyen en menos lugares), o
  • minimice la cantidad de código en los encabezados (de modo que el costo de incluir un encabezado en varias unidades de traducción disminuya)

El código C normalmente toma la segunda opción, más o menos hasta su extremo: casi nada aparte de las declaraciones forward y las macros se guardan en los encabezados. C++ a menudo se encuentra alrededor del medio, que es donde se obtiene el peor tiempo de compilación total posible (pero las compilaciones de PCH y/o incrementales pueden reducir un poco el tiempo de nuevo), pero yendo más lejos en la otra dirección, minimizar el número de unidades de traducción puede realmente hacer maravillas para el tiempo de compilación total.

 3
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:12

Poco beneficio En un buen compilador para una plataforma moderna, inline afectará solo a unas pocas funciones. Es solo una pista para el compilador, los compiladores modernos son bastante buenos para tomar esta decisión por sí mismos, y la sobrecarga de una llamada a una función se ha vuelto bastante pequeña (a menudo, el principal beneficio de la inserción no es reducir la sobrecarga de la llamada, sino abrir más optimizaciones).

Tiempo de compilación Sin embargo, dado que inline también cambia la semántica, tiene que #include todo en una enorme unidad de compilación. Esto normalmente aumenta significativamente el tiempo de compilación, lo cual es un asesino en grandes proyectos.

Tamaño del código
si se aleja de las plataformas de escritorio actuales y sus compiladores de alto rendimiento, las cosas cambian mucho. En este caso, el aumento del tamaño del código generado por un compilador menos inteligente será un problema, tanto que hará que el código sea significativamente más lento. En plataformas embebidas, el tamaño del código suele ser el primero restricción.

Aún así, algunos proyectos pueden y se benefician de "todo en línea". Le da el mismo efecto que la optimización del tiempo de enlace, al menos si su compilador no sigue ciegamente el inline.

 3
Author: peterchen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-13 09:12:39

Esa es más o menos la filosofía detrás de La Optimización de todo el Programa y la Generación de Código de Tiempo de Enlace (LTCG) : las oportunidades de optimización son mejores con el conocimiento global.

Desde un punto de vista práctico es una especie de dolor porque ahora cada cambio que hagas requerirá una recompilación de todo tu árbol de fuentes. En términos generales, necesita una compilación optimizada con menos frecuencia de la que necesita para realizar cambios arbitrarios.

Probé esto en la era de los Metrowerks (es bastante fácil de configurar con una compilación de estilo "Unity") y la compilación nunca terminó. Lo menciono solo para señalar que es una configuración de flujo de trabajo que es probable que imponga impuestos a la cadena de herramientas de maneras que no estaban anticipando.

 2
Author: Joe Valenzuela,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-22 18:40:13

La suposición aquí es que el compilador no puede optimizar entre funciones. Esa es una limitación de compiladores específicos y no un problema general. Usar esto como una solución general para un problema específico puede ser malo. El compilador puede muy bien simplemente inflar su programa con lo que podrían haber sido funciones reutilizables en la misma dirección de memoria (llegar a usar la caché) que se compilan en otro lugar (y perder rendimiento debido a la caché).

Grandes funciones en general costo en optimización, hay un equilibrio entre la sobrecarga de variables locales y la cantidad de código en la función. Mantener el número de variables en la función (tanto pasadas como locales y globales) dentro del número de variables desechables para la plataforma da como resultado que casi todo pueda permanecer en registros y no tenga que ser desalojado a la ram, también no se requiere un marco de pila (depende del destino), por lo que la sobrecarga de llamadas a funciones se reduce notablemente. Difícil de hacer en el mundo real aplicaciones todo el tiempo, pero la alternativa un pequeño número de grandes funciones con un montón de variables locales el código va a pasar una cantidad significativa de tiempo desalojando y cargando registros con variables a/desde ram (depende del destino).

Intente llvm puede optimizar a través de todo el programa no solo función por función. La versión 27 había alcanzado al optimizador de gcc, al menos para una prueba o dos, no hice pruebas exhaustivas de rendimiento. Y 28 está fuera, así que asumo que es mejor. Incluso con unos pocos archivos, el número de combinaciones de botones de ajuste son demasiadas para meterse con ellas. Me parece mejor no optimizar en absoluto hasta que tenga todo el programa en un archivo, luego realice su optimización, dándole al optimizador todo el programa para trabajar, básicamente lo que está tratando de hacer con inlining, pero sin el equipaje.

 2
Author: old_timer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-22 21:56:15

Supongamos que foo() y bar() ambos llaman a algunos helper(). Si todo está en una unidad de compilación, el compilador puede elegir no insertar helper(), para reducir el tamaño total de la instrucción. Esto hace que foo() haga una llamada a una función no en línea a helper().

El compilador no sabe que una mejora de nanosegundos en el tiempo de ejecución de foo() agrega 1 100/día a su línea de fondo en expectativa. No sabe que una mejora de rendimiento o degradación de cualquier cosa fuera de foo() no tiene impacto en sus resultados.

Solo usted como programador sabe estas cosas (después de un cuidadoso perfil y análisis, por supuesto). La decisión de no insertar bar() es una forma de decirle al compilador lo que usted sabe.

 1
Author: dshin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-05 02:52:32

El problema con la inserción es que desea que las funciones de alto rendimiento quepan en la caché. Podría pensar que la sobrecarga de llamadas de función es el gran éxito de rendimiento, pero en muchas arquitecturas, una falla de caché soplará la pareja empuja y sale del agua. Por ejemplo, si tiene una función grande (tal vez profunda) que necesita ser llamada muy raramente desde su ruta principal de alto rendimiento, podría hacer que su bucle principal de alto rendimiento crezca hasta el punto en que no encaje en L1 icache. Eso será reduzca la velocidad de su código, mucho más que la llamada de función ocasional.

 0
Author: nmichaels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-22 18:37:21