Что считается небольшим объектом в Go в отношении распределения стека?

Код:

func MaxSmallSize() {
    a := make([]int64, 8191)
    b := make([]int64, 8192)
    _ = a
    _ = b
}

Затем запустите go build -gcflags='-m' . 2>&1, чтобы проверить детали распределения памяти. Результат:

./mem.go:10: can inline MaxSmallSize
./mem.go:12: make([]int64, 8192) escapes to heap
./mem.go:11: MaxSmallSize make([]int64, 8191) does not escape

Мой вопрос: почему a – это маленький объект, а b – большой?

make 64 КБ уйдут в кучу, и меньше будет выделено в стеке. Является ли _MaxSmallSize = 32 << 10 причиной?

go env

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/vagrant/gopath"
GORACE=""
GOROOT="/home/vagrant/go"
GOTOOLDIR="/home/vagrant/go/pkg/tool/linux_amd64"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build201775001=/tmp/go-build"
CXX="g++"
CGO_ENABLED="1"

person Bryce    schedule 15.02.2017    source источник
comment
Это зависит от реализации, разные компиляторы могут делать это по-разному, разные архитектуры могут делать это по-разному, и разные версии могут обрабатывать это по-разному. Так в чем именно ваш вопрос?   -  person Volker    schedule 15.02.2017


Ответы (1)


Поскольку это не упоминается в спецификации языка, это деталь реализации, и поэтому она может варьироваться в зависимости от ряда факторов (версия Go, целевая ОС, архитектура и т. д.).

Если вы хотите узнать его текущее значение или место, с которого можно начать копать, ознакомьтесь с пакетом cmd/compile/internal/gc.

анализ экранирования, который решает, где разместить переменную, находится в cmd/compile/internal/gc/esc.go. Проверка операции создания слайса находится в неэкспортированной функции esc():

func esc(e *EscState, n *Node, up *Node) {
    // ...

    // Big stuff escapes unconditionally
    // "Big" conditions that were scattered around in walk have been gathered here
    if n.Esc != EscHeap && n.Type != nil &&
        (n.Type.Width > MaxStackVarSize ||
            (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= 1<<16 ||
            n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
        if Debug['m'] > 2 {
            Warnl(n.Lineno, "%v is too large for stack", n)
        }
        n.Esc = EscHeap
        addrescapes(n)
        escassignSinkNilWhy(e, n, n, "too large for stack") // TODO category: tooLarge
    }

    // ...
}

Решение о размере находится в функции isSmallMakeSlice(), это в файле cmd/compile/internal/gc/walk.go< /а>:

func isSmallMakeSlice(n *Node) bool {
    if n.Op != OMAKESLICE {
        return false
    }
    l := n.Left
    r := n.Right
    if r == nil {
        r = l
    }
    t := n.Type

    return Smallintconst(l) && Smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < (1<<16)/t.Elem().Width)
}

Ограничение по размеру такое:

r.Int64() < (1<<16)/t.Elem().Width

r — длина или емкость слайса (если указано ограничение), t.Elem().Width — размер элемента в байтах:

NumElem < 65536 / SizeElem

В твоем случае:

NumElem < 65536 / 8 = 8192

Таким образом, если тип среза — []uint64, 8192 — это предел, от которого он выделяется в куче (а не в стеке), как вы уже поняли.

person icza    schedule 15.02.2017