Skip to content

Instantly share code, notes, and snippets.

@achille-roussel
Created July 25, 2019 02:26
Show Gist options
  • Save achille-roussel/f6fc908f4336e3d6160134e63a4bb620 to your computer and use it in GitHub Desktop.
Save achille-roussel/f6fc908f4336e3d6160134e63a4bb620 to your computer and use it in GitHub Desktop.
Benchmark: Static vs Reflect vs Unsafe
goos: darwin
goarch: amd64
BenchmarkAppend/Static 50000000 24.5 ns/op
BenchmarkAppend/Reflect 20000000 72.1 ns/op
BenchmarkAppend/Unsafe 30000000 43.7 ns/op
PASS
ok _/tmp 4.135s
package main
import (
"reflect"
"testing"
"unsafe"
)
type T struct {
A int8
B int16
C int32
D int64
E string
F string
G string
H string
}
func (t *T) AppendStatic(b []byte) []byte {
b = appendInt8(b, t.A)
b = appendInt16(b, t.B)
b = appendInt32(b, t.C)
b = appendInt64(b, t.D)
b = appendString(b, t.E)
b = appendString(b, t.F)
b = appendString(b, t.G)
b = appendString(b, t.H)
return b
}
func appendInt8(b []byte, v int8) []byte {
return append(b, byte(v))
}
func appendInt16(b []byte, v int16) []byte {
return append(b, byte(v), byte(v>>8))
}
func appendInt32(b []byte, v int32) []byte {
return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24))
}
func appendInt64(b []byte, v int64) []byte {
return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24), byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56))
}
func appendString(b []byte, s string) []byte {
b = appendInt32(b, int32(len(s)))
b = append(b, s...)
return b
}
func (t *T) AppendReflect(b []byte) []byte {
v := reflect.ValueOf(t).Elem()
n := v.NumField()
for i := 0; i < n; i++ {
f := v.Field(i)
switch f.Kind() {
case reflect.Int8:
b = appendInt8(b, int8(f.Int()))
case reflect.Int16:
b = appendInt16(b, int16(f.Int()))
case reflect.Int32:
b = appendInt32(b, int32(f.Int()))
case reflect.Int64:
b = appendInt64(b, int64(f.Int()))
case reflect.String:
b = appendString(b, f.String())
default:
panic("unsupported type")
}
}
return b
}
func (t *T) AppendUnsafe(b []byte) []byte {
p := unsafe.Pointer(t)
for i := range appendFuncUnsafe {
f := &appendFuncUnsafe[i]
b = f.append(b, unsafe.Pointer(uintptr(p)+f.offset))
}
return b
}
func appendInt8Unsafe(b []byte, p unsafe.Pointer) []byte {
return append(b, byte(*(*int8)(p)))
}
func appendInt16Unsafe(b []byte, p unsafe.Pointer) []byte {
v := uint16(*(*int16)(p))
return append(b, byte(v), byte(v>>8))
}
func appendInt32Unsafe(b []byte, p unsafe.Pointer) []byte {
v := uint32(*(*int32)(p))
return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24))
}
func appendInt64Unsafe(b []byte, p unsafe.Pointer) []byte {
v := uint64(*(*int64)(p))
return append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24), byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56))
}
func appendStringUnsafe(b []byte, p unsafe.Pointer) []byte {
s := *(*string)(p)
b = appendInt32(b, int32(len(s)))
b = append(b, s...)
return b
}
type appendOffset struct {
append func([]byte, unsafe.Pointer) []byte
offset uintptr
}
func offsetOf(i int) uintptr {
return reflect.TypeOf(T{}).Field(i).Offset
}
var appendFuncUnsafe = []appendOffset{
{appendInt8Unsafe, offsetOf(0)},
{appendInt16Unsafe, offsetOf(1)},
{appendInt32Unsafe, offsetOf(2)},
{appendInt64Unsafe, offsetOf(3)},
{appendStringUnsafe, offsetOf(4)},
{appendStringUnsafe, offsetOf(5)},
{appendStringUnsafe, offsetOf(6)},
{appendStringUnsafe, offsetOf(7)},
}
func BenchmarkAppend(b *testing.B) {
b.Run("Static", func(b *testing.B) {
benchmarkAppend(b, (*T).AppendStatic)
})
b.Run("Reflect", func(b *testing.B) {
benchmarkAppend(b, (*T).AppendReflect)
})
b.Run("Unsafe", func(b *testing.B) {
benchmarkAppend(b, (*T).AppendUnsafe)
})
}
var sink []byte
func benchmarkAppend(b *testing.B, f func(*T, []byte) []byte) {
var a [256]byte
var t = T{
E: "Hello World!",
F: "1234567890",
G: "qwertyuiopasdfghjklzxcvbnm",
}
for i := 0; i < b.N; i++ {
sink = f(&t, a[:0])
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment