Skip to content

Commit

Permalink
add benchmarks showing that numeric offsets for field lookups perform…
Browse files Browse the repository at this point in the history
… much better
  • Loading branch information
jmoiron committed May 16, 2014
1 parent 214c7f3 commit 8aa2977
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 29 deletions.
44 changes: 18 additions & 26 deletions reflect/reflect.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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{}
Expand All @@ -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
Expand All @@ -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])
}
84 changes: 81 additions & 3 deletions reflect/reflect_test.go
Original file line number Diff line number Diff line change
@@ -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)
}

Expand Down Expand Up @@ -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.")
}
}
}

0 comments on commit 8aa2977

Please sign in to comment.