Inline-функции
Inline-функции, non-local return и модификаторы crossinline и noinline.
5 вопросов
JuniorТеорияЧастоЧто даёт пометка функции как inline, и что такое non-local return?
Что даёт пометка функции как inline, и что такое non-local return?
inline копирует тело функции и её lambda-аргументы в каждое место вызова, избегая создания объекта lambda. Так как lambda встроена, голый return внутри неё — non-local: он выходит из внешней функции, а не только из lambda.
Типичные ошибки
- ✗Считать, что
inline— лишь подсказка скорости и не влияет на семантику return - ✗Думать, что голый
returnв lambda всегда выходит только из lambda - ✗Встраивать огромные функции, раздувая каждое место вызова вместо ускорения
Уточняющие вопросы
- →Как выйти только из самой lambda, а не из внешней функции?
- →Когда
inlineне даёт реальной пользы и лишь раздувает размер кода?
MiddleДебаггингИногдаПочему этот inline job не компилируется, когда его lambda работает внутри Runnable?
Почему этот inline job не компилируется, когда его lambda работает внутри Runnable?
Встроенная lambda может сделать non-local return, что нелегально внутри вложенного Runnable. Пометьте параметр crossinline job: () -> Unit, чтобы запретить такой return и разрешить запуск внутри Runnable. Либо уберите inline у job.
Типичные ошибки
- ✗Считать, что любую встроенную lambda можно вызвать откуда угодно, включая вложенный объект
- ✗Хвататься за
noinline, когда lambda на самом деле нуженcrossinline - ✗Винить Java SAM-преобразование вместо правила non-local return
Уточняющие вопросы
- →Чем
crossinlineотличается отnoinlineдля параметра lambda? - →Почему non-local return нелегален именно внутри тела
Runnable?
MiddleПроизводительностьИногдаЧто inline реально экономит в рантайме по сравнению с невстроенной lambda?
Что inline реально экономит в рантайме по сравнению с невстроенной lambda?
Невстроенная lambda становится на JVM объектом функции: захватывающая выделяет экземпляр на каждый вызов, незахватывающая переиспользуется, а вызов идёт через виртуальный invoke. inline копирует тело в место вызова — ни объекта, ни invoke. Выигрыш реален в горячем цикле и тонкой функции высшего порядка и ничтожен для lambda, вызванной один раз.
Типичные ошибки
- ✗Думать, что незахватывающая lambda выделяется на каждом вызове, хотя её можно переиспользовать
- ✗Считать, что
inlineубирает лишь кадр стека, а объект lambda всё равно создаётся - ✗Встраивать большую функцию с множеством мест вызова, где размер кода дороже сэкономленного объекта
Уточняющие вопросы
- →Почему захватывающая lambda выделяется на каждый вызов, а незахватывающая — нет?
- →Когда встраивание большой функции обходится дороже, чем убранная им аллокация?
MiddleТеорияИногдаКогда нужен noinline и чем он отличается от crossinline?
Когда нужен noinline и чем он отличается от crossinline?
В inline-функции каждый lambda-параметр встраивается, поэтому объекта функции просто нет. noinline выводит один параметр из встраивания: он остаётся настоящим объектом функции, и его можно сохранить в поле, вернуть или передать в невстраиваемую функцию. crossinline оставляет lambda встроенной, но запрещает non-local return, разрешая вызывать её из вложенного контекста.
Типичные ошибки
- ✗Менять их местами — брать
noinlineтам, где вызов из вложенного объекта требуетcrossinline - ✗Думать, что
noinlineотключает встраивание всей функции, а не одного параметра - ✗Считать, что встроенную lambda можно сохранить в поле или вернуть вообще без модификатора
Уточняющие вопросы
- →Почему
noinline-lambda можно сохранить в поле, а встроенную по умолчанию — нельзя? - →Что вообще выигрывает
inline-функция, у которой все lambda помеченыnoinline?
MiddleДебаггингРедкоПочему crossinline ломает return в этой inline-функции?
Почему crossinline ломает return в этой inline-функции?
crossinline запрещает non-local return из lambda, но код на него полагается: return выходит из someFun, поэтому с crossinline не скомпилируется. Уберите crossinline — обычный inline встраивает action и разрешает non-local return.
Типичные ошибки
- ✗Воспринимать
crossinlineкак безобидную оптимизацию, а не как запрет non-local return - ✗Думать, что
returnвыходит только из lambda, поэтомуcrossinlineничего не меняет - ✗Чинить добавлением
noinline, что заодно отключает встраивание lambda
Уточняющие вопросы
- →В какой ситуации
crossinlineна этом параметре действительно понадобился бы? - →В чём разница между
crossinlineиnoinline?