¿Cuándo usar la función en línea y cuándo no usarla?

185

Sé que en línea es una sugerencia o solicitud para el compilador y se usa para evitar gastos generales de llamadas a funciones.

Entonces, ¿sobre qué base se puede determinar si una función es candidata para alinearse o no? ¿En qué caso se debe evitar la inserción?

Ashish
fuente
11
inlinees para el recién llegado de C ++ lo que CFLAGSes para el recién llegado de Gentoo: no, compilar con -O3 -funroll-loops -finline-functionsno hará volar su antiguo Pentium;)
Gregory Pakosz
1
Una razón para no usar en línea es que algunos depuradores no le permitirán establecer un punto de interrupción o pasar a una función en línea.
Rob deFriesse
1
Duplicado: stackoverflow.com/questions/1875947/…
Steve Jessop
55
No debe determinar si una función debe estar en línea o no. Deje que el compilador lo haga; es mejor en lo que eres (y puede en línea funciones selectivamente en función del entorno de cada llamada).
David Thornley
@DavidThornley A veces, incluso con el indicador O3 establecido, el compilador no alinea la función si la definición está en el archivo cpp. Por lo tanto, la regla de pulgar que sigo es en línea uno liners y también esas funciones sin ningún bucle.
talekeDskobeDa

Respuestas:

210

Evitar el costo de una llamada a funciones es solo la mitad de la historia.

hacer:

  • usar en inlinelugar de#define
  • las funciones muy pequeñas son buenas candidatas para inline: código más rápido y ejecutables más pequeños (más posibilidades de permanecer en el caché de código)
  • la función es pequeña y se llama con mucha frecuencia

no:

  • funciones grandes: conduce a ejecutables más grandes, lo que perjudica significativamente el rendimiento independientemente de la ejecución más rápida que resulta de la sobrecarga de llamadas
  • funciones en línea que están vinculadas a E / S
  • la función rara vez se usa
  • constructores y destructores: incluso cuando está vacío, el compilador genera código para ellos
  • romper la compatibilidad binaria al desarrollar bibliotecas:
    • en línea una función existente
    • cambiar una función en línea o hacer que una función en línea no esté en línea: la versión anterior de la biblioteca llama a la implementación anterior

al desarrollar una biblioteca, para que una clase sea extensible en el futuro, debe:

  • agregar destructor virtual no en línea incluso si el cuerpo está vacío
  • hacer que todos los constructores no estén en línea
  • escribir implementaciones no en línea del constructor de copia y el operador de asignación a menos que la clase no se pueda copiar por valor

Recuerde que la inlinepalabra clave es una pista para el compilador: el compilador puede decidir no incluir una función en línea y puede decidir incluir funciones que no se marcaron inlineen primer lugar. En general, evito marcar la función inline(aparte, tal vez cuando escribo funciones muy muy pequeñas).

En cuanto al rendimiento, el enfoque inteligente es (como siempre) perfilar la aplicación, y finalmente inlineun conjunto de funciones que representan un cuello de botella.

Referencias


EDITAR: Bjarne Stroustrup, El lenguaje de programación C ++:

Una función se puede definir para ser inline. Por ejemplo:

inline int fac(int n)
{
  return (n < 2) ? 1 : n * fac(n-1);
}

El inlineespecificador es una pista para el compilador de que debería intentar generar código para una llamada en fac()línea en lugar de establecer el código para la función una vez y luego llamar a través del mecanismo habitual de llamada de función. Un compilador inteligente puede generar la constante 720para una llamada fac(6). La posibilidad de funciones en línea recursivas mutuamente, funciones en línea que se repiten o no dependen de la entrada, etc., hace que sea imposible garantizar que cada llamada de una inlinefunción esté realmente en línea. El grado de inteligencia de un compilador no se puede legislar, por lo que un compilador puede generar 720, otro 6 * fac(5), y otro más una llamada no en línea fac(6).

Para hacer posible la alineación en ausencia de facilidades de compilación y enlace inusualmente inteligentes, la definición, y no solo la declaración, de una función en línea debe estar dentro del alcance (§9.2). Un inlineespecificador no afecta la semántica de una función. En particular, una función en línea todavía tiene una dirección única y también tiene staticvariables (§7.1.2) de una función en línea.

EDIT2: ISO-IEC 14882-1998, 7.1.2 Especificadores de funciones

Una declaración de función (8.3.5, 9.3, 11.4) con un inlineespecificador declara una función en línea. El especificador en línea indica a la implementación que la sustitución en línea del cuerpo de la función en el punto de llamada debe preferirse al mecanismo habitual de llamada a la función. No se requiere una implementación para realizar esta sustitución en línea en el punto de llamada; sin embargo, incluso si se omite esta sustitución en línea, se seguirán respetando las otras reglas para las funciones en línea definidas en 7.1.2.

