Slices: make vs append
- Introduction
- Set up benchmarking
- make with size=0 capacity=n
- make with size=n capacity=n
- Shoot-out
- Verdict
- References
Introduction
An idiom to create slices and maps is to use make whose signature is
func make(t Type, size ...IntegerType) Type
which by interpretation says the second argument size
defines the size of the slice, while an optional extra value
is to use the alternative capacity that MUST NOT BE LESS THAN THE SIZE
Thus bringing us to this signature:
make(<T>, size, capacity)
In this performance clinic series, we shall focus on slices firstly!
Slices are the most common representation of items in a collection
that can be iterated over, used collectively and searched.
The million dollar question when creating the very common slice is: what’s the most performant way of creating a slice? make([]<T>, n)
? make([]<T>, 0, n)
? or a plain append()
?
<a class=“btn btn-light " onclick=“location.href='/starting'”
Our initial program will start off as a simple function whose purpose is to create a slice of certain size and return it:
func makeIt(n int) (data []int) {
for i := 0; i < n; i++ {
data = append(data, i)
}
return data
}
Set up benchmarking
To set up our shoot-out for benchmarking and solid data backed by numbers, we need to do the following:
Benchmark the code
To figure out how it performs, let’s start by benchmarking it.
package makestyles
import "testing"
var sink interface{}
func benchmarkMake(b *testing.B, size int) {
for i := 0; i < b.N; i++ {
sink = makeIt(size)
}
if sink == nil {
b.Fatal("Benchmark didn't run")
}
}
func BenchmarkMake10(b *testing.B) {
benchmarkMake(b, 10)
}
func BenchmarkMake100(b *testing.B) {
benchmarkMake(b, 100)
}
func BenchmarkMake1000(b *testing.B) {
benchmarkMake(b, 1000)
}
func BenchmarkMake10000(b *testing.B) {
benchmarkMake(b, 10000)
}
func BenchmarkMake100000(b *testing.B) {
benchmarkMake(b, 100000)
}
func BenchmarkMake1000000(b *testing.B) {
benchmarkMake(b, 1000000)
}
Commit the result
Having successfully added Bencher to our repository in two clicks, we mailed the first change. That made Bencher spawn the following:
make with size=0 capacity=n
A common pattern is to invoke make([]T, 0, n)
but the question here is: is it performant? How much better?
To find out these answers, we create a branch perhaps called make-with-capacity-n
and then make this modification:
diff --git a/makestyles/bench_test.go b/makestyles/bench_test.go
index 7239d97..5e8305e 100644
--- a/makestyles/bench_test.go
+++ b/makestyles/bench_test.go
@@ -3,6 +3,7 @@ package makestyles
import "testing"
func makeIt(n int) (data []int) {
+ data = make([]int, 0, n)
for i := 0; i < n; i++ {
data = append(data, i)
}
Results
After Bencher runs, we can look at our Github check:
which when clicked on demonstrates the results:
and then navigate to the Bencher details URL which looks like:
We now know what’s up! That actually improved things significantly!!
However, is that all that we can do? We haven’t exhausted the traditional ways of making slices.
make with size=n capacity=n
To use this pattern, we can invoke make([]T, n)
and then use an index over each slice’s item. Is it performant? How much better?
diff --git a/makestyles/bench_test.go b/makestyles/bench_test.go
index 7239d97..3058f3e 100644
--- a/makestyles/bench_test.go
+++ b/makestyles/bench_test.go
@@ -3,6 +3,7 @@ package makestyles
import "testing"
func makeIt(n int) (data []int) {
+ data = make([]int, n)
for i := 0; i < n; i++ {
data[i] = i
}
Results
After Bencher runs, we can look at our Github checks:
which when clicked on demonstrates the results:
and then if we navigate to the Bencher details URL which looks like:
Shoot-out
If we compare the results side by side, we can see the following:
Verdict
In this instance make([]<T>, size)
is more performant than make([]<T>, 0, size)
which is more performant than just a plain append
in almost every single dimension that matters.
References
Resource | URL |
---|---|
append Go standard library | https://golang.org/pkg/builtin#make |
Bencher website | https://bencher.orijtech.com/ |
Bencher on the Github marketplace | https://github.com/marketplace/gobencher/ |