🧩 Go Квиз: оцените отношение времени g()/f()
#Golang
Идея проста: какая из функций будет «дороже» и во сколько раз?
- f делает три умножения на итерацию плюс запись в массив. Это «дешёвая» арифметика с линейным проходом по памяти.
- g на каждой итерации создаёт срез a[:] и вызывает doNothing. Если компилятор встраивает пустую функцию и выкидывает бесполезный срез, тело цикла почти исчезает. Если запретить инлайнинг, вы получите N вызовов функции и накладные расходы на создание среза на каждой итерации.
Чего ожидать?
С инлайнингом и DCE: g() часто быстрее f(), потому что почти ничего не делает, тогда как f() реально пишет в память. Отношение g()/f() < 1.
Без инлайнинга: g() резко замедляется из-за стоимости вызова функции и построения среза на каждой итерации. Отношение g()/f() ≫ 1, иногда на порядок.
Вывод: результат зависит от оптимизаций компилятора. «Смысл» цикла, который не производит наблюдаемых эффектов, Go охотно выкидывает.
Как проверить у себя
Что запомнить
- Микробенчи в Go чувствительны к инлайнингу и dead-code elimination.
- Запись в память удерживает цикл «реальным», тогда как пустые вызовы и срезы могут исчезнуть.
- Измеряйте обе конфигурации: с оптимизациями и без, чтобы понять истинную стоимость.
Если коротко:
- В реальном билде с оптимизациями g()/f() обычно < 1.
- С запретом инлайнинга g()/f() обычно ≫ 1 из-за накладных расходов вызова.
#Golang
Идея проста: какая из функций будет «дороже» и во сколько раз?
const N = 1 << 12
func f(){
for a, i := [N]int{}, 0; i < len(a); i++ {
a[i] = i * i * i * i
}
}
func doNothing(a []int) {}
func g(){
for a, i := [N]int{}, 0; i < len(a); i++ {
doNothing(a[:])
}
}
- f делает три умножения на итерацию плюс запись в массив. Это «дешёвая» арифметика с линейным проходом по памяти.
- g на каждой итерации создаёт срез a[:] и вызывает doNothing. Если компилятор встраивает пустую функцию и выкидывает бесполезный срез, тело цикла почти исчезает. Если запретить инлайнинг, вы получите N вызовов функции и накладные расходы на создание среза на каждой итерации.
Чего ожидать?
С инлайнингом и DCE: g() часто быстрее f(), потому что почти ничего не делает, тогда как f() реально пишет в память. Отношение g()/f() < 1.
Без инлайнинга: g() резко замедляется из-за стоимости вызова функции и построения среза на каждой итерации. Отношение g()/f() ≫ 1, иногда на порядок.
Вывод: результат зависит от оптимизаций компилятора. «Смысл» цикла, который не производит наблюдаемых эффектов, Go охотно выкидывает.
Как проверить у себя
Копировать код
# обычный режим
go test -bench . -benchmem
# отключить инлайнинг -l, чтобы увидеть цену вызова функции
go test -gcflags='-l' -bench . -benchmem
Что запомнить
- Микробенчи в Go чувствительны к инлайнингу и dead-code elimination.
- Запись в память удерживает цикл «реальным», тогда как пустые вызовы и срезы могут исчезнуть.
- Измеряйте обе конфигурации: с оптимизациями и без, чтобы понять истинную стоимость.
Если коротко:
- В реальном билде с оптимизациями g()/f() обычно < 1.
- С запретом инлайнинга g()/f() обычно ≫ 1 из-за накладных расходов вызова.
👍2🔥1