revs Gregory Pakosz
fuente
34
inlinees mucho más que una pista para el compilador. Cambia las reglas del lenguaje sobre múltiples definiciones. Además, tener datos estáticos no es una razón de hierro fundido para evitar incluir una función. La implementación está obligada a asignar un único objeto estático para cada función estática, ya sea que la función se declare inlineo no. Las clases siguen siendo extensibles si tienen constructores en línea y destructores virtuales. Y el destructor de llaves vacías es la única función virtual que a veces es una buena idea dejar en línea.
CB Bailey
2
Es una pista en el sentido de que la función no necesariamente termina en línea (pero el inglés no es mi lengua materna). Sobre las estadísticas en las funciones marcadas inline, el resultado es que la función no se alinea: usted paga el precio de la llamada y también cada unidad de traducción que incluye y llama a la función obtiene su propia copia del código y las variables estáticas. La razón para no incluir constructores y destructores cuando se desarrolla una biblioteca es la compatibilidad binaria con futuras versiones de su biblioteca
Gregory Pakosz
14
Es incorrecto llamarlo una "pista para el compilador". En realidad, las no inlinefunciones pueden estar en línea si el compilador lo desea. Y las inlinefunciones no estarán alineadas si el compilador decide no incluirlas. Como dijo Charles Bailey, cambia las reglas del lenguaje. En lugar de pensar en ello como una pista de optimización, es más exacto considerarlo como un concepto completamente diferente. La inlinepalabra clave le dice al compilador que permita múltiples definiciones, y nada más. La optimización "en línea" se puede aplicar a casi cualquier función, esté o no marcada inline.
jalf
26
Es solo que, cuando Stroustrup escribe "el especificador en línea es una pista para el compilador", me sorprende que me culpen por citarlo. De todos modos, he pasado suficiente tiempo haciendo todo lo posible para respaldar esta respuesta con la mayor cantidad posible de referencias
Gregory Pakosz
2
@GregoryPakosz: Pero no todos usamos inlinepara obtener la función en línea. A veces queremos los otros beneficios, como evitar ODR.
Carreras de ligereza en órbita
57

inlinetiene muy poco que ver con la optimización. inlinees una instrucción para el compilador de no producir un error si la definición de la función dada ocurre varias veces en el programa y una promesa de que la definición se producirá en cada traducción que se use y en todas partes que aparezca tendrá exactamente la misma definición.

Dadas las reglas anteriores, inlinees adecuado para funciones cortas cuyo cuerpo no necesita incluir dependencias adicionales sobre lo que necesitaría una declaración. Cada vez que se encuentra la definición, se debe analizar y se puede generar código para su cuerpo, por lo que implica una sobrecarga del compilador sobre una función definida solo una vez en un solo archivo fuente.

Un compilador puede en línea (es decir, reemplazar una llamada a la función con código que realiza esa acción de esa función) cualquier llamada de función que elija. Solía ​​ser el caso de que "obviamente" no podía alinear una función que no se declaró en la misma unidad de traducción que la llamada, pero con el uso creciente de la optimización del tiempo de enlace, incluso esto no es cierto ahora. Igualmente cierto es el hecho de que las funciones marcadas inlinepueden no estar en línea.

CB Bailey
fuente
Tengo la sensación de que es más una feliz coincidencia que una característica intencional de C ++. La idea es muy similar a las variables globales 'estáticas' de C. Sin embargo, es una respuesta muy interesante. Ojalá hubieran usado una palabra clave como 'interna' para indicar la vinculación interna.
Rehno Lindeque
+1. @Rehno: No estoy muy seguro de lo que estás diciendo. ¿Qué tiene que ver el enlace con la inlinepalabra clave? ¿Y qué es una feliz coincidencia?
jalf
@jalf: Al leer mi comentario en retrospectiva, me doy cuenta de que es bastante vago y no está tan bien pensado. La definición de la misma función en varios archivos da como resultado un error de enlazador que puede contrarrestarse declarando la función 'estática'. Sin embargo, 'en línea' le permite hacer lo mismo con diferencias sutiles que en realidad no obtienen enlaces internos como lo hace 'estático'. Sospecho que esto en realidad es más una coincidencia porque los implementadores / diseñadores del lenguaje se dieron cuenta de que necesitarán hacer algo especial con las funciones declaradas en los archivos de encabezado y que se transfieren a "en línea".
Rehno Lindeque
44
No estoy seguro de por qué su comentario obtuvo tantos votos positivos, ya que el rendimiento es la razón dominante para usar en línea.
gast128
10

Decirle al compilador que alinee una función es una optimización, y la regla más importante de la optimización es que la optimización prematura es la raíz de todo mal. Siempre escriba código claro (usando algoritmos eficientes), luego perfile su programa y solo optimice las funciones que están tardando demasiado.

