Как стать автором
Обновить

Комментарии 15

в runtime — для платформ или движков JVM, V8, а теперь и Ruby с его JIT-компиляцией. Чтобы для них произвести инлайниг, сначала происходит JIT-компиляция, а вместо байт-кодов уже вставляется машинный код. Это тоже можно рассматривать как встраивание.


Понятно, что статья про go, и это лишь сноска на полях, но всё же. Есть ли какой-то внятный источник, почему мы это можем рассматривать, как встраивание? Для меня это не глядит корректным, но, может быть, кто-то авторитетный решил, что это корректно.

Функции foo и bar вызывают друг друга, и это можно оптимизировать

(Мыжпрограмисты) Всё таки только одна из них вызывает другую, а вторая нет.

Заголовок немного сбивает с толку. Под встраиванием все-таки обычно подразумевают композицию, как в набившем оскомину вопросе "Почему встраивание в Go - не наследование". Но статья интересная, спасибо.

Спасибо за интересную статью!

Go и других языках, которые используют LLVM-стек

Go разве на LLVM? Вроде там свой компилятор, со своей (не)хитрой кодогенерацией. Навскидку нагугленный пруф

почистить за собой стек и регистры процессора.

Почистить стек - это как? ЕМНИП его никто не чистит, иначе откуда тогда понятие "мусор со стека" и собственно само ускорение за счёт стека. То же самое про регистры.

Общая часть про "инлайнинг вообще" рисует неточнкую картину не по-мелочам (что простительно - это не фокус статьи), а по-крупному.
1. Выгода он инлайна идёт из 2 механизмов:
- а) экономия на call-ret
- б) заинлайненый код может исполняться одноверменно (перемешиваться как компилятором так и ООО-процессором) с другим кодом в вызывающей ф-ии. Для явного call-ret компилятор ничего не перемешиваем, процессор - весьма ограниченно.

Вы вовсе не упомянули про пункт "б". И явно описали худший случай для пункта а).
Скажем для С типовой случай инлайнинга это static- функции. В этом случае предусмотрен вариант передачи аргументов непосредственно через регистры, минуя стек. Что делает страшное описание вызова ф-ии на деле не таким накладным.

Кто работает с C/C++, знает, что компилятор можно попросить заинлайнить функцию. А как в Go? 

Можно только попросить, но inline не обязывает компилятор C/C++ встраивать функцию
поэтому выглядит что не сильно отличается С/С++ от Golang в этом плане
https://stackoverflow.com/questions/1759300/when-should-i-write-the-keyword-inline-for-a-function-method

Спасибо, не знал

Никита, спасибо, хорошая статья, только у меня не получилось такой большой разницы в бенчмарках для foo() и bar()

```
package main
import "testing"

var n int
var x, y = f()

func f() (int, int) {return 2, 4}

func Benchmark_Foo(b *testing.B) {
var v int
for i := 0; i < b.N; i++ {
v = foo(x, y)
}
n = v
}

func Benchmark_Bar(b *testing.B) {
var v int
for i := 0; i < b.N; i++ {
v = bar(x, y)
}
n = v
}

//go:noinline
func foo(a, b int) int {
ret := 0
for i := 0; i < 10; i++ {
ret += a
ret *= b
}
return ret
}

func bar(a, b int) int {
ret := 0
for i := 0; i < 10; i++ {
ret += a
ret *= b
}
return ret
}

```

`
goos: windows
goarch: amd64
pkg: ivanburak/20220212/inline
cpu: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
Benchmark_Foo-8 287415028 4.143 ns/op
Benchmark_Bar-8 305612419 3.926 ns/op
`
go version go1.19.1 windows/amd64

У меня ещё веселее (go1.19)

goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
Benchmark_Foo-12        261612658                4.551 ns/op
Benchmark_Bar-12        163953508                7.072 ns/op
PASS
ok      test    3.795s

WSL2, go1.19:

goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
Benchmark_Foo-12        255724063                4.657 ns/op
Benchmark_Bar-12        288291266                4.160 ns/op
PASS
ok      test    3.298s

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

Что Go может инлайнить, а что нет 

Прошу мне немного пояснить.

Есть main, который вызывает foofoo вызывает barbar вызывает runtime.Caller ...

Правильно ли я понял, что ни одна функция не заинлайнится?

Вопрос следующий. У меня есть сервис -> в нем логгер -> внутри логгера, у функции log.Error() есть runtime.Caller(1), чтобы сразу видеть где ошибка (Пишет имя файлика и строку). Получается ли так, что раз сервис создается еще в main, то вообще никакого инлайнинга у меня в итоге нет?

P.S. debug.Stack() я так понимаю с инлайном работает нормально?

так получается, что go сам инлайнит всё, что может, а мы можем принудительно ему только указать, что не нужно инлайнить //go: noinline ?

Да, все правильно

Зарегистрируйтесь на Хабре, чтобы оставить комментарий