go cmd/compile: autotemps can make stack frame too large

6fe3ivhb  于 6个月前  发布在  Go
关注(0)|答案(5)|浏览(54)

当顺序传递引入临时变量时,即使它们对于栈来说太大,也总是在栈上分配它们。
我们应该使用与逃逸分析相同的规则来决定是否将临时变量放在栈上还是堆上。
排序前:
排序后:

kdfy810k

kdfy810k1#

这是一个错误:

$ go tool compile tmp2.go
tmp2.go:5:6: stack frame too large (>1GB)
gmol1639

gmol16392#

在walk.go文件中(func是smallMakeSlice,case是ONEW),cutoff (1<<16)也在几个地方重复出现。应该统一它们。

ttvkxqim

ttvkxqim3#

我们如何让autotemps堆分配这个case?顺序发生在逃逸分析完成后。

b09cbbtk

b09cbbtk4#

这可能是个棘手的问题。乍一看,没有立即出现问题——顺序可能会引入对新对象的调用。如果对象是无指针的,那么它可以正常工作。但是如果对象有指针,那么之前不会逃逸的东西现在会逃逸。我认为正确的解决方案(尽管困难)是将这些堆分配的对象以某种方式视为栈(没有写屏障,作为栈扫描的一部分进行扫描等)。
一个好的第一步是仅解决无指针对象的问题。

bnl4lu3b

bnl4lu3b5#

看起来这也适用于切片字面量。

package main

import t "testing"

const M = 1024 * 1024 * 100

var n int = M - 1
var b byte

func f() {
	type _ int
	var bs = []byte{M+M: 0} 
	b = bs[n]
}

func main() {
	x := t.AllocsPerRun(2, f)
	println(int(x)) // 0
}

{edit] 一些数组字面量的特殊情况:

package main

const N = 100 * 1024 * 1024

var m = make(map[byte][N]byte, 1)
func foo() {
	m[0] = [N]byte{} // copy stack to heap
}

var i interface{}
func bar() {
	i = [N]byte{} // copy stack to heap
}

func pen(vx ...interface{}) {
	type _ int
}

func qin(m map[byte][N]byte) {
	m[1] = [N]byte{} // copy stack to heap
}

func main() {
	run := func(f func(), c chan struct{}) {
		defer close(c)
		var x int
		println(&x) // <address 1>
		f()
		println(&x) // <address 2>
	}
	c1 := make(chan struct{})
	go run(foo, c1)
	<-c1
	c2 := make(chan struct{})
	go run(bar, c2)
	<-c2
	c3 := make(chan struct{})
	go run(func() {qin(map[byte][N]byte{})}, c3)
	<-c3
	c4 := make(chan struct{})
	go run(func() {pen([N]byte{})}, c4)
	<-c4
}

相关问题