Si encuentra que una función en particular es muy corta y simple, y se llama decenas de miles de veces en un circuito interno cerrado, podría ser un buen candidato.

Sin embargo, es posible que se sorprenda: muchos compiladores de C ++ incorporarán automáticamente pequeñas funciones para usted, e incluso podrían ignorar su solicitud.

dmazzoni
fuente
De hecho, tengo mis sospechas de que ciertos compiladores ignoran muy sigilosamente 'inline' por completo y solo responden a '__inline' o '__force_inline'. ¡Supongo que esto es para disuadir el abuso!
Rehno Lindeque
No suele ser el caso. inline es solo una pista, pero es una pista que la mayoría de los compiladores toman en serio. Puede configurar el compilador para que emita el lenguaje ensamblador junto con el código objeto ( /FAcsen Visual Studio, -sen GCC) para ver exactamente lo que hace. En mi experiencia, ambos compiladores sopesan bastante la palabra clave en línea.
Crashworks
1
Es interesante, porque en mi experiencia ni g ++ ni VC pesan la inlinepalabra clave en absoluto. Es decir, si ve que la función está en línea y elimina el inlineespecificador, seguirá estando en línea. Si tienes ejemplos específicos de lo contrario, ¡compártelos!
Pavel Minaev
44
¿Cómo inlineobstaculiza la palabra clave "código claro"? La palabra clave en "optimización prematura" es prematura , no optimización. Decir que debes * evitar activamente las optimizaciones es solo basura. El punto de esa cita es que debe evitar las optimizaciones que pueden no ser necesarias y tener efectos secundarios dañinos en el código (como hacerlo menos mantenible). No veo cómo la inlinepalabra clave hará que el código sea menos mantenible, o cómo puede ser perjudicial agregarlo a una función.
jalf
3
jalf, a veces incluir una función hará que su código sea más lento, no más rápido. Un ejemplo es cuando se llama a la función desde varios lugares diferentes en su código; Si la función no está en línea, aún podría estar en la memoria caché de instrucciones cuando se llama desde un lugar diferente, y el predictor de ramificación ya podría haberse calentado. Hay algunos patrones que siempre mejoran la eficiencia, por lo que nunca está de más usarlos. La alineación no es una de ellas. Por lo general, no tiene ningún efecto en el rendimiento, a veces ayuda y a veces duele. Respaldo mi consejo: primero el perfil, luego en línea.
dmazzoni
5

La mejor manera de averiguarlo es crear un perfil de su programa y marcar pequeñas funciones que se llaman muchas veces y grabar ciclos de CPU de esa manera inline. La palabra clave aquí es "pequeña": una vez que la sobrecarga de la llamada de función es insignificante en comparación con el tiempo dedicado a la función, no tiene sentido alinearlos.

El otro uso que sugeriría es que si tiene funciones pequeñas que se llaman en el código crítico de rendimiento con la frecuencia suficiente para hacer que una memoria caché pierda relevancia, probablemente también debería incluirlas. De nuevo, es algo que el perfilador debería poder decirte.

Timo Geusch
fuente
4

¡La optimización temprana es la raíz de todo mal!

Como regla general, generalmente solo incluyo "getters" y "setters". Una vez que el código funciona y es estable, la creación de perfiles puede mostrar qué funciones podrían beneficiarse de la incorporación.

Por otro lado, la mayoría de los compiladores modernos tienen algoritmos de optimización bastante buenos, y alinearán lo que debería haber alineado para usted.

Reasumir: escriba funciones de línea única y se preocupe por las demás más adelante.

Kornel Kisielewicz
fuente
2

Las funciones en línea pueden mejorar el rendimiento de su código al eliminar la necesidad de insertar argumentos en la pila. si la función en cuestión está en una parte crítica de su código, debe tomar la decisión en línea, no en línea, en la parte de optimización de su proyecto,

puedes leer más sobre las líneas en las preguntas frecuentes de c ++

solo
fuente
1

A menudo uso las funciones en línea no como una optimización, sino para hacer que el código sea más legible. A veces, el código en sí es más corto y más fácil de entender que los comentarios, los nombres descriptivos, etc. Por ejemplo:

void IncreaseCount() { freeInstancesCnt++; }

El lector conoce de inmediato la semántica completa del código.

danatel
fuente
0

Generalmente sigo una regla de pulgar donde hago una función con 3-4 declaraciones simples como en línea. Pero es bueno recordar que es solo una pista para el compilador. La última llamada para que esté en línea o no la toma el compilador únicamente. Si hay más de estas muchas declaraciones, no declararé en línea, ya que con un compilador estúpido, puede provocar una acumulación de código.

Naveen
fuente
0

La mejor manera sería examinar y comparar las instrucciones generadas para en línea y no en línea. Sin embargo, siempre es seguro omitirlo inline. Usar inlinepodría generar problemas que no quieres.

