What is considered “small” object in Go regarding stack allocation?

后端 未结 1 550
盖世英雄少女心
盖世英雄少女心 2020-12-18 14:05

The code:

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

Then run go build -gc

相关标签:
1条回答
  • 2020-12-18 14:14

    Since this is not mentioned in the language spec, it is an implementation detail, and as such, it may vary based on a number of things (Go version, target OS, architecture etc.).

    If you want to find out its current value or a place to start digging, check out the cmd/compile/internal/gc package.

    The escape analysis which decides where to allocate the variable is in cmd/compile/internal/gc/esc.go. Check of the make slice operation is in unexported function 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
        }
    
        // ...
    }
    

    The decision involving the size is in function isSmallMakeSlice(), this is in file 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)
    }
    

    The size limit is this:

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

    r is the length or capacity of the slice (if cap is provided), t.Elem().Width is the byte size of the element type:

    NumElem < 65536 / SizeElem
    

    In your case:

    NumElem < 65536 / 8 = 8192
    

    So if the slice type is []uint64, 8192 is the limit from which it is allocated on the heap (instead of the stack), just as you experienced.

    0 讨论(0)
提交回复
热议问题