Здравствуйте!
Сегодня поговорим как мы можем оптимизировать работу функций высшего порядка в Котлин, с помощью ключевого слова inline.
Inline нужен в следующих случаях:
- Мы не хотим создавать лишние объекты (см. ниже)
- Нам нужен локальный return (см. ниже)
- Нам нужна реинфекция типов (ищите тут)
Функции высшего порядка, как вы помните, это функции, которые берут другие функции в качестве параметра (или возвращают их в качестве результата). Допустим есть такой код:
Здесь всё просто, есть функция doSomething, которая что-то делает, и есть место её вызова — это функция main. Для её работы, лямбда выражения компилируются в анонимные классы, каждый раз когда используются. Так получаются накладные расходы в виде выделения дополнительной памяти при вызове таких функций. А теперь представьте если мы обращаемся к такой функции в цикле. Сколько же памяти уйдет? Поэтому не плохо бы это оптимизировать.
Пометим ключевым словом inline нашу функцию:
С виду больше ничего не изменилось, но поменяется то, как компилятор поступит с этой функцией. Он встроит нашу функцию (doSomething) и те лямбды, которые ей переданы(func) в место её вызова(main). Теперь под капотом, это будет выглядеть вот так, как будто мы написали:
На примере мы видим, что компилятор вырезал всё «лишнее», осталась только место вызова (это у нас main здесь), ну собственно сам полезный код. Так мы уменьшили накладные расходы по созданию объектов и снизили количество вызовов функций.
Давайте посмотрим ещё пример:
Под капотом, это будет выглядеть как если бы мы написали:
Мы опять видим что, код просто встраивается в место вызова. Хорошо, думаю с этим понятно, идем дальше.
👉 Из-за того, что мы так встроили функцию, у нас появились некоторые ограничения на работу с лямбдами у данной функции. Например мы лишаемся возможности их куда-то передать (1) или сохранить (2). Следующий код не сработает:
Ели же мы хотим это делать, то нам пригодится другое ключевое слово — noinline, которым помечают лямбды в составе inline функции:
Ошибки исчезли.
noinline говорит компилятору, что не нужно встраивать эту лямбду в место вызова, что, конечно, даст нам возможности что-то делать с этой лямбдой как мы хотели выше, но при этом убьет всю оптимизацию, которую дает нам inline. Потому, это имеет смысл использовать если лямбд больше чем одна.
👉 Хорошо, идем дальше. Обычно мы не можем использовать ключевое слово return из лямбды(только если с меткой), будет просто ошибка компиляции. Но если мы используем inline — то можем. Рассмотрим всё тот же пример выше, но с return:
Как вы думаете это сработает? Так как код функции встроен в место вызова (main) то return возвратит нас из main, а не из cube как можно было подумать. На экране мы увидим:
125
А "end of program" – не выведется вообще.
Такое завершение внешней функции из лямбды называется – нелокальные return (non-local return).
👉 Если нас не устраивает такое поведение return, мы можем запретить его, использовав новое ключевое слово crossinline для этой лямбды:
Как видите – ошибка, non-local return теперь не доступен здесь. А с точки зрения влияния на код, crossinline старается сохранить всю ту оптимизацию, что дал нам inline, если это возможно, однако для всех случаев это не гарантируется.
Справедливости ради, стоит отметить что noinline отключает non-local return тоже.
Рассмотрим еще пример с crossinline. Здесь мы передаем выполнение лямбды (body) в другой контекст, из которого не возможен non-local return из main. Мы могли бы использовать noinline, но в данном примере сохранить оптимизацию от inline возможно, потому пишем crossinline:
В заключение хотел бы сказать, что считается, что оптимизация от inline не такая однозначная и подходит не везде, требуется смотреть каждый отдельный случай где она применима если вы заботитесь о производительности. По этому кидаться помечать все ваши функции ключевым словом inline - плохая идея. Чрезмерное и не обдуманное использование инлайна в вашем коде ведет к сильному разрастанию байт-кода и увеличению времени компиляции.
На этом пока всё.
Спасибо что дочитали! Подписывайтесь ✅ на канал, это помогает алгоритмам продвигать меня, а вам позволит не пропустить новый материал. Для меня это действительно важно!
Ссылки: https://kotlinlang.ru/docs/inline-functions.html
Поэкспериментируйте теперь сами =)
До встречи! Будет много интересного!