Skip to content

Instantly share code, notes, and snippets.

@fmstephe
Last active August 23, 2017 10:09
Show Gist options
  • Save fmstephe/f0eb393c4ec41940741376ab08cbdf7e to your computer and use it in GitHub Desktop.
Save fmstephe/f0eb393c4ec41940741376ab08cbdf7e to your computer and use it in GitHub Desktop.
Subtle allocation behaviour converting from string to byte
package main
import "testing"
var bytesHole []byte
var intHole int
// This tests a compiler optimisation described very clearly in this PR
// https://go-review.googlesource.com/c/go/+/3120
func BenchmarkStringBytes32(b *testing.B) {
str := makeString(32)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
convertToBytes(str)
}
}
func BenchmarkStringBytes33(b *testing.B) {
str := makeString(33)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
convertToBytes(str)
}
}
func BenchmarkStringBytes32Count(b *testing.B) {
str := makeString(32)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
countStr(str)
}
}
func BenchmarkStringBytes33Count(b *testing.B) {
str := makeString(33)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
countStr(str)
}
}
func makeString(length int) string {
bs := []byte{}
for i := 0; i < length; i++ {
bs = append(bs, byte(i))
}
return string(bs)
}
// Here the []byte escapes, and will always be allocated to the heap regardless of size
//go:noinline
func convertToBytes(s string) {
bytesHole = []byte(s)
}
// Here the []byte does not escape, and can be stack allocated if it's <= 32 bytes
//go:noinline
func countStr(s string) {
count([]byte(s))
}
//go:noinline
func count(bs []byte) {
for _, b := range bs {
intHole += int(b)
}
}
/*
BenchmarkStringBytes32-4 30000000 47.1 ns/op 32 B/op 1 allocs/op
BenchmarkStringBytes33-4 30000000 54.1 ns/op 48 B/op 1 allocs/op
BenchmarkStringBytes32Count-4 20000000 68.2 ns/op 0 B/op 0 allocs/op
BenchmarkStringBytes33Count-4 10000000 110 ns/op 48 B/op 1 allocs/op
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment