Skip to content

Instantly share code, notes, and snippets.

@xogeny
Created January 27, 2015 18:12
Show Gist options
  • Save xogeny/c8dfd0c4742aee502926 to your computer and use it in GitHub Desktop.
Save xogeny/c8dfd0c4742aee502926 to your computer and use it in GitHub Desktop.
A different append benchmark for Golang
package copy_vs_append
import (
"testing"
)
func buildArray(n int64) []int64 {
ret := make([]int64, n, n)
var i int64
for i = 0; i < n; i++ {
ret[i] = i
}
return ret
}
func checkArray(y []int64, t *testing.T) {
if len(y) != 1000 {
t.Fatalf("Expected len(y) to be 1000 but was %d", len(y))
}
for i := int64(0); i < int64(1000); i++ {
if y[i] != i {
t.Fatalf("Expected y[%d] = %d but was %d", i, i, y[i])
}
}
}
func TestCopy(t *testing.T) {
existing := buildArray(1000)
y := doCopy(existing, true, false)
checkArray(y, t)
}
func TestAppend(t *testing.T) {
existing := buildArray(1000)
y := doCopy(existing, false, false)
checkArray(y, t)
}
func TestAppendAlloc(t *testing.T) {
existing := buildArray(1000)
y := doCopy(existing, false, true)
checkArray(y, t)
}
func doCopy(src []int64, useCopy bool, preAlloc bool) []int64 {
var y []int64
if useCopy {
y = make([]int64, 1000, 1000)
copy(y, src)
} else {
var init []int64
if preAlloc {
init = make([]int64, 0, 1000)
} else {
init = []int64{}
}
y = append(init, src...)
}
return y
}
func BenchmarkAppend(b *testing.B) {
existing := buildArray(1000)
for i := 0; i < b.N; i++ {
doCopy(existing, false, false)
}
}
func BenchmarkAppendAlloc(b *testing.B) {
existing := buildArray(1000)
for i := 0; i < b.N; i++ {
doCopy(existing, false, true)
}
}
func BenchmarkAppendAllocInline(b *testing.B) {
existing := buildArray(1000)
for i := 0; i < b.N; i++ {
var init []int64
init = make([]int64, 0, 1000)
_ = append(init, existing...)
}
}
func BenchmarkAppendAllocInlineNoFunc(b *testing.B) {
existing := make([]int64, 1000, 1000)
for i := 0; i < b.N; i++ {
var init []int64
init = make([]int64, 0, 1000)
_ = append(init, existing...)
}
}
func BenchmarkCopy(b *testing.B) {
existing := buildArray(1000)
for i := 0; i < b.N; i++ {
doCopy(existing, true, true)
}
}
@xogeny
Copy link
Author

xogeny commented Jan 27, 2015

A follow up, I modified this to allow arrays of different sizes (1000 vs 100000) and I did a b.ResetTimer just before the loop to ignore the cost of creating the array.

Results were:

PASS
BenchmarkAppend                   10000     210092 ns/op
BenchmarkAppendAlloc              10000     203140 ns/op
BenchmarkAppendAllocInline        10000     204048 ns/op
BenchmarkAppendAllocInlineNoFunc  5000      258291 ns/op
BenchmarkCopy                     10000     207800 ns/op

@zigo101
Copy link

zigo101 commented Jun 27, 2017

The compiler will make some optimization if it finds a local variable is not used actually.
(In only realize this recently)

So it is best to use global variables as the inputs and outputs of tested code.

package copy_vs_append

import (
	"testing"
)

func buildArray(n int64) []int64 {
	ret := make([]int64, n, n)
	var i int64
	for i = 0; i < n; i++ {
		ret[i] = i
	}
	return ret
}

func checkArray(y []int64, t *testing.T) {
	if len(y) != 1000 {
		t.Fatalf("Expected len(y) to be 1000 but was %d", len(y))
	}
	for i := int64(0); i < int64(1000); i++ {
		if y[i] != i {
			t.Fatalf("Expected y[%d] = %d but was %d", i, i, y[i])
		}
	}
}

var existing = buildArray(1000)
var y []int64

func TestCopy(t *testing.T) {
	y = doCopy(existing, true, false)
	checkArray(y, t)
}

func TestAppend(t *testing.T) {
	y = doCopy(existing, false, false)
	checkArray(y, t)
}

func TestAppendAlloc(t *testing.T) {
	y = doCopy(existing, false, true)
	checkArray(y, t)
}

func doCopy(src []int64, useCopy bool, preAlloc bool) []int64 {
	var y []int64
	if useCopy {
		y = make([]int64, 1000, 1000)
		copy(y, src)
	} else {
		var init []int64

		if preAlloc {
			init = make([]int64, 0, 1000)
		} else {
			init = []int64{}
		}
		y = append(init, src...)
	}
	return y
}

func BenchmarkAppend(b *testing.B) {
	for i := 0; i < b.N; i++ {
		y = doCopy(existing, false, false)
	}
}

func BenchmarkAppendAlloc(b *testing.B) {
	for i := 0; i < b.N; i++ {
		y = doCopy(existing, false, true)
	}
}

func BenchmarkAppendAllocInline(b *testing.B) {
	for i := 0; i < b.N; i++ {
		var init []int64

		init = make([]int64, 0, 1000)
		y = append(init, existing...)
	}
}

func BenchmarkAppendAllocInlineNoFunc(b *testing.B) {
	for i := 0; i < b.N; i++ {

		var init []int64

		init = make([]int64, 0, 1000)
		y = append(init, existing...)
	}
}

func BenchmarkCopy(b *testing.B) {
	for i := 0; i < b.N; i++ {
		y = doCopy(existing, true, true)
	}
}

Benchmark result:

BenchmarkAppend-4                    	  500000	      2118 ns/op
BenchmarkAppendAlloc-4               	 1000000	      2254 ns/op
BenchmarkAppendAllocInline-4         	 1000000	      2260 ns/op
BenchmarkAppendAllocInlineNoFunc-4   	 1000000	      2259 ns/op
BenchmarkCopy-4                      	 1000000	      2264 ns/op

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment