¿Beneficios de las funciones en línea en C ++?

254

¿Cuáles son las ventajas / desventajas de usar funciones en línea en C ++? Veo que solo aumenta el rendimiento del código que genera el compilador, pero con los compiladores optimizados de hoy, CPU rápidas, memoria enorme, etc. (no como en 1980 <donde la memoria era escasa y todo tenía que caber en 100 KB de memoria) qué ¿Qué ventajas tienen realmente hoy?

Lennie De Villiers
fuente
48
Esta es una de esas preguntas donde el conocimiento común está mal. Todos han respondido con la respuesta estándar de Comp Sci. (La inclusión ahorra costos de llamadas de función pero aumenta el tamaño del código). Basura. Proporciona un mecanismo simple para que el compilador aplique más OPTIMIZACIONES.
Martin York
37
Esta es una de esas respuestas haciéndose pasar por comentarios. Si no le gusta ninguna de las respuestas publicadas, publique su propia respuesta y vea cómo funciona.
Dave Van den Eynde
10
La base de esta pregunta es errónea. Las funciones en línea de C ++ tienen poco que ver con los compiladores en línea durante la compilación. Es desafortunado que inlinesea ​​una palabra clave de c ++ y que la alineación sea una técnica de optimización del compilador. Consulte esta pregunta " cuándo debo escribir la palabra clave inlinepara una función / método " para obtener la respuesta correcta.
deft_code
3
@JoseVega Tu enlace se destrozó: el enlace actual es exforsys.com/tutorials/c-plus-plus/inline-functions.html

Respuestas:

143

Las funciones en línea son más rápidas porque no es necesario presionar y abrir elementos de la pila como parámetros y la dirección de retorno; sin embargo, hace que su binario sea un poco más grande.

¿Hace una diferencia significativa? No es lo suficientemente notable en el hardware moderno para la mayoría. Pero puede marcar la diferencia, que es suficiente para algunas personas.

Marcar algo en línea no le garantiza que estará en línea. Es solo una sugerencia para el compilador. A veces no es posible, como cuando tiene una función virtual o cuando hay recurrencia involucrada. Y a veces el compilador simplemente elige no usarlo.

Pude ver una situación como esta haciendo una diferencia detectable:

inline int aplusb_pow2(int a, int b) {
  return (a + b)*(a + b) ;
}

for(int a = 0; a < 900000; ++a)
    for(int b = 0; b < 900000; ++b)
        aplusb_pow2(a, b);
Brian R. Bondy
fuente
26
Como sospeché, la alineación no hace ninguna diferencia con lo anterior. Compilado con gcc 4.01. Versión 1 forzada a usar la alineación: 48.318u 1.042s 5: 51.39 99.4% 0 + 0k 0 + 0io 0pf + 0w Versión 2 forzada sin alineación 348.311u 1.019s 5: 52.31 99.1% 0 + 0k 0 + 0io 0pf + 0w Esto es Un buen ejemplo donde el conocimiento común está mal.
Martin York
36
Si bien la llamada en sí es importante, esa es solo la ganancia menor que obtienes al usar en línea. La mayor ganancia es que el compilador ahora ve dónde los punteros no se alían entre sí, dónde las variables de la persona que llama terminan en la llamada y así sucesivamente. Por lo tanto, la siguiente optimización es lo que más importa.
Johannes Schaub - litb
31
Probablemente no haga una diferencia en este fragmento ya que el resultado de la función nunca se usa ni la función tiene efectos secundarios. Vemos un aumento de rendimiento medible en la inclusión en el procesamiento de imágenes.
zócalo
44
La razón para que no haya diferencias podría ser que el compilador podría estar en línea por su propia cuenta; o que el código es pequeño para que no haya problemas de captación previa de código.
einpoklum
3
@einpoklum El compilador incluso puede haber optimizado todo el ciclo debido a eso.
noɥʇʎԀʎzɐɹƆ
197

Ventajas

  • Al incluir su código donde sea necesario, su programa pasará menos tiempo en la llamada a la función y en la devolución de partes. Se supone que hace que su código vaya más rápido, incluso a medida que se hace más grande (ver más abajo). Los accesos triviales en línea podrían ser un ejemplo de línea efectiva.
  • Al marcarlo como en línea, puede poner una definición de función en un archivo de encabezado (es decir, se puede incluir en una unidad de compilación múltiple, sin que el vinculador se queje)

Desventajas

  • Puede hacer que su código sea más grande (es decir, si usa en línea para funciones no triviales). Como tal, podría provocar paginación y derrotar las optimizaciones del compilador.
  • Rompe ligeramente su encapsulación porque expone el procesamiento interno de su objeto (pero luego, cada miembro "privado" también lo haría). Esto significa que no debe usar la alineación en un patrón de ejemplo.
  • Rompe ligeramente su encapsulación 2: la alineación de C ++ se resuelve en tiempo de compilación. Lo que significa que si cambia el código de la función en línea, deberá volver a compilar todo el código que lo usa para asegurarse de que se actualizará (por el mismo motivo, evito los valores predeterminados para los parámetros de la función)
  • Cuando se usa en un encabezado, hace que su archivo de encabezado sea más grande y, por lo tanto, diluirá información interesante (como la lista de métodos de clase) con un código que al usuario no le importa (esta es la razón por la que declaro funciones en línea dentro de un clase, pero lo definirá en un encabezado después del cuerpo de la clase, y nunca dentro del cuerpo de la clase).

Magia en línea

  • El compilador puede o no alinear las funciones que marcó como en línea; También puede decidir en línea funciones no marcadas como en línea en el momento de la compilación o la vinculación.
  • Inline funciona como un copiar / pegar controlado por el compilador, que es bastante diferente de una macro preprocesadora: la macro se forzará a la fuerza, contaminará todos los espacios de nombres y códigos, no será fácilmente depurable, y se hará incluso si el compilador lo hubiera considerado ineficiente.
  • Todos los métodos de una clase definidos dentro del cuerpo de la clase en sí se consideran "en línea" (incluso si el compilador aún puede decidir no en línea)
  • No se supone que los métodos virtuales sean inlinables. Aún así, a veces, cuando el compilador puede saber con certeza el tipo de objeto (es decir, el objeto fue declarado y construido dentro del mismo cuerpo de la función), incluso una función virtual estará en línea porque el compilador sabe exactamente el tipo del objeto.
  • Los métodos / funciones de plantilla no siempre están en línea (su presencia en un encabezado no los hará en línea automáticamente).
  • El siguiente paso después de "en línea" es la metaprogramación de plantilla. Es decir, "alineando" su código en tiempo de compilación, a veces, el compilador puede deducir el resultado final de una función ... Por lo tanto, un algoritmo complejo a veces puede reducirse a un tipo de return 42 ;enunciado. Esto es para mí una línea extrema . Sucede raramente en la vida real, alarga el tiempo de compilación, no hinchará su código y lo hará más rápido. Pero al igual que el grial, no intente aplicarlo en todas partes porque la mayoría del procesamiento no se puede resolver de esta manera ... Aún así, esto es genial de todos modos ...
    :-p
paercebal
fuente
Dijiste que rompe ligeramente tu encapsulación. ¿Podría explicarlo con un ejemplo?
Destructor
66
@PravasiMeet: es C ++. Supongamos que entrega una DLL / biblioteca compartida a un cliente, que compila contra ella. La función en línea foo, usando la variable miembro X y haciendo el trabajo Y, se integrará en el código del cliente. Supongamos que necesita entregar una versión actualizada de su DLL donde cambió la variable miembro a Z, y agregar un trabajo YY además del trabajo Y. El cliente simplemente copia la DLL en su proyecto y BOOM, porque el código de foo en su binario no está el código actualizado que usted escribió ... A pesar de que el cliente no tiene acceso legal a su código privado, la inclusión lo hace bastante "público".
paercebal
@paercebal Con respecto a su penúltima viñeta, ¿puede dar un ejemplo de cuándo una plantilla de función no está en línea? Pensé que siempre estaban en línea, aunque ahora no tengo una referencia a mano (aunque una prueba simple parece confirmarlo).
Konrad Rudolph
En @KonradRudolph n4594 veo: 3.2/6: There can be more than one definition of [..] inline function with external linkage [..] non-static function template. En 5.1.5 / 6 For a generic lambda, the closure type has a public inline function call operator member template. Y en 7.1.2/2: the use of inline keyword is to declare an inline functiondonde es una sugerencia alinear el cuerpo de la función en el punto de llamada. Por lo tanto, concluyo que incluso si pueden comportarse de la misma manera, las funciones en línea y las plantillas de función siguen siendo nociones ortogonales separadas que se pueden mezclar (es decir, plantilla de función en línea)
paercebal
encapsulación está rota? ¿Cómo es eso? la encapsulación es para la programación de los chicos, no para los objetos reales en la memoria. en ese punto a nadie le importa. Incluso si distribuye una biblioteca, el compilador puede elegir en línea o no hacerlo solo. así que al final, cuando obtienes una nueva lib, solo tienes que recompilar todo lo que usa las funciones y los objetos de esa biblioteca.
FalcoGer
42

En arcaico C y C ++, inlinees como register: una sugerencia (nada más que una sugerencia) al compilador sobre una posible optimización.

En C ++ moderno, inlinele dice al enlazador que, si se encuentran múltiples definiciones (no declaraciones) en diferentes unidades de traducción, son todas iguales, y el enlazador puede conservar libremente una y descartar todas las demás.

inline es obligatorio si una función (no importa cuán compleja o "lineal") se defina en un archivo de encabezado, para permitir que múltiples fuentes la incluyan sin que el vinculador obtenga un error de "definición múltiple".

Las funciones miembro definidas dentro de una clase son "en línea" de forma predeterminada, al igual que las funciones de plantilla (en contraste con las funciones globales).

//fileA.h
inline void afunc()
{ std::cout << "this is afunc" << std::endl; }

//file1.cpp
#include "fileA.h"
void acall()
{ afunc(); }

//main.cpp
#include "fileA.h"
void acall();

int main()
{ 
   afunc(); 
   acall();
}

//output
this is afunc
this is afunc

Tenga en cuenta la inclusión de fileA.h en dos archivos .cpp, lo que da como resultado dos instancias de afunc(). El enlazador descartará uno de ellos. Si no inlinese especifica, el vinculador se quejará.

Emilio Garavaglia
fuente
16

Inline es una sugerencia para el compilador que es libre de ignorar. Es ideal para pequeños fragmentos de código.

Si su función está en línea, básicamente se inserta en el código donde se realiza la llamada a la función, en lugar de llamar a una función separada. Esto puede ayudar con la velocidad ya que no tiene que hacer la llamada real.

También ayuda a las CPU con la canalización, ya que no tienen que volver a cargar la tubería con nuevas instrucciones causadas por una llamada.

La única desventaja es el aumento del tamaño binario, pero, siempre que las funciones sean pequeñas, esto no importará demasiado.

Tiendo a dejar este tipo de decisiones a los compiladores hoy en día (bueno, las inteligentes de todos modos). Las personas que los escribieron tienden a tener un conocimiento mucho más detallado de las arquitecturas subyacentes.

revs paxdiablo
fuente
12

La función en línea es la técnica de optimización utilizada por los compiladores. Simplemente se puede anteponer una palabra clave en línea al prototipo de función para hacer una función en línea. La función en línea indica al compilador que inserte el cuerpo completo de la función siempre que esa función se haya utilizado en el código.

Ventajas: -

  1. No requiere función llamando a la sobrecarga.

  2. También ahorra sobrecarga de variables push / pop en la pila, mientras que la función llama.

  3. También ahorra gastos generales de devolución de llamada de una función.

  4. Aumenta la localidad de referencia mediante el uso de caché de instrucciones.

  5. Después del compilador en línea también puede aplicar la optimización intraprocesal si se especifica. Este es el más importante, de esta manera el compilador ahora puede enfocarse en la eliminación de código muerto, puede dar más énfasis en la predicción de ramificación, eliminación de variables de inducción, etc.

Para ver más al respecto, puede seguir este enlace http://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.html

