Created
July 25, 2019 02:26
-
-
Save achille-roussel/f6fc908f4336e3d6160134e63a4bb620 to your computer and use it in GitHub Desktop.
Benchmark: Static vs Reflect vs Unsafe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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