Why these benchmark results are so different?
func Benchmark1(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = make([]byte, 8)
}
}
func Benchmark2(b *testing.B) {
length := 1
for n := 0; n < b.N; n++ {
_ = make([]byte, 7+length)
}
}
Benchmark results:
Benchmark1-8 500000000 3.37 ns/op
Benchmark2-8 30000000 50.6 ns/op
The constant expression 8
is evaluated at compile time. The make
is allocated on a goroutine stack (cheap). The variable expression 7 + length
is evaluated at run time. The make
is allocated on the program heap (expensive). If the make
size is too large for a stack allocation (for example, constant (64*1024)
and variable (64*1024-1)+length
) then both allocations are made on the heap and the benchmark times are the same.
$ go tool compile -m a_test.go
a_test.go:5: Benchmark1 b does not escape
a_test.go:7: Benchmark1 make([]byte, 8) does not escape
a_test.go:14: make([]byte, 7 + length) escapes to heap
a_test.go:11: Benchmark2 b does not escape
$
a_test.go
:
package a
import "testing"
func Benchmark1(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = make([]byte, 8)
}
}
func Benchmark2(b *testing.B) {
length := 1
for n := 0; n < b.N; n++ {
_ = make([]byte, 7+length)
}
}
Go pseudo-assembler:
$ go tool compile -S a_test.go
Benchmark1:
"".Benchmark1 t=1 size=112 value=0 args=0x8 locals=0x20
0x0000 00000 (a_test.go:5) TEXT "".Benchmark1(SB), $32-8
0x0000 00000 (a_test.go:5) SUBQ $32, SP
0x0004 00004 (a_test.go:5) MOVQ "".b+40(FP), CX
0x0009 00009 (a_test.go:5) FUNCDATA $0, gclocals·87d20ce1b58390b294df80b886db78bf(SB)
0x0009 00009 (a_test.go:5) FUNCDATA $1, gclocals·790e5cc5051fc0affc980ade09e929ec(SB)
0x0009 00009 (a_test.go:6) MOVQ $0, AX
0x000b 00011 (a_test.go:6) NOP
0x000b 00011 (a_test.go:6) MOVQ 112(CX), BX
0x000f 00015 (a_test.go:6) CMPQ BX, AX
0x0012 00018 (a_test.go:6) JLE $0, 98
0x0014 00020 (a_test.go:7) MOVQ $0, BX
0x0016 00022 (a_test.go:7) MOVB BL, "".autotmp_0001(SP)
0x0019 00025 (a_test.go:7) MOVB BL, "".autotmp_0001+1(SP)
0x001d 00029 (a_test.go:7) MOVB BL, "".autotmp_0001+2(SP)
0x0021 00033 (a_test.go:7) MOVB BL, "".autotmp_0001+3(SP)
0x0025 00037 (a_test.go:7) MOVB BL, "".autotmp_0001+4(SP)
0x0029 00041 (a_test.go:7) MOVB BL, "".autotmp_0001+5(SP)
0x002d 00045 (a_test.go:7) MOVB BL, "".autotmp_0001+6(SP)
0x0031 00049 (a_test.go:7) MOVB BL, "".autotmp_0001+7(SP)
0x0035 00053 (a_test.go:7) LEAQ "".autotmp_0001(SP), BX
0x0039 00057 (a_test.go:7) CMPQ BX, $0
0x003d 00061 (a_test.go:7) JEQ $1, 103
0x003f 00063 (a_test.go:7) MOVQ $8, "".autotmp_0002+16(SP)
0x0048 00072 (a_test.go:7) MOVQ $8, "".autotmp_0002+24(SP)
0x0051 00081 (a_test.go:7) MOVQ BX, "".autotmp_0002+8(SP)
0x0056 00086 (a_test.go:6) INCQ AX
0x0059 00089 (a_test.go:6) NOP
0x0059 00089 (a_test.go:6) MOVQ 112(CX), BX
0x005d 00093 (a_test.go:6) CMPQ BX, AX
0x0060 00096 (a_test.go:6) JGT $0, 20
0x0062 00098 (a_test.go:9) ADDQ $32, SP
0x0066 00102 (a_test.go:9) RET
0x0067 00103 (a_test.go:7) MOVL AX, (BX)
0x0069 00105 (a_test.go:7) JMP 63
Benchmark2
:
"".Benchmark2 t=1 size=144 value=0 args=0x8 locals=0x58
0x0000 00000 (a_test.go:11) TEXT "".Benchmark2(SB), $88-8
0x0000 00000 (a_test.go:11) MOVQ (TLS), CX
0x0009 00009 (a_test.go:11) CMPQ SP, 16(CX)
0x000d 00013 (a_test.go:11) JLS 129
0x000f 00015 (a_test.go:11) SUBQ $88, SP
0x0013 00019 (a_test.go:11) FUNCDATA $0, gclocals·87d20ce1b58390b294df80b886db78bf(SB)
0x0013 00019 (a_test.go:11) FUNCDATA $1, gclocals·790e5cc5051fc0affc980ade09e929ec(SB)
0x0013 00019 (a_test.go:12) MOVQ $1, "".length+56(SP)
0x001c 00028 (a_test.go:13) MOVQ $0, AX
0x001e 00030 (a_test.go:13) MOVQ "".b+96(FP), BP
0x0023 00035 (a_test.go:13) NOP
0x0023 00035 (a_test.go:13) MOVQ 112(BP), BX
0x0027 00039 (a_test.go:13) MOVQ AX, "".n+48(SP)
0x002c 00044 (a_test.go:13) CMPQ BX, AX
0x002f 00047 (a_test.go:13) JLE $0, 124
0x0031 00049 (a_test.go:14) MOVQ "".length+56(SP), AX
0x0036 00054 (a_test.go:14) ADDQ $7, AX
0x003a 00058 (a_test.go:14) LEAQ type.[]uint8(SB), BX
0x0041 00065 (a_test.go:14) MOVQ BX, (SP)
0x0045 00069 (a_test.go:14) MOVQ AX, 8(SP)
0x004a 00074 (a_test.go:14) MOVQ AX, 16(SP)
0x004f 00079 (a_test.go:14) PCDATA $0, $0
0x004f 00079 (a_test.go:14) CALL runtime.makeslice(SB)
0x0054 00084 (a_test.go:14) MOVQ 24(SP), BX
0x0059 00089 (a_test.go:14) MOVQ BX, "".autotmp_0005+64(SP)
0x005e 00094 (a_test.go:14) MOVQ 32(SP), BX
0x0063 00099 (a_test.go:14) MOVQ BX, "".autotmp_0005+72(SP)
0x0068 00104 (a_test.go:14) MOVQ 40(SP), BX
0x006d 00109 (a_test.go:14) MOVQ BX, "".autotmp_0005+80(SP)
0x0072 00114 (a_test.go:13) MOVQ "".n+48(SP), AX
0x0077 00119 (a_test.go:13) INCQ AX
0x007a 00122 (a_test.go:13) NOP
0x007a 00122 (a_test.go:13) JMP 30
0x007c 00124 (a_test.go:16) ADDQ $88, SP
0x0080 00128 (a_test.go:16) RET
0x0081 00129 (a_test.go:11) CALL runtime.morestack_noctxt(SB)
0x0086 00134 (a_test.go:11) JMP 0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With