From 8aa29771f8043ff5c3a64923517678ad6755b555 Mon Sep 17 00:00:00 2001 From: Jason Moiron Date: Fri, 16 May 2014 10:45:57 -0400 Subject: [PATCH] add benchmarks showing that numeric offsets for field lookups perform much better --- reflect/reflect.go | 44 +++++++++------------ reflect/reflect_test.go | 84 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 29 deletions(-) diff --git a/reflect/reflect.go b/reflect/reflect.go index 4affa0f4..2a221b93 100644 --- a/reflect/reflect.go +++ b/reflect/reflect.go @@ -1,27 +1,15 @@ package reflect -import "sync" +import ( + "fmt" + "sync" +) import "reflect" type stringMap map[string]string type intMap map[string][]int -var cache = struct { - mapping map[reflect.Type]stringMap - sync.RWMutex -}{} - -var cacheint = struct { - mapping map[reflect.Type]intMap - sync.RWMutex -}{} - -func init() { - cache.mapping = make(map[reflect.Type]stringMap) - cacheint.mapping = make(map[reflect.Type]intMap) -} - // Mapper is a general purpose mapper of names to struct fields. A Mapper // behaves like most marshallers, optionally obeying a field tag for name // mapping and a function to provide a basic mapping of fields to names. @@ -56,6 +44,16 @@ func (m *Mapper) MapType(t reflect.Type) stringMap { return getMapping(t, m.tagName, m.mapFunc) } +func (m *Mapper) FieldMap(v reflect.Value) map[string]reflect.Value { + r := map[string]reflect.Value{} + nm := m.MapType(v.Type()) + fmt.Println(nm) + for tagName, fieldName := range nm { + r[tagName] = v.FieldByName(fieldName) + } + return r +} + func getMapping(t reflect.Type, tagName string, mapFunc func(string) string) stringMap { queue := []reflect.Type{t} m := stringMap{} @@ -69,7 +67,7 @@ func getMapping(t reflect.Type, tagName string, mapFunc func(string) string) str name := f.Tag.Get(tagName) if len(name) == 0 { - name = f.Name + name = mapFunc(f.Name) } // if the name is "-", disabled via a tag, skip it @@ -93,23 +91,17 @@ func getMapping(t reflect.Type, tagName string, mapFunc func(string) string) str continue } // add it to the map at the current position - m[name] = mapFunc(f.Name) + m[name] = f.Name } } return m } -func getMappingInt(reflect.Type) intMap { - return intMap{} -} +func noop(s string) string { return s } func FieldByName(i interface{}, name string) reflect.Value { t := reflect.TypeOf(i) - m, ok := cache.mapping[t] - if !ok { - m = getMapping(t, "db", func(s string) string { return s }) - cache.mapping[t] = m - } + m := getMapping(t, "db", noop) v := reflect.ValueOf(i) return v.FieldByName(m[name]) } diff --git a/reflect/reflect_test.go b/reflect/reflect_test.go index 34a204ed..5713f6aa 100644 --- a/reflect/reflect_test.go +++ b/reflect/reflect_test.go @@ -1,9 +1,11 @@ package reflect -import "testing" -import r "reflect" +import ( + "reflect" + "testing" +) -func ival(v r.Value) int { +func ival(v reflect.Value) int { return v.Interface().(int) } @@ -59,3 +61,79 @@ func TestEmbedded(t *testing.T) { t.Errorf("Expecting %d, got %d", ival(v), z.B) } } + +func TestMapping(t *testing.T) { + type Person struct { + ID int + Name string + WearsGlasses bool `db:"wears_glasses"` + } + + //m := NewMapperFunc("db", strings.ToLower) + //p := Person{1, "Jason", true} +} + +type E1 struct { + A int +} +type E2 struct { + E1 + B int +} +type E3 struct { + E2 + C int +} +type E4 struct { + E3 + D int +} + +func BenchmarkFieldNameL1(b *testing.B) { + e4 := E4{D: 1} + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := v.FieldByName("D") + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +} + +func BenchmarkFieldNameL4(b *testing.B) { + e4 := E4{} + e4.A = 1 + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := v.FieldByName("A") + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +} + +func BenchmarkFieldPosL1(b *testing.B) { + e4 := E4{D: 1} + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := v.Field(1) + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +} + +func BenchmarkFieldPosL4(b *testing.B) { + e4 := E4{} + e4.A = 1 + for i := 0; i < b.N; i++ { + v := reflect.ValueOf(e4) + f := v.Field(0) + f = f.Field(0) + f = f.Field(0) + f = f.Field(0) + if f.Interface().(int) != 1 { + b.Fatal("Wrong value.") + } + } +}