Почему не все методы делают inline
Что проверяют
Знаете ли вы, зачем в Kotlin нужен inline, и понимаете ли, что это инструмент для функций с лямбдами, а не универсальный ускоритель. Хороший ответ содержит цену, а не только выгоду.
Как отвечать
inline заставляет компилятор скопировать тело функции и тела переданных ей лямбд прямо в место вызова. Основной выигрыш не в том, что исчезает вызов, а в том, что лямбда не превращается в объект и не создаётся на каждой итерации. Попутно открываются reified параметры типа и возврат из внешней функции прямо из лямбды.
Почему помечают не всё:
- у функции без лямбда-параметров выигрыша почти нет, компилятор прямо предупреждает, что эффект незначителен. Мелкие методы и так встраивают JIT в ART и оптимизатор R8 на сборке;
- тело копируется в каждое место вызова, растут размер байткода и dex, хуже работает кэш инструкций. Для большой функции с десятками вызовов это заметно;
- публичная inline-функция не может обращаться к непубличному API: её тело оказывается внутри чужого модуля;
- виртуальный вызов встроить нечем, компилятор не знает, чьё тело подставить, поэтому у открытых членов приём не работает;
- библиотека, раздающая inline-функции, теряет бинарную совместимость: старое тело уже вкомпилировано в вызывающий код;
- стектрейсы и пошаговая отладка становятся менее очевидными.
Если лямбду нужно сохранить или передать дальше, параметр помечают noinline, если вызвать из другого контекста, то crossinline.
Где спотыкаются
Объясняют inline по-плюсовому, через накладные расходы вызова, и не упоминают лямбды. В Kotlin дороже обходится аллокация объекта функции, и именно её убирает встраивание.
Помечают inline всё подряд, включая крупные функции без лямбда-параметров. Размер сборки растёт, скорость не меняется, а на код-ревью это выглядит как карго-культ.
Путают inline с value class (бывшие inline-классы). Первое про подстановку тела функции, второе про представление значения без обёртки. Совпадение в названии, механизмы разные.