Skip to content

Commit

Permalink
Store fields counts in struct mapping and initialize slices capacity …
Browse files Browse the repository at this point in the history
…to improve performances.
  • Loading branch information
samonzeweb committed May 5, 2018
1 parent ceeb32c commit 18ac3a5
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 22 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
*.swp
.vscode

# Profiling
*.out

# Databases
*.db

Expand Down
44 changes: 34 additions & 10 deletions dbreflect/dbreflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type StructMapping struct {
Name string
structMapping structMappingDetails
opLockSQLName string
fieldCount int
keyCount int
autoCount int
}

// innerStructMapping contains the details of a relation between a struct
Expand Down Expand Up @@ -58,6 +61,7 @@ func NewStructMapping(structInfo reflect.Type) (*StructMapping, error) {
}

sm.Name = sm.structMapping.name
sm.setFieldsCount()

err = sm.setOpLockField()
if err != nil {
Expand Down Expand Up @@ -160,7 +164,7 @@ func (smd *structMappingDetails) newSubStructMapping(structField reflect.StructF
// Optional prefix and relation
var options map[string]string
subStructMapping.prefix, options = smd.tagData(structField.Tag)
if relation,ok := options[optionRelation]; ok {
if relation, ok := options[optionRelation]; ok {
subStructMapping.relation = relation
}

Expand Down Expand Up @@ -194,6 +198,26 @@ func (*structMappingDetails) tagData(tag reflect.StructTag) (string, map[string]
return firstValue, tagMaps
}

// setFieldsCount set all fields count (all, auto, keys)
func (sm *StructMapping) setFieldsCount() {
sm.fieldCount = 0
sm.autoCount = 0
sm.keyCount = 0

f := func(_ string, fieldMapping *fieldMapping, _ *reflect.Value) (stop bool, err error) {
sm.fieldCount++
if fieldMapping.isAuto {
sm.autoCount++
}
if fieldMapping.isKey {
sm.keyCount++
}
return false, nil
}

sm.structMapping.traverseTree("", "", nil, f)
}

// setOpLockField searchs optimistic locking field an update the struct mapping
// with the op lock field data.
// It returns an error if there is more then one op lock field.
Expand Down Expand Up @@ -242,7 +266,7 @@ func isValidNonAutoOpLockFieldType(fieldMapping *fieldMapping) bool {

// GetAllColumnsNames returns the names of all columns.
func (sm *StructMapping) GetAllColumnsNames() []string {
columns := make([]string, 0, 0)
columns := make([]string, 0, sm.fieldCount)

f := func(fullName string, _ *fieldMapping, _ *reflect.Value) (stop bool, err error) {
columns = append(columns, fullName)
Expand All @@ -255,7 +279,7 @@ func (sm *StructMapping) GetAllColumnsNames() []string {

// GetNonAutoColumnsNames returns the names of non auto columns.
func (sm *StructMapping) GetNonAutoColumnsNames() []string {
columns := make([]string, 0, 0)
columns := make([]string, 0, sm.fieldCount-sm.autoCount)

f := func(fullName string, fieldMapping *fieldMapping, _ *reflect.Value) (stop bool, err error) {
if !fieldMapping.isAuto {
Expand All @@ -270,7 +294,7 @@ func (sm *StructMapping) GetNonAutoColumnsNames() []string {

// GetAutoColumnsNames returns the names of auto columns.
func (sm *StructMapping) GetAutoColumnsNames() []string {
columns := make([]string, 0, 0)
columns := make([]string, 0, sm.autoCount)

f := func(fullName string, fieldMapping *fieldMapping, _ *reflect.Value) (stop bool, err error) {
if fieldMapping.isAuto {
Expand All @@ -285,7 +309,7 @@ func (sm *StructMapping) GetAutoColumnsNames() []string {

// GetKeyColumnsNames returns the names of key columns.
func (sm *StructMapping) GetKeyColumnsNames() []string {
columns := make([]string, 0, 0)
columns := make([]string, 0, sm.keyCount)

f := func(fullName string, fieldMapping *fieldMapping, _ *reflect.Value) (stop bool, err error) {
if fieldMapping.isKey {
Expand All @@ -305,7 +329,7 @@ func (sm *StructMapping) GetAllFieldsPointers(s interface{}) []interface{} {
v := reflect.ValueOf(s)
v = reflect.Indirect(v)

pointers := make([]interface{}, 0, 0)
pointers := make([]interface{}, 0, sm.fieldCount)

f := func(fullName string, _ *fieldMapping, value *reflect.Value) (stop bool, err error) {
pointers = append(pointers, value.Addr().Interface())
Expand All @@ -323,7 +347,7 @@ func (sm *StructMapping) GetNonAutoFieldsValues(s interface{}) []interface{} {
v := reflect.ValueOf(s)
v = reflect.Indirect(v)

values := make([]interface{}, 0, 0)
values := make([]interface{}, 0, sm.fieldCount-sm.autoCount)

f := func(fullName string, fieldMapping *fieldMapping, value *reflect.Value) (stop bool, err error) {
if !fieldMapping.isAuto {
Expand All @@ -343,7 +367,7 @@ func (sm *StructMapping) GetKeyFieldsValues(s interface{}) []interface{} {
v := reflect.ValueOf(s)
v = reflect.Indirect(v)

values := make([]interface{}, 0, 0)
values := make([]interface{}, 0, sm.keyCount)

f := func(fullName string, fieldMapping *fieldMapping, value *reflect.Value) (stop bool, err error) {
if fieldMapping.isKey {
Expand Down Expand Up @@ -423,7 +447,7 @@ func (sm *StructMapping) GetAutoFieldsPointers(s interface{}) ([]interface{}, er
v := reflect.ValueOf(s)
v = reflect.Indirect(v)

pointers := make([]interface{}, 0, 0)
pointers := make([]interface{}, 0, sm.autoCount)

f := func(fullName string, fieldMapping *fieldMapping, value *reflect.Value) (stop bool, err error) {
if fieldMapping.isAuto {
Expand Down Expand Up @@ -510,7 +534,7 @@ func (smd *structMappingDetails) traverseTree(relation string, prefix string, st
if relation != "" {
fullName = relation + "." + fullName
}

if startValue != nil {
fieldValue := startValue.FieldByName(fm.name)
stopped, err = f(fullName, &fm, &fieldValue)
Expand Down
28 changes: 20 additions & 8 deletions dbreflect/dbreflect_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"
)

// Record will be used to bench dbreflect (no meaning by itself)
type Record struct {
ID int `db:"id,key,auto"`
Dummy1 string `db:"dummy1"`
Expand All @@ -14,14 +15,25 @@ type Record struct {
Dummy5 string `db:"dummy5"`
}

// go test -bench=GetAllFieldsPointers -run=none ./dbreflect/ -benchtime=10s -cpuprofile cpu.out
// $GOPATH/bin/pprof -http=localhost:7777 cpu.out

// go test -bench=GetAllFieldsPointers -run=nore ./dbreflect/ -benchtime=10s -benchmem -memprofilerate=1 -memprofile mem.out
// $GOPATH/bin/pprof -http=localhost:7777 -alloc_space mem.out

// Escape analysis : go test -gcglags "-m"... (ou go build)
// gcglags informations : go tool compile -help
// Tips to benchmark :
//
// Do a simple bencmark, without profiling :
// go test -bench=GetAllFieldsPointers -run=^$ ./dbreflect/ -benchmem -benchtime=10s
//
// CPU profiling, and analysis using a browser :
// go test -bench=GetAllFieldsPointers -run=^$ ./dbreflect/ -benchtime=10s -cpuprofile cpu.out
// $GOPATH/bin/pprof -http=localhost:7777 cpu.out
//
// Memory profiling, and analysis using a browser :
// go test -bench=GetAllFieldsPointers -run=^$ ./dbreflect/ -benchtime=10s -benchmem -memprofilerate=1 -memprofile mem.out
// $GOPATH/bin/pprof -http=localhost:7777 -alloc_space mem.out
//
// Escape analysis : go test -gcflags="-m -m"...
// go test -gcflags="-m -m" ./dbreflect 2>&1 | grep dbreflect.go
//
// Note : profiling is done here with latest pprof.
// Install it with :
// go get -u github.com/google/pprof

func BenchmarkGetAllFieldsPointers(b *testing.B) {
r := Record{}
Expand Down
20 changes: 16 additions & 4 deletions dbreflect/dbreflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type ComplexStructsWithRelations struct {
// no prefix but a relation
SimpleStruct `db:",rel=firsttable"`
// prefix and relation
Foobar SubStruct `db:"nested_,rel=secondtable"`
Foobar SubStruct `db:"nested_,rel=secondtable"`
}

func TestStructMapping(t *testing.T) {
Expand All @@ -75,11 +75,17 @@ func TestStructMapping(t *testing.T) {
So(structMap.Name, ShouldEndWith, "SimpleStruct")
})

Convey("It store data about all tagged fields ", func() {
Convey("It store data about all tagged fields", func() {
So(len(structMap.GetAllColumnsNames()), ShouldEqual, 2)
So(structMap.GetAllColumnsNames(), ShouldContain, "id")
So(structMap.GetAllColumnsNames(), ShouldContain, "my_text")
})

Convey("It counts fields", func() {
So(structMap.fieldCount, ShouldEqual, 2)
So(structMap.autoCount, ShouldEqual, 1)
So(structMap.keyCount, ShouldEqual, 1)
})
})

Convey("NewStructMapping with a complex struct type with sub-structs", t, func() {
Expand All @@ -96,6 +102,12 @@ func TestStructMapping(t *testing.T) {
So(structMapDetails.subStructMapping[1].prefix, ShouldEqual, "nested_")
So(structMapDetails.subStructMapping[1].structMapping.name, ShouldEndWith, "SubStruct")
})

Convey("It counts fields", func() {
So(structMap.fieldCount, ShouldEqual, 5)
So(structMap.autoCount, ShouldEqual, 1)
So(structMap.keyCount, ShouldEqual, 1)
})
})

Convey("NewStructMapping with nested structs and relations", t, func() {
Expand All @@ -112,7 +124,7 @@ func TestStructMapping(t *testing.T) {
So(structMapDetails.subStructMapping[1].prefix, ShouldEqual, "nested_")
So(structMapDetails.subStructMapping[1].relation, ShouldEndWith, "secondtable")
})
})
})
}

func TestScannableStructs(t *testing.T) {
Expand Down Expand Up @@ -178,7 +190,7 @@ func TestGetAllColumnsNames(t *testing.T) {
So(columns[2], ShouldEqual, "secondtable.nested_foo")
So(columns[3], ShouldEqual, "secondtable.nested_bar")
})
})
})
}

func TestGetNonAutoColumnsNames(t *testing.T) {
Expand Down

0 comments on commit 18ac3a5

Please sign in to comment.