Что такое встроенная функция
Он будет упоминаться во многих технических материалах и блогах о базовом языке Go.встроенная функцияЭтот термин также используется некоторымивстроенная функцияГоворя о встраивании кода, расширении функций, расширении функций и т. д., на самом деле то, что я хочу выразить, — это оптимизация вызовов функций компилятором языка Go.Компилятор напрямую заменит некоторые вызовы функций кодом в теле функции вызываемой функции.Место вызова расширено, чтобы уменьшить затраты времени, вызванные вызовами функций. Это распространенный метод, используемый компилятором языка Go для оптимизации кода.
встроенная функцияЭто не уникально для компилятора языка Go. Компиляторы на многих языках оптимизируют встроенные функции при компиляции кода. Википедия объясняет встроенные функции следующим образом (я намеренно выделил жирным шрифтом информацию, требующую внимания):
существуетИнформатикасередина,встроенная функция(иногда называютОнлайн-функцияилифункция расширения времени компиляции) этоЯзык программированияструктура, используемая для предположенияпереводчикдля некоторых особенныхфункцияпровестиВстроенное расширение(иногда называютонлайн-расширение), то есть рекомендуетсяКомпилятор вставляет указанный корпус функции и заменяет каждый вызов функции (контекст), тем самым экономя дополнительные затраты времени на вызов функции каждый раз. Но когда вы решите использовать встроенные функции, вы должны использовать пробел и программу.эффективностьМежду ними существует компромисс, потому что встроенное расширение слишком большого количества более сложных функций приведет к большим накладным расходам ресурсов хранения. Кроме того, необходимо обратить особое внимание нарекурсивная функцияВстроенная экспансия может привести к бесконечным компилятору частей компилятора.
Примечание: Встроенная оптимизация обычно используется для быстрого выполнения функций, потому что в этом случае более заметно потребление времени на вызов функции, а объем функции внутреннего контейнера не будет значительно увеличивать занимаемое скомпилированным исполняемым файлом пространство..
Встроенные функции в Go
В качестве примера предположим, что у меня есть программа, подобная следующей, которая суммирует два числа (не нервничайте, это не арифметическая задача — сумма двух чисел...)
package main
import "fmt"
func main() {
x := 20
y := 5
res := add(x, y)
fmt.Println(res)
}
func add(x int, y int) int {
return x + y
}
Вышеупомянутая функция очень проста. Функция add добавляет два параметра. Компилятор выполнит встроенную оптимизацию при компиляции приведенного выше кода Go и поместитadd
Тело функции расширяется непосредственно на месте вызова, что эквивалентно тому, как написан приведенный выше код Go.
package main
import "fmt"
func main() {
x := 20
y := 5
// 内联函数, 或者叫把函数展开在调用处
res := x + y
fmt.Println(res)
}
func add(x int, y int) int {
return x + y
}
Сообщите компилятору, что функция не встроена
Мы также не видим вызова функции добавления в ассемблерном коде, скомпилированном из исходников, но мы можем указать компилятору не выполнять встроенную оптимизацию, добавив специальный комментарий к функции добавления
// 注意下面这行注释,"//"后面不要有空格
//go:noinline
func add(x int, y int) int {
return x + y
}
Как проверить, что эта аннотация настоящая и действующая, чтобы компилятор мог ошибитьсяadd
Как насчет встроенной оптимизации функций? мы можем использоватьgo tool compile -S scratch.go
Ассемблерный код, в который скомпилирован напечатанный код Go, в ассемблерном коде мы можем найти правильныйadd
вызов функции.
0x0053 00083 (scratch.go:6) CALL "".add(SB)
Это также доказывает обратное, что при нормальных обстоятельствах компилятор Go будетadd
Встраивание функции оптимизации.
Пусть компилятор скажет нам, какие оптимизации будут внесены в код.
Помимо анализа скомпилированного исходного кода сборки, мы также можемgo buildпередача команд-gcflags -mпараметр
$ go build -gcflags --help
[.......]
// 传递 -m 选项会输出编译器对代码的优化
-m print optimization decisions
Пусть компилятор скажет нам, какие оптимизации он сделал с кодом при компиляции кода Go.
Затем создайте наш апплет с помощью -gcflags -m
$ go build -gcflags -m scratch.go
./scratch_16.go:10:6: can inline add
./scratch_16.go:6:12: inlining call to add
./scratch_16.go:7:13: inlining call to fmt.Println
./scratch_16.go:7:13: res escapes to heap
./scratch_16.go:7:13: main []interface {} literal does not escape
./scratch_16.go:7:13: io.Writer(os.Stdout) escapes to heap
Через вывод терминала мы можем узнать, что компилятор судитadd
Функции могут быть встроены, а такжеadd
функция встроена, в дополнение кfmt.Println
Сделаны встроенные оптимизации. есть еще одинio.Writer(os.Stdout) escapes to heapВывод показывает, что объект ввода-вывода перемещается в кучу. Выход из кучи — еще одна оптимизация. В предыдущей статье из серии «Управление памятью в Go» —Escape-анализ кода для управления памятью Goбыло сказано подробно.
какие функции не будут включены
Итак, выполняет ли компилятор Go встроенную оптимизацию для всех небольших быстро выполняющихся функций? Я проверил документацию и обнаружил, что в Go есть критерий при принятии решения о встраивании функции:
Тело функции содержит: вызов закрытия, функция с ключевыми словами select, for, defer, go не будет встроена. И кроме них есть и другие ограничения. При анализе AST Go утверждает 80 узлов в качестве бюджета для встраивания. Каждый узел потребляет бюджет. Например,a = a + 1
Эта строка кода содержит 5 узлов: AS, NAME, ADD, NAME, LITERAL. Вот соответствующий дамп SSA:
Когда накладные расходы функции превышают этот бюджет, ее нельзя встроить..
Приведенное выше описание перевода самообслуживанияmedium.com/ah-journey-i…
Суммировать
Встраивание является важным средством высокопроизводительного программирования. Каждый вызов функции имеет накладные расходы: создание кадров стека, чтение и запись регистров, этих накладных расходов можно избежать путем встраивания, а повышение производительности составляет около 5–6%. Однако копирование тела функции в строку также увеличит размер скомпилированного бинарного файла.К счастью, при программировании на языке Go компилятор поможет нам решить, какие функции можно встроить, что значительно снижает умственную нагрузку пользователя.
Сегодняшняя статья здесь. Если вам понравилась моя статья, пожалуйста, дайте мне лайк. Я буду делиться тем, что я узнал, и практическим опытом из первых рук в технических статьях каждую неделю. Спасибо за вашу поддержку. Поиск WeChat и подписка на общедоступную учетную запись «Управление сетью Наоби Нао» каждую неделю обучают вас углубленным знаниям, а также есть вводные руководства по Kubernetes, специально написанные для инженеров-разработчиков.