Three new books, Go Optimizations 101, Go Details & Tips 101 and Go Generics 101 are published now. It is most cost-effective to buy all of them through this book bundle in the Leanpub book store.

How to dirtily make a byte slice (without zeroing its byte elements)?

Prior to Go 1.21, there is no way to achieve this, even if with unsafe ways.

Since Go 1.21, the implmentation of strings.Builder.Grow calls an internal bytealg.MakeNoZero function instead of the built-in make function the old implementation called. For most cases, the built-in make function will zero the elements of the result slice, so it is often comparatively slower.

With the 1.21+ implementation, now we get a chance to create byte slices without initializing their elements to zero (though achieving this functionality requires the use of unsafe functions).

import (
	"strings"
	"unsafe"
)

func MakeDirtyByteSlice(n int) []byte {
	var b strings.Builder
	b.Grow(n)
	var p = unsafe.StringData(b.String())
	return unsafe.Slice(p, n)
}

However, for an unknown reason (which should be releted to the official compiler/runtime implementation), benchmark results show that the function is not as faster as expected when the length of the created byte slice is not large.

package ttt

import (
	"strings"
	"testing"
	"unsafe"
)

const Len = 20000

func init() {
	println("=============== Len =", Len)
}

func MakeDirtyByteSlice(n int) []byte {
	var b strings.Builder
	b.Grow(n)
	var p = unsafe.StringData(b.String())
	return unsafe.Slice(p, n)
}

func Benchmark_MakeDirtyByteSlice(tb *testing.B) {
	for range tb.N {
		r = MakeDirtyByteSlice(Len)
	}
}

func Benchmark_make(tb *testing.B) {
	for range tb.N {
		r = make([]byte, Len)
	}
}

func MakeByteSlice(initialValues ...[]byte) []byte {
	n := 0
	for i := range initialValues {
		n += len(initialValues[i])
	}
	n, r := 0, MakeDirtyByteSlice(n)
	for _, s := range initialValues {
		n += copy(r[n:], s)
	}
	return r
}

const N = Len / 4

var x = []byte(strings.Repeat("x", N))
var y = []byte(strings.Repeat("y", N))
var z = []byte(strings.Repeat("z", N))
var w = []byte(strings.Repeat("z", N))
var r []byte

func Benchmark_MakeByteSlice(tb *testing.B) {
	for range tb.N {
		r = MakeByteSlice(x, y, z, w)
	}
}

func Benchmark_make_append(tb *testing.B) {
	for range tb.N {
		r = make([]byte, 0, Len)
		r = append(r, x...)
		r = append(r, y...)
		r = append(r, z...)
		r = append(r, w...)
	}
}

Benchmark results:
$ gotv . test -bench=. -benchmem
[Run]: $HOME/.cache/gotv/tag_go1.23rc1/bin/go test -bench=. -benchmem
=============== Len = 4000
goos: linux
goarch: amd64
pkg: example.com
cpu: Intel(R) Core(TM) i5-4210U CPU @ 1.70GHz
Benchmark_MakeDirtyByteSlice-4   	 1752531	       669.5 ns/op	    4096 B/op	       1 allocs/op
Benchmark_make-4                 	 1268131	       940.3 ns/op	    4096 B/op	       1 allocs/op
Benchmark_MakeByteSlice-4        	  913239	      1119 ns/op	    4096 B/op	       1 allocs/op
Benchmark_make_append-4          	 1142342	      1046 ns/op	    4096 B/op	       1 allocs/op

$ gotv . test -bench=. -benchmem
[Run]: $HOME/.cache/gotv/tag_go1.23rc1/bin/go test -bench=. -benchmem
=============== Len = 20000
goos: linux
goarch: amd64
pkg: example.com
cpu: Intel(R) Core(TM) i5-4210U CPU @ 1.70GHz
Benchmark_MakeDirtyByteSlice-4   	  628150	      1863 ns/op	   20480 B/op	       1 allocs/op
Benchmark_make-4                 	  320997	      3404 ns/op	   20480 B/op	       1 allocs/op
Benchmark_MakeByteSlice-4        	  262684	      4213 ns/op	   20480 B/op	       1 allocs/op
Benchmark_make_append-4          	  246081	      4525 ns/op	   20480 B/op	       1 allocs/op


(more articles ↡)

The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.

If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.

Tapir, the author of Go 101, has been on writing the Go 101 series books and maintaining the go101.org website since 2016 July. New contents will be continually added to the book and the website from time to time. Tapir is also an indie game developer. You can also support Go 101 by playing Tapir's games (made for both Android and iPhone/iPad):
Individual donations via PayPal are also welcome.

Articles in this book: