¿Cuándo se debe usar en línea en Rust?

85

Rust tiene un atributo "en línea" que se puede usar en uno de esos tres sabores:

#[inline]

#[inline(always)]

#[inline(never)]

¿Cuándo deben usarse?

En la referencia de Rust, vemos una sección de atributos en línea que dice

El compilador integra automáticamente funciones basadas en heurísticas internas. La inserción incorrecta de funciones puede hacer que el programa sea más lento, por lo que debe usarse con cuidado.

En el foro interno de Rust, huon también fue conservador acerca de especificar en línea .

Pero vemos un uso considerable en la fuente de Rust, incluida la biblioteca estándar. Se agregan muchos atributos en línea a las funciones de una línea, que deberían ser fáciles de detectar y optimizar para los compiladores a través de heurísticas de acuerdo con la referencia. ¿Son esos de hecho no necesarios?

WiSaGaN
fuente

Respuestas:

69

Una limitación del compilador actual de Rust es que si no está utilizando LTO (Optimización de tiempo de enlace), nunca insertará una función que no esté marcada #[inline]en cajas. Rust utiliza un modelo de compilación independiente similar a C ++ porque la implementación de LTO de LLVM no se adapta bien a proyectos grandes. Por lo tanto, las funciones pequeñas expuestas a otras cajas deben marcarse a mano. Esta no es una gran situación y es probable que se solucione en el futuro mediante una combinación de mejoras en LTO y MIR en línea.

#[inline(never)]a veces es útil para depurar (separar un fragmento de código que no funciona como se esperaba). En teoría, se puede utilizar para realizar evaluaciones comparativas, pero suele ser una mala idea: desactivar la alineación no evita otras optimizaciones entre procedimientos, como la propagación constante. En términos de código normal, puede reducir el tamaño del código si tiene una función auxiliar de uso frecuente que solo se usa para el manejo de errores.

#[inline(always)]generalmente es una mala idea; si una función es lo suficientemente grande como para que el compilador no la alinee por defecto, es lo suficientemente grande como para que la sobrecarga de la llamada no importe (y la inserción excesiva aumenta la presión de la caché de instrucciones). Hay excepciones, pero necesita medidas de rendimiento para justificarlo. Este ejemplo es el tipo de situación en la que vale la pena considerarlo. #[inline(always)]también se puede utilizar para mejorar la -O0calidad del código, pero por lo general no vale la pena preocuparse por eso.

Eli Friedman
fuente
19
tenga en cuenta que inline(never)se utiliza en los intrínsecos de pánico para asegurarse de que el optimizador no incorpore funciones que solo se invocan en el caso de pánico.
oli_obk
4
-1 porque falta algo en el primer punto. Los elementos genéricos pueden insertarse en distintas cajas, ya que se compilan de manera eficaz cuando se crean instancias, por lo que el código necesario para la inserción está disponible fácilmente. Y esto significa que una gran cantidad de estos elementos que no están marcados, aún pueden ser empaquetados en cruz. En algunos tipos de cajas de biblioteca básicas, ¡cada artículo es genérico!
sonroja el