Also, if the problem is so pathological, it might be worth filling an issue.
Slightly off topic (but relevant to my comment above), do you know why this:
func TraverseInline() {
var ints := make([]int, N)
for i := range ints {
sum += i
}
}
should be slower than: func TraverseArray(ints []int) int {
sum := 0
for i := range ints {
sum += i
}
return sum
}
func TraverseWithFunc() {
var ints := make([]int, N)
sum := TraverseArray(ints)
}
Seeing a huge difference here. With an array of 300000000 ints, TraverseInline() takes about 750ms, whereas TraverseWithFunc() takes 394ms. With gccgo (GCC 4.7.1), the different is much slighter, but there's still a difference: 196ms compared to 172ms. (These are best-case figures, I'm doing lots of loops.) Go is compiled, not JIT, so I don't see why a function should offer better performance.For more complex functions, you could try profiling using the runtime/pprof package. More info here: http://blog.golang.org/2011/06/profiling-go-programs.html
If you want to dig deeper and are not averse to assembly, you can pass -gcflags -S to 'go build' and see the compiler output. Here's what I got for the above code:
--- prog list "TraverseInline" ---
0000 (/tmp/tmp.rkM3kSJ6Hd/trav.go:10) TEXT TraverseInline+0(SB),$40-8
0001 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVQ $type.[]int+0(SB),(SP)
0002 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVQ $300000000,8(SP)
0003 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVQ $300000000,16(SP)
0004 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) CALL ,runtime.makeslice+0(SB)
0005 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVQ 24(SP),BX
0006 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVL 32(SP),SI
0007 (/tmp/tmp.rkM3kSJ6Hd/trav.go:11) MOVL 36(SP),BX
0008 (/tmp/tmp.rkM3kSJ6Hd/trav.go:12) MOVL $0,DX
0009 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) MOVL $0,AX
0010 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) JMP ,12
0011 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) INCL ,AX
0012 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) CMPL AX,SI
0013 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) JGE ,16
0014 (/tmp/tmp.rkM3kSJ6Hd/trav.go:14) ADDL AX,DX
0015 (/tmp/tmp.rkM3kSJ6Hd/trav.go:13) JMP ,11
0016 (/tmp/tmp.rkM3kSJ6Hd/trav.go:16) MOVL DX,.noname+0(FP)
0017 (/tmp/tmp.rkM3kSJ6Hd/trav.go:16) RET ,
--- prog list "TraverseArray" ---
0018 (/tmp/tmp.rkM3kSJ6Hd/trav.go:19) TEXT TraverseArray+0(SB),$0-24
0019 (/tmp/tmp.rkM3kSJ6Hd/trav.go:20) MOVL $0,DX
0020 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) MOVL $0,AX
0021 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) MOVL ints+8(FP),SI
0022 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) JMP ,24
0023 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) INCL ,AX
0024 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) CMPL AX,SI
0025 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) JGE ,28
0026 (/tmp/tmp.rkM3kSJ6Hd/trav.go:22) ADDL AX,DX
0027 (/tmp/tmp.rkM3kSJ6Hd/trav.go:21) JMP ,23
0028 (/tmp/tmp.rkM3kSJ6Hd/trav.go:24) MOVL DX,.noname+16(FP)
0029 (/tmp/tmp.rkM3kSJ6Hd/trav.go:24) RET ,
--- prog list "TraverseWithFunc" ---
0030 (/tmp/tmp.rkM3kSJ6Hd/trav.go:27) TEXT TraverseWithFunc+0(SB),$40-8
0031 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVQ $type.[]int+0(SB),(SP)
0032 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVQ $300000000,8(SP)
0033 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVQ $300000000,16(SP)
0034 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) CALL ,runtime.makeslice+0(SB)
0035 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVQ 24(SP),DX
0036 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVL 32(SP),CX
0037 (/tmp/tmp.rkM3kSJ6Hd/trav.go:28) MOVL 36(SP),AX
0038 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) LEAQ (SP),BX
0039 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) MOVQ DX,(BX)
0040 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) MOVL CX,8(BX)
0041 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) MOVL AX,12(BX)
0042 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) CALL ,TraverseArray+0(SB)
0043 (/tmp/tmp.rkM3kSJ6Hd/trav.go:29) MOVL 16(SP),AX
0044 (/tmp/tmp.rkM3kSJ6Hd/trav.go:30) MOVL AX,.noname+0(FP)
0045 (/tmp/tmp.rkM3kSJ6Hd/trav.go:30) RET ,https://gist.github.com/3053930
The numbers are from Go 1.0 as that's what is available on Ubuntu Precise, my test box. (Getting similar numbers with 1.0.2 on a different box where I don't have gccgo.)
I realized that the first loop was using "i < len(ints)" as a loop test, which turns out to be more expensive than I thought, and the compiler doesn't optimize it into a constant (which would be expecting too much, I guess). After rewriting the test, the function call case is only slightly faster, although it is still significantly faster with gccgo.
for i := 0; i < len(ints); i++ {
sum += i
} sum += ints[i]for index, value := range mySlice { }
and you get the index and the value, or you can do
for _, value := range mySlice { }
If you only need the value.
The Go authors should be very receptive and informative on such an issue.
2 theories on why gcc is doing better. Maybe its unrolling the loop, and avoids a bunch of jumps. Alternatively, it could be using SSE, but this theory is a much less likely, because I would expect it to be 4 times faster not twice as fast. gc doesn't use SSE instructions yet.