feedback
Началось все с банальной ошибки с использованием defer в функции с именованным возвращаемым значением (naked return).

Лучше не забывать: если возвращаемые параметры неименованные - данные копируются в момент вызова return, и defer ничего не меняет. Если же возвращаемые параметры именованные, то defer функция может изменять return. Может быть полезно, например, сделать именованный (err error) и в случае чего обработать и вернуть ошибку внутри defer file.Close() после основной логики, где err может быть nil. Или поступить проще и стараться явно указывать, что именно возвращаешь...

Это скорее всего очень очень corner case, касательно влияния defer на return значения, но стоит иметь в виду, чтобы в момент закрытия каких нить коннектов, файлов и не пропустить ошибку.

Интереснее про оптимизацию: в цикле defer функции будут всегда heap allocated, как минимум из за того, что непонятно итоговое количество deferнутых вызовов (рис 1). Но если defer внутри if, не в цикле и выполняется максимум 1 раз — она будет stack-allocated, что уже быстрее.
Инлайн (open coded тип) defer функции в конец кода, но с ограничениями.
- в функции не должно быть ни одного heap-deferа
- кол-во defer × кол-во return <=15: Go копирует defer вызов в каждый return путь, и если всего получится +15, то defer уползут в кучу
- Общее число defer-ов в функции <= 8: возможно, open-coded defer внутри компилятора реализован через 8 битовый bitmask, поэтому так. Не нашла сорсов, только отрывки из виктории метрикс.

Опять же, можно только посоветовать не добавлять defer для частых и мелких операций, а оставить в тех функциях, где это имеет смысл - и там и там все равно не будут инлайниться, а пропустить закрытие файлов, анлоков мьютексов достаточно легко.
Особенности использования defer в Go: оптимизация и corner cases
Link copied