io: optimize BinReader.ReadArray()

reflect.MethodByName is a rather expensive function especially when
called on hot path. This became obvious during profiling of db restore.
This commit replaces reflection with a cast to an interface.
This commit is contained in:
Evgenii Stratonikov 2019-12-06 18:13:52 +03:00
parent e4d821f32d
commit 1784a14148
3 changed files with 18 additions and 33 deletions

View file

@ -47,19 +47,14 @@ func (r *BinReader) ReadArray(t interface{}, maxSize ...int) {
panic(value.Type().String() + " is not a pointer to a slice")
}
sliceType := value.Elem().Type()
elemType := sliceType.Elem()
isPtr := elemType.Kind() == reflect.Ptr
if isPtr {
checkHasDecodeBinary(elemType)
} else {
checkHasDecodeBinary(reflect.PtrTo(elemType))
}
if r.Err != nil {
return
}
sliceType := value.Elem().Type()
elemType := sliceType.Elem()
isPtr := elemType.Kind() == reflect.Ptr
ms := maxArraySize
if len(maxSize) != 0 {
ms = maxSize[0]
@ -82,27 +77,18 @@ func (r *BinReader) ReadArray(t interface{}, maxSize ...int) {
} else {
elem = arr.Index(i).Addr()
}
method := elem.MethodByName("DecodeBinary")
method.Call([]reflect.Value{reflect.ValueOf(r)})
el, ok := elem.Interface().(decodable)
if !ok {
panic(elemType.String() + "is not decodable")
}
el.DecodeBinary(r)
}
value.Elem().Set(arr)
}
func checkHasDecodeBinary(v reflect.Type) {
method, ok := v.MethodByName("DecodeBinary")
if !ok || !isDecodeBinaryMethod(method) {
panic(v.String() + " does not have DecodeBinary(*io.BinReader)")
}
}
func isDecodeBinaryMethod(method reflect.Method) bool {
t := method.Type
return t != nil &&
t.NumIn() == 2 && t.In(1) == reflect.TypeOf((*BinReader)(nil)) &&
t.NumOut() == 0
}
// ReadBE reads from the underlying io.Reader
// into the interface v in big-endian format.
func (r *BinReader) ReadBE(v interface{}) {