Комментарии 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
А можно прям обязать: https://stackoverflow.com/questions/8381293/how-do-i-force-gcc-to-inline-a-function
Никита, спасибо, хорошая статья, только у меня не получилось такой большой разницы в бенчмарках для 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
Что Go может инлайнить, а что нет
Прошу мне немного пояснить.
Есть main, который вызывает foo; foo вызывает bar; bar вызывает runtime.Caller ...
Правильно ли я понял, что ни одна функция не заинлайнится?
Вопрос следующий. У меня есть сервис -> в нем логгер -> внутри логгера, у функции log.Error() есть runtime.Caller(1), чтобы сразу видеть где ошибка (Пишет имя файлика и строку). Получается ли так, что раз сервис создается еще в main, то вообще никакого инлайнинга у меня в итоге нет?
P.S. debug.Stack() я так понимаю с инлайном работает нормально?
так получается, что go сам инлайнит всё, что может, а мы можем принудительно ему только указать, что не нужно инлайнить //go: noinline ?
Как Go выполняет встраивание