Когда в Rust следует использовать inline?

86

В Rust есть "встроенный" атрибут, который можно использовать в одном из этих трех вариантов:

#[inline]

#[inline(always)]

#[inline(never)]

Когда их следует использовать?

В справочнике Rust мы видим раздел встроенных атрибутов, в котором говорится

Компилятор автоматически встраивает функции на основе внутренней эвристики. Неправильное встраивание функций может фактически замедлить работу программы, поэтому использовать ее следует с осторожностью.

На форуме Rust internals huon также консервативно подходил к указанию inline .

Но мы видим значительное использование в исходном коде Rust, включая стандартную библиотеку. К однострочным функциям добавляется множество встроенных атрибутов, которые компиляторам должно быть легко обнаружить и оптимизировать с помощью эвристики в соответствии со ссылкой. На самом деле они не нужны?

WiSaGaN
источник

Ответы:

70

Одно из ограничений текущего компилятора Rust заключается в том, что если вы не используете LTO (оптимизацию времени компоновки), он никогда не будет встраивать функцию, не отмеченную #[inline]в ящиках. Rust использует отдельную модель компиляции, аналогичную C ++, поскольку реализация LTO LLVM плохо масштабируется для больших проектов. Поэтому небольшие функции, доступные для других ящиков, необходимо маркировать вручную. Это не самая лучшая ситуация, и она, вероятно, будет исправлена ​​в будущем с помощью некоторой комбинации улучшений встраивания LTO и MIR.

#[inline(never)]иногда полезно для отладки (отделение части кода, который не работает должным образом). Теоретически его можно использовать для тестирования производительности, но обычно это плохая идея: отключение встраивания не предотвращает другие межпроцедурные оптимизации, такие как распространение констант. Что касается обычного кода, он может уменьшить размер кода, если у вас есть часто используемая вспомогательная функция, которая используется только для обработки ошибок.

#[inline(always)]вообще плохая идея; если функция достаточно велика, чтобы компилятор не встраивал ее по умолчанию, она достаточно велика, чтобы накладные расходы на вызов не имели значения (а чрезмерное встраивание увеличивает давление в кеш-память инструкций). Бывают исключения, но вам нужны измерения производительности, чтобы это обосновать. Этот пример заслуживает рассмотрения. #[inline(always)]также можно использовать для улучшения -O0качества кода, но обычно об этом не стоит беспокоиться.

Эли Фридман
источник
19
обратите внимание, что inline(never)это используется во встроенных функциях паники, чтобы убедиться, что оптимизатор не встраивает функции, которые вызываются только в случае паники.
oli_obk 06
4
-1, потому что в первом пункте чего-то не хватает. Общие элементы могут быть встроены в крейты, поскольку они эффективно компилируются при создании экземпляров, поэтому код, необходимый для встраивания, легко доступен. А это значит, что огромное количество таких предметов, которые не промаркированы, все же могут быть встроены в кросс-ящик. В некоторых типах ящиков базовой библиотеки каждый элемент является универсальным!
bluss