我在用不同的方式在 golang 中初始化/声明数组。我得到了不同的行为/结果。
转到版本 go1.3 达尔文/amd64
版本 1:
func main() {
a := [100000000]int64{}
var i int64
for i = 0; i < 100000000; i++ {
a[i] = i
}
}
生成一个 763MB 的二进制文件。当我用这条消息运行它时,它会在几秒钟后崩溃。
运行时:goroutine 堆栈超过 1000000000 字节的限制
致命错误:堆栈溢出
版本 2:
func main() {
var a [100000000]int64
var i int64
for i = 0; i < 100000000; i++ {
a[i] = i
}
}
生成一个 456KB 的二进制文件。它在不到一秒钟的时间内运行。
问题:
任何人都可以帮助我理解为什么存在这些差异(以及我可能遗漏的其他差异)?谢谢!
编辑:
运行时间:
我构建了两个不同的片段并运行编译版本,因此没有添加编译时间。相比之下,我第一次运行 version1 的速度非常慢。这是输出。
go build version1.go
go build version2.go
这些是执行输出
版本 1
第一次运行
time ./version1
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow
runtime stack:
runtime.throw(0x2fb42a8e)
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/panic.c:520 +0x69
runtime.newstack()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/stack.c:770 +0x486
runtime.morestack()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:228 +0x61
goroutine 16 [stack growth]:
main.main()
/Users/ec/repo/offers/lol/version1.go:3 fp=0x2b7b85f50 sp=0x2b7b85f48
runtime.main()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:247 +0x11a fp=0x2b7b85fa8 sp=0x2b7b85f50
runtime.goexit()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445 fp=0x2b7b85fb0 sp=0x2b7b85fa8
created by _rt0_go
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:97 +0x120
goroutine 17 [runnable]:
runtime.MHeap_Scavenger()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/mheap.c:507
runtime.goexit()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445
./version1 0.00s user 0.10s system 1% cpu 7.799 total
第二次运行
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow
runtime stack:
runtime.throw(0x2fb42a8e)
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/panic.c:520 +0x69
runtime.newstack()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/stack.c:770 +0x486
runtime.morestack()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:228 +0x61
goroutine 16 [stack growth]:
main.main()
/Users/ec/repo/offers/lol/version1.go:3 fp=0x2b7b85f50 sp=0x2b7b85f48
runtime.main()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:247 +0x11a fp=0x2b7b85fa8 sp=0x2b7b85f50
runtime.goexit()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445 fp=0x2b7b85fb0 sp=0x2b7b85fa8
created by _rt0_go
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/asm_amd64.s:97 +0x120
goroutine 17 [runnable]:
runtime.MHeap_Scavenger()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/mheap.c:507
runtime.goexit()
/usr/local/Cellar/go/1.3/libexec/src/pkg/runtime/proc.c:1445
./version1 0.00s user 0.10s system 98% cpu 0.102 total
版本 2
第一次运行
time ./version2
./version2 0.16s user 0.26s system 99% cpu 0.429 total
第二次运行
time ./version2
./version2 0.17s user 0.25s system 97% cpu 0.421 total
最佳答案
在版本 1 中,您声明了一个由编译器立即分配的 [100000000]int64{}
文字数组。
版本 2,您只是将 a
的类型声明为 [100000000]int64
。
当您只有一个变量声明时,在编译过程中此时的内容是未知的。在版本 2 中,编译器知道 a
是 [100000000]int64
类型,但直到运行时才会分配内存。
当您使用文字时,确切的内存表示将写入二进制文件。它的工作原理与您声明 string
文字与 string
类型的变量相同;字符串文字将被写入到位,而变量声明只是一个占位符。
即使当前的编译器(go 1.3)允许 a
转义到堆中,文字数据也应该存在于堆栈帧中。您可以在汇编输出中看到这一点(帧大小为 800000016):
TEXT "".func1+0(SB),$800000016-0
如果您确实需要一个比堆栈中可以容纳的更大的文字,您可以将它放在一个全局变量中。以下执行得很好:
var a = [100000000]int64{1}
func func1() {
var i int64
for i = 0; i < 100000000; i++ {
a[i] = i
}
}
我确实必须在这里的
a
中至少初始化一个值,因为如果它等于零值,编译器似乎可以省略这个文字。关于arrays - Go:空花括号对数组初始化内存分配的影响,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26063242/