Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GoLang Pointer Performance

The following code shows two benchmarks. The first one creates a struct by value in each iteration, while the second one does use a pointer to the struct.

Why is the latter 20x slower ?? I know about GC issues with GoLang, but shouldn't escape analysis handle those situations ?

I'm using go1.4beta1, but 1.3.3 gave me the [same - wrong] different results.

Any idea ?

package main

import "testing"

type Adder struct {
    vals []int
}

func (a *Adder) add() int {
    return a.vals[0] + a.vals[1]
}

func BenchmarkWithoutPointer(b *testing.B) {
    accum := 0
    for i := 0; i < b.N; i++ {
        adder := Adder{[]int{accum, i}}
        accum = adder.add()
    }
    _ = accum
}

func BenchmarkWithPointer(b *testing.B) {
    accum := 0
    for i := 0; i < b.N; i++ {
        adder := &Adder{[]int{accum, i}}
        accum = adder.add()
    }
    _ = accum
}

Benchmark go1.4.1:

 $ go test -bench=.                                                                                                                             

testing: warning: no tests to run
PASS
BenchmarkWithoutPointer 1000000000           2.92 ns/op
BenchmarkWithPointer    30000000            57.8 ns/op
ok      github.com/XXXXXXXXXX/bench/perf    5.010s

Benchmark go1.3.3:

testing: warning: no tests to run
PASS
BenchmarkWithoutPointer 500000000            7.89 ns/op
BenchmarkWithPointer    50000000            37.5 ns/op
ok      

EDIT:

Conclusion:

As Ainar-G said, the []int does escape to heap in the second benchmark. After reading a bit more about 1.4beta1 it seems, that new write barriers are introduced when accessing the heap caused by the new GC plans. But raw execution seems to have increased. Looking forward to 1.5 =).

like image 966
Kr0e Avatar asked Nov 13 '14 10:11

Kr0e


1 Answers

Running the benchmark with the -m gcflag gives the possible answer:

./main_test.go:16: BenchmarkWithoutPointer []int literal does not escape
(...)
./main_test.go:25: []int literal escapes to heap

Your []int in the second example escapes to heap, which is slower than stack. If you use separate x and y fields for your arguments instead of a slice

type Adder struct {
    x, y int
}

func (a *Adder) add() int {
    return a.x + a.y
}

the benchmark shows the expected behaviour:

BenchmarkWithoutPointer 1000000000               2.27 ns/op
BenchmarkWithPointer    2000000000               1.98 ns/op
like image 126
Ainar-G Avatar answered Sep 18 '22 10:09

Ainar-G