wallyk
fuente
0

Cuando decido si usar en línea, generalmente tengo en cuenta la siguiente idea: en las máquinas modernas, la latencia de memoria puede ser un cuello de botella más grande que los cálculos en bruto. Se sabe que las funciones de línea que se llaman a menudo aumentan el tamaño del ejecutable. Además, dicha función podría almacenarse en el caché de código de la CPU, lo que disminuirá el número de errores de caché cuando se necesite acceder a ese código.

Por lo tanto, tiene que decidir por usted mismo: ¿la alineación aumenta o disminuye el tamaño del código máquina generado? ¿Qué tan probable es que llamar a la función cause una pérdida de caché? Si está salpicado en todo el código, entonces diría que la probabilidad es alta. Si está restringido a un solo ciclo cerrado, entonces la probabilidad es baja.

Normalmente uso inlining en los casos que enumero a continuación. Sin embargo, cuando está realmente preocupado por el rendimiento, la creación de perfiles es esencial. Además, es posible que desee comprobar si el compilador realmente toma la pista.

  • Rutinas cortas que se llaman en un ciclo cerrado.
  • Accesores muy básicos (get / set) y funciones de envoltura.
  • El código de plantilla en los archivos de encabezado desafortunadamente obtiene automáticamente la pista en línea.
  • Código corto que se usa como una macro. (Por ejemplo, min () / max ())
  • Cortas rutinas de matemáticas.
Rehno Lindeque
fuente
0

Además, un método en línea tiene graves efectos secundarios al mantener proyectos grandes. Cuando se cambia el código en línea, el compilador reconstruirá automáticamente todos los archivos que lo utilizan (es un buen compilador). Esto podría desperdiciar mucho tiempo de desarrollo.

Cuando un inlinemétodo se transfiere a un archivo fuente y ya no está en línea, se debe reconstruir todo el proyecto (al menos esta ha sido mi experiencia). Y también cuando los métodos se convierten en línea.

Thomas Matthews
fuente
1
Esa es una cuestión diferente. Obtiene el problema de reconstrucción para el código que se coloca en un archivo de encabezado. Tanto si se está marcada inlineo no, no tiene importancia (que no sea sin la inlinepalabra clave, obtendrá errores de enlace - pero la inlinepalabra clave no es el problema que causa reconstrucciones excesivas.
JALF
Sin embargo, cambiar un método en línea causará compilaciones excesivas versus cambiar un método no en línea en un archivo de origen.
Thomas Matthews
0

Uno debería usar el calificador de función en línea solo cuando el código de la función es pequeño. Si las funciones son más grandes, debería preferir las funciones normales ya que el ahorro en espacio de memoria vale el sacrificio comparativamente pequeño en la velocidad de ejecución.

nitish
fuente
0

Cuando piense que su código es lo suficientemente pequeño como para usarlo en línea y recuerde que la función en línea duplica su código y péguelo donde se llama la función, por lo que puede ser lo suficientemente bueno como para aumentar su tiempo de ejecución pero también un mayor consumo de memoria. No puede usar la función en línea cuando está usando un bucle / variable estática / recursiva / switch / goto / función virtual. Virtual significa esperar hasta el tiempo de ejecución e inline significa durante la compilación para que no puedan usarse simultáneamente.

Sachin Pathak
fuente
-2

He leído algunas respuestas y veo que faltan algunas cosas.

La regla que uso es no usar en línea, a menos que quiera que esté en línea. Parece tonto, ahora explicación.

Los compiladores son lo suficientemente inteligentes y las funciones cortas siempre están en línea. Y nunca hace una función larga como en línea, a menos que el programador diga que lo haga.

Sé que en línea es una pista o solicitud para compilar

En realidad inlinees un pedido para el compilador, no tiene opciones y después de que la inlinepalabra clave hace que todo el código esté en línea. Por lo tanto, nunca puede usar inlinepalabras clave y el compilador diseñará el código más corto.

Entonces, ¿cuándo usarlo inline?

Para usar si quieres tener algún código en línea. Solo conozco un ejemplo, porque lo uso en una sola situación. Es autenticación de usuario.

Por ejemplo tengo esta función:

inline bool ValidUser(const std::string& username, const std::string& password)
{
    //here it is quite long function
}

No importa cuán grande sea esta función, quiero tenerla en línea porque hace que mi software sea más difícil de descifrar.

ST3
fuente
2
en línea sigue siendo una pista. El compilador puede fallar en línea si considera que su función está demasiado hinchada.
EsPete
Uno dice que en línea es una orden ... el otro dice que es una pista ¿Alguien confirmaría su afirmación para que podamos determinar cuál es la verdadera?
@ user2918461 apoyo la declaración en línea es solo una pista. Esto ha sido respaldado por muchos sitios web y libros
WARhead