为什么切片不断从堆栈中逃脱?

我正在尝试解决leetcode问题排列。但是当我用-benchmem进行测试时,我发现它分配太多了,达到1957年的分配/操作时permute([]int{1,2,3,4,5,6})


我发现它在生成子数字目标时逃逸到堆中。即使我试图分配[6]int,并使用不安全的包来构建切片,它仍然.moved to heap


我的问题是,为什么切片逃逸到堆,以及如何在堆栈上分配切片?


这是我的代码:


package main


import (

    "fmt"

    "reflect"

    "unsafe"

)



func permute(nums []int) [][]int {

    resLen := 1

    for i := 1; i<= len(nums);i ++{

        resLen *= i

    }

    // pre allocate

    res := make([][]int, resLen)

    for i := range res{

        res[i] = make([]int, 0, len(nums))

    }


    build(res, nums)


    return res

}


func build(res [][]int,targets []int){

    step := len(res) / len(targets)

    for i := range targets{

        for j := i*step; j < (i+1) * step; j ++{

            res[j] = append(res[j], targets[i])

        }

        if len(targets) != 1{

            var ab = [6]int{}

            var buff []int

            var bp  *reflect.SliceHeader

            bp = (*reflect.SliceHeader)(unsafe.Pointer(&buff))

            bp.Data = uintptr(unsafe.Pointer(&ab))

            bp.Cap = 6

            buff = append(buff, targets[:i]...)

            buff = append(buff, targets[i+1:]...)

            build(res[i*step:(i+1)*step], buff)

        }

    }

    return

}


func main() {

    nums := []int{1,2,3}

    res := permute(nums)

    fmt.Println(res)

}


build函数没有不安全,但逃逸到堆:


func build(res [][]int, targets []int) {

    step := len(res) / len(targets)

    for i := range targets {

        for j := i * step; j < (i+1)*step; j++ {

            res[j] = append(res[j], targets[i])

        }

        if len(targets) != 1 {

            buff := make([]int, 0, 6) //  make([]int, 0, 6) escapes to heap

            buff = append(buff, targets[:i]...)

            buff = append(buff, targets[i+1:]...)

            build(res[i*step:(i+1)*step], buff)

        }

    }

    return

}

还有我的测试用例:


package main


import "testing"


func Benchmark(b *testing.B){

    for i:=0;i<b.N;i++{

        permute([]int{1,2,3,4,5,6})

    }

}

当我运行时,它报告go build -gcflags="-m"./main.go:32:8: moved to heap: ab


收到一只叮咚
浏览 101回答 1
1回答

幕布斯6054654

尝试使用颠覆编译器只会使转义分析更难完成其工作,从而阻止切片被堆栈分配。只需分配单个切片,并在每次循环迭代中重复使用它:unsafe.Pointerfunc build(res [][]int, targets []int) {&nbsp; &nbsp; buff := make([]int, 0, 6)&nbsp; &nbsp; step := len(res) / len(targets)&nbsp; &nbsp; for i := range targets {&nbsp; &nbsp; &nbsp; &nbsp; buff = buff[:0]&nbsp; &nbsp; &nbsp; &nbsp; for j := i * step; j < (i+1)*step; j++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; res[j] = append(res[j], targets[i])&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if len(targets) != 1 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buff = append(buff, targets[:i]...)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; buff = append(buff, targets[i+1:]...)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; build(res[i*step:(i+1)*step], buff)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return}这可以通过编译器正确优化./main.go:26:17: make([]int, 0, 6) does not escape并且只会导致所需的分配:Benchmark-8&nbsp; 44607&nbsp; 26838 ns/op&nbsp; 52992 B/op&nbsp; 721 allocs/op
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go