c++ - una - Cuándo usar la función en línea y cuándo no usarla?
tipos de funciones en lenguaje c (13)
¡La optimización temprana es la raíz de todo mal!
Como regla general, usualmente alineo solo "getters" y "setters". Una vez que el código está funcionando y es estable, los perfiles pueden mostrar qué funciones podrían beneficiarse de la creación.
Por otro lado, la mayoría de los compiladores modernos tienen algoritmos de optimización bastante buenos y detallarán lo que debería haber escrito para usted.
Reasumiendo: escriba funciones en línea de una sola línea y preocúpese por otras más adelante.
Sé que en línea es una pista o una solicitud al compilador y se usa para evitar los gastos generales de las llamadas a funciones.
Entonces, ¿sobre qué base se puede determinar si una función es candidata o no? ¿En ese caso uno debe evitar la inflación?
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, nombres descriptivos, etc. Por ejemplo:
void IncreaseCount() { freeInstancesCnt++; }
El lector conoce inmediatamente la semántica completa del código.
Además, un método en línea tiene efectos secundarios graves cuando se mantienen 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 perder mucho tiempo de desarrollo.
Cuando un método en inline
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.
Al decidir si usar en línea, generalmente tengo en cuenta la siguiente idea: en las máquinas modernas, la latencia de la memoria puede ser un cuello de botella más grande que los cálculos en bruto. Se sabe que las funciones de entrada que se llaman a menudo aumentan el tamaño del ejecutable. Además, dicha función podría almacenarse en la memoria caché de códigos de la CPU, lo que reducirá el número de fallas de la memoria caché cuando se necesite acceder a ese código.
Por lo tanto, tiene que decidir por sí mismo: ¿aumenta o disminuye el tamaño del código de máquina generado? ¿Qué tan probable es que llamar a la función causará una falta 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, con suerte, baja.
Por lo general, uso en línea en los casos que menciono a continuación. Sin embargo, cuando esté genuinamente 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 sugerencia en línea.
- Código corto que se usa como una macro. (Por ejemplo, min () / max ())
- Rutinas de matemáticas cortas.
Decir al compilador que debe alinear una función es una optimización, y la regla de optimización más importante es que la optimización prematura es la raíz de todo mal. Siempre escriba un código claro (usando algoritmos eficientes), luego perfile su programa y solo optimice las funciones que llevan demasiado tiempo.
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 C ++ le agregarán pequeñas funciones automáticamente, y es posible que también ignoren su solicitud de alineación.
Evitar el costo de una llamada de función es solo la mitad de la historia.
hacer:
- use en
inline
lugar 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é del código) - la función es pequeña y se llama muy a menudo
no hacer:
- funciones grandes: conduce a archivos ejecutables más grandes, lo que afecta 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án vacíos, el compilador genera código para ellos
- rompiendo la compatibilidad binaria al desarrollar bibliotecas:
- en línea una función existente
- cambiar una función en línea o hacer una función en línea no en línea: la versión anterior de la biblioteca llama a la implementación anterior
Al desarrollar una biblioteca, para hacer que una clase sea extensible en el futuro, debes:
- 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 copias y el operador de asignación, a menos que la clase no se pueda copiar por valor
Recuerde que la palabra clave en inline
es una pista para el compilador: el compilador puede decidir no alinear una función y, en primer lugar, puede decidir alinear las funciones que no estaban marcadas en inline
. Generalmente evito marcar la función en 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 luego inline
un conjunto de funciones que representan un cuello de botella.
Referencias
- En línea o no en línea
- [9] Funciones en línea
- Políticas / problemas de compatibilidad binaria con C ++
- GotW # 33: en línea
- Inline Redux
- C ++ efectivo - Elemento 33: Use la alineación juiciosamente
EDITAR: Bjarne Stroustrup, el lenguaje de programación C ++:
Una función se puede definir para estar en
inline
. Por ejemplo:
inline int fac(int n)
{
return (n < 2) ? 1 : n * fac(n-1);
}
El especificador en
inline
es una sugerencia para el compilador de que debe intentar generar código para una llamada defac()
línea en lugar de establecer el código para la función una vez y luego llamar a través del mecanismo de llamada de función habitual. Un compilador inteligente puede generar la constante720
para unfac(6)
llamadafac(6)
. La posibilidad de funciones en línea mutuamente recursivas, funciones en línea que se repiten o no dependiendo de la entrada, etc., hace que sea imposible garantizar que cada llamada de una función eninline
esté realmente en línea. El grado de inteligencia de un compilador no puede legislarse, por lo que un compilador puede generar720
, otro6 * fac(5)
y otro más unfac(6)
llamada no en líneafac(6)
.Para hacer posible la alineación en ausencia de recursos 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 especificador en
inline
no afecta la semántica de una función. En particular, una función en línea aún tiene una dirección única y también tiene variablesstatic
(§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 especificador en
inline
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 es preferible al mecanismo de llamada de función habitual. No se requiere una implementación para realizar esta sustitución en línea en el punto de llamada; sin embargo, incluso si esta sustitución en línea se omite, las otras reglas para las funciones en línea definidas en 7.1.2 se seguirán respetando.
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 hacerla en línea o no es tomada solo por el compilador. Si hay más de estas muchas afirmaciones que no declararé en línea como con un compilador estúpido, esto puede llevar a la saturación del código.
He leído algunas respuestas y veo que faltan algunas cosas.
La regla que uso no es para 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 lo hacen en línea. Y nunca funciona tanto como en línea, a menos que el programador diga que haga eso.
Sé que en línea es una pista o solicitud al compilador
En realidad, en inline
es una orden para el compilador, no tiene opciones y después inline
palabra clave en inline
hace que todo el código esté en línea. Por lo tanto, nunca puede usar la palabra clave en inline
y el compilador diseñará el código más corto.
Entonces, ¿cuándo usar en 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 tenerlo como en línea porque hace que mi software sea más difícil de descifrar.
La mejor manera de averiguarlo es perfilar su programa y marcar pequeñas funciones que se llaman muchas veces y quemar a través de ciclos de CPU como en inline
. La palabra clave aquí es "pequeña": una vez que la sobrecarga de la llamada a la función es insignificante en comparación con el tiempo empleado en la función, no tiene sentido encuadrarla.
El otro uso que sugeriría es que si tiene funciones pequeñas que son llamadas en el código de rendimiento crítico con la frecuencia suficiente para hacer que una memoria caché se pierda, también debería incluirlas. Nuevamente, es algo que el perfilador debería poder decirle.
La mejor manera sería examinar y comparar las instrucciones generadas para inline y no inlined. Sin embargo, siempre es seguro omitir en inline
. Usar en inline
podría ocasionar problemas que no desea.
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 se encuentra 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,
puede leer más sobre las líneas en el faq c ++
Se debe usar el calificador de función en línea solo cuando el código de función es pequeño. Si las funciones son más grandes, se deben preferir las funciones normales, ya que el ahorro en espacio de memoria vale la pena el pequeño sacrificio en velocidad de ejecución.
inline
tiene muy poco que ver con la optimización. inline
es una instrucción para que el compilador no produzca un error si la definición dada de la función ocurre varias veces en el programa y una promesa de que la definición ocurrirá en cada traducción que se use y donde quiera que aparezca tendrá exactamente la misma definición .
Dadas las reglas anteriores, inline
es adecuado para funciones cortas cuyo cuerpo no necesita incluir dependencias adicionales sobre lo que solo una declaración necesitaría. Cada vez que se encuentra la definición, se debe analizar y se puede generar un código para su cuerpo, por lo que implica una sobrecarga del compilador sobre una función definida solo una vez en un único archivo fuente.
Un compilador puede alinear (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 ocurrir que "obviamente" no podía alinear una función que no estaba declarada en la misma unidad de traducción que la llamada, pero con el uso creciente de la optimización de tiempo de enlace, incluso ahora esto no es cierto. Igualmente cierto es el hecho de que las funciones marcadas en inline
pueden no estar en línea.