TSS
fuente
44
1) Es una sugerencia, no una instrucción 2) Puede causar más errores de caché debido al aumento en el tamaño del código si una función de uso común está en línea mucho
Flexo
3
¿El enlace al final es tu blog personal? Si es así, debe declararlo como tal, de lo contrario parece spam.
Flexo
6

Me gustaría agregar que las funciones en línea son cruciales cuando construyes una biblioteca compartida. Sin marcar la función en línea, se exportará a la biblioteca en forma binaria. También estará presente en la tabla de símbolos, si se exporta. Por otro lado, las funciones en línea no se exportan, ni a los binarios de la biblioteca ni a la tabla de símbolos.

Puede ser crítico cuando la biblioteca está diseñada para cargarse en tiempo de ejecución. También puede afectar a las bibliotecas compatibles con binarios compatibles. En tales casos, no use en línea.

Doc
fuente
@Johnsyweb: lee mi respuesta cuidadosamente. Lo que ha dicho es cierto cuando está creando un ejecutable. ¡Pero el compilador no puede simplemente ignorar inlineal construir una biblioteca compartida!
doc
4

Durante la optimización, muchos compiladores incluirán funciones en línea incluso si no las marcó. Por lo general, solo necesita marcar las funciones como en línea si sabe algo que el compilador no sabe, ya que generalmente puede tomar la decisión correcta.

Devrin
fuente
Muchos compiladores tampoco lo harán, MSVC, por ejemplo, no lo hará a menos que se lo
pidas
4

inlinele permite colocar una definición de función en un archivo de encabezado y #includeese archivo de encabezado en varios archivos de origen sin violar la regla de una definición.

Ferruccio
fuente
3

En términos generales, en estos días, con cualquier compilador moderno preocuparse por incluir algo es una pérdida de tiempo. El compilador debería optimizar todas estas consideraciones para usted a través de su propio análisis del código y su especificación de los indicadores de optimización pasados ​​al compilador. Si le importa la velocidad, dígale al compilador que optimice la velocidad. Si le importa el espacio, dígale al compilador que optimice el espacio. Como se mencionó en otra respuesta, un compilador decente incluso se alineará automáticamente si realmente tiene sentido.

Además, como han dicho otros, usar inline no garantiza inline de nada. Si desea garantizarlo, tendrá que definir una macro en lugar de una función en línea para hacerlo.

¿Cuándo en línea y / o definir una macro para forzar la inclusión? - Solo cuando tiene un aumento demostrado y necesario en la velocidad de una sección crítica de código que se sabe que afecta el rendimiento general de la aplicación.

Jeff alto
fuente
... Si le importa el espacio, dígale al compilador que optimice para el espacio : decirle al compilador que optimice la velocidad puede generar binarios más pequeños con C ++ y C. de manera similar, decirle al compilador que optimice el espacio puede resultar en una ejecución más rápida. Ahora, estas características no siempre funcionan como se anuncia. los humanos tienen la capacidad de comprender algunos aspectos de su programa mejor que las interpretaciones generalizadas de un compilador (que también deben mantenerse usables rápidamente). La intervención humana no tiene que ser algo malo.
Justin
3

No se trata solo de rendimiento. Tanto C ++ como C se utilizan para la programación integrada, sobre el hardware. Si, por ejemplo, escribe un controlador de interrupciones, debe asegurarse de que el código se pueda ejecutar de una vez, sin que se intercambien registros adicionales y / o páginas de memoria. Eso es cuando en línea es útil. Los buenos compiladores se "alinean" ellos mismos cuando se necesita velocidad, pero "en línea" los obliga.

JM Stoorvogel
fuente
1

Se metió en el mismo problema con la incorporación de funciones en las bibliotecas. Parece que las funciones en línea no se compilan en la biblioteca. como resultado, el vinculador genera un error de "referencia indefinida", si un ejecutable quiere usar la función en línea de la biblioteca. (me pasó compilando fuente Qt con gcc 4.5.

Martin Wilde
fuente
1

¿Por qué no hacer que todas las funciones estén en línea por defecto? Porque es una compensación de ingeniería. Existen al menos dos tipos de "optimización": acelerar el programa y reducir el tamaño (huella de memoria) del programa. La alineación generalmente acelera las cosas. Elimina la sobrecarga de la llamada de función, evitando empujar y luego sacar parámetros de la pila. Sin embargo, también aumenta la huella de memoria del programa, porque cada llamada a la función ahora debe reemplazarse con el código completo de la función. Para complicar aún más las cosas, recuerde que la CPU almacena fragmentos de memoria usados ​​con frecuencia en un caché en la CPU para un acceso ultrarrápido. Si hace que la imagen de memoria del programa sea lo suficientemente grande, su programa no podrá usar la memoria caché de manera eficiente y, en el peor de los casos, la alineación podría ralentizarlo.

mehrdad zomorodiyan
fuente
1

Nuestro profesor de informática nos instó a nunca usar inline en un programa de c ++. Cuando se le preguntó por qué, nos explicó amablemente que los compiladores modernos deberían detectar cuándo usar en línea automáticamente.

Entonces, sí, el en línea puede ser una técnica de optimización que se utilizará siempre que sea posible, pero aparentemente esto es algo que ya se ha hecho para ti siempre que sea posible incorporar una función de todos modos.

HumbleWebDev
fuente
55
Tu profesor lamentablemente está completamente equivocado. inlineen C ++ tiene dos significados muy distintos: solo uno de ellos está relacionado con la optimización, y su profesor tiene razón al respecto. Sin embargo, el segundo significado de a inlinemenudo es necesario para satisfacer la regla de una definición .
Konrad Rudolph
-1

Conclusión de otra discusión aquí:

¿Hay algún inconveniente con las funciones en línea?

Aparentemente, no hay nada de malo en usar funciones en línea.

¡Pero vale la pena señalar los siguientes puntos!

  • El uso excesivo de la línea puede hacer que los programas sean más lentos Dependiendo del tamaño de una función, su alineación puede hacer que el tamaño del código aumente o disminuya. Incluir una función de acceso muy pequeña generalmente disminuirá el tamaño del código, mientras que incluir una función muy grande puede aumentar drásticamente el tamaño del código. En los procesadores modernos, el código más pequeño generalmente se ejecuta más rápido debido a un mejor uso del caché de instrucciones. - Pautas de Google

  • Los beneficios de velocidad de las funciones en línea tienden a disminuir a medida que la función crece en tamaño. En algún momento, la sobrecarga de la llamada a la función se vuelve pequeña en comparación con la ejecución del cuerpo de la función, y se pierde el beneficio - Fuente

  • Hay algunas situaciones en las que una función en línea puede no funcionar:

    • Para una función que devuelve valores; si existe una declaración de devolución.
    • Para una función que no devuelve ningún valor; si existe una instrucción loop, switch o goto.
    • Si una función es recursiva. -Fuente
  • La __inlinepalabra clave hace que una función se inserte solo si especifica la opción de optimización. Si se especifica optimizar, si se cumple o no __inlinedepende de la configuración de la opción de optimizador en línea. De manera predeterminada, la opción en línea está vigente siempre que se ejecuta el optimizador. Si especifica optimizar, también debe especificar la opción noinline si desea __inlineque se ignore la palabra clave. -Fuente

prakash
fuente
3
Sería cierto si el en línea fuera un comando y no una pista para el compilador. El compilador en realidad decide qué en línea.
Martin York
1
@LokiAstari Sé que en línea se solicita al compilador. Mi argumento es que si es una pista para el compilador, deberíamos dejar que el compilador decida qué es lo mejor. Por qué usar en línea de cualquier manera, incluso si usa en línea, sigue siendo el compilador quien tomará la decisión final. También me pregunto si mi Microsoft ha introducido _forceinline.
Krishna Oza
@krish_oza: Mi comentario aquí. Se trata de esta respuesta. La respuesta aquí es completamente incorrecta. Debido a que el compilador no tiene en cuenta la inlinepalabra clave para determinar si se debe codificar o no todos los puntos mencionados anteriormente son incorrectos. Serían verdaderas si el compilador usara la palabra clave para alinear (solo se usa para determinar el marcado de múltiples definiciones para fines de vinculación).
Martin York