Почему анализ экранирования Go ведет себя по-разному, когда простой тип передается двум разным функциям с одинаковой сигнатурой

#go #escape-analysis

# #Вперед #escape-анализ

Вопрос:

Я не совсем понимаю, почему анализ экранирования Go говорит, что локальная целочисленная переменная экранируется в кучу, когда она передается fmt.Fprintln , в то время как она не экранируется, когда она передается другой самописной функции с той же сигнатурой, fmt.Fprintln что и . Я надеюсь, что два сценария A и B, приведенные ниже, должны прояснить проблему.

Сценарий A: x экранирование в кучу
Возьмите следующее содержимое main.go :

 package main

import (
    "fmt"
    "os"
)

func main() {
    x := 73
    fmt.Fprintln(os.Stdout, x)
}
 

Когда вы теперь запускаете go build -gcflags "-m" , вы видите вывод, который указывает, что x экранирование выполняется в кучу. В выводе говорится что-то вроде

 ...
.main.go:10:14: x escapes to heap
...
 

Сценарий B: x не экранируется в кучу
Теперь возьмем следующее содержимое main.go :

 package main

import (
    "io"
    "os"
)

func main() {
    x := 73
    myOwnPrintFunction(os.Stdout, x)
}

//go:noinline
func myOwnPrintFunction(w io.Writer, a ...interface{}) {
    println(a)
}
 

При повторном запуске go build -gcflags "-m" вы видите вывод, который указывает, что x он больше не экранируется в кучу. Теперь вывод говорит что-то вроде

 ...
.main.go:10:20: x does not escape
...
 

Я не понимаю здесь разницы. Я бы подумал, что сценарий A правильный, поскольку x передается переменной функции, которая принимает произвольное количество interface{} аргументов. Но, по-видимому, это неправильно, поскольку переменная больше не экранируется, когда она передается самописной функции myOwnPrintFunction , которая также является переменной функцией, принимающей произвольное количество interface{} аргументов, и где компилятору дано указание не вводить эту простую функцию.

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

1. Это неуказанная деталь реализации и зависит от используемого компилятора. Основной причиной может быть то, что fmt.Fprintln делает гораздо больше, чем println . Все это не имеет ничего общего с тем, как вызывается функция, или если это ваша собственная функция или из stdlib, но если анализ escape может доказать, что переменная может быть выделена стеком.