From bf1db1534e608954e7fab6f7d668f62cca0a5f82 Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Sun, 17 Nov 2013 09:50:34 +0800 Subject: [PATCH] Use channel to make gorm faster --- field.go | 5 ++-- gorm_test.go | 4 +-- main.go | 3 +-- model.go | 74 ++++++++++++++++++++++++++++++++-------------------- utils.go | 1 - 5 files changed, 52 insertions(+), 35 deletions(-) diff --git a/field.go b/field.go index 16016c34..6015a815 100644 --- a/field.go +++ b/field.go @@ -13,6 +13,7 @@ type Field struct { Value interface{} model *Model dbName string + isBlank bool isPrimaryKey bool autoCreateTime bool autoUpdateTime bool @@ -23,8 +24,8 @@ type Field struct { structField reflect.StructField } -func (f *Field) isBlank() bool { - return isBlank(f.reflectValue) +func (f *Field) parseBlank() { + f.isBlank = isBlank(f.reflectValue) } func (f *Field) isScanner() bool { diff --git a/gorm_test.go b/gorm_test.go index 54c4c49e..38ec2b54 100644 --- a/gorm_test.go +++ b/gorm_test.go @@ -1365,7 +1365,7 @@ func TestQueryChain(t *testing.T) { } func BenchmarkGorm(b *testing.B) { - b.N = 5000 + b.N = 2000 for x := 0; x < b.N; x++ { e := strconv.Itoa(x) + "benchmark@example.org" email := BigEmail{Email: e, UserAgent: "pc", RegisteredAt: time.Now()} @@ -1388,7 +1388,7 @@ func BenchmarkRawSql(b *testing.B) { update_sql := "UPDATE emails SET email = $1, updated_at = $2 WHERE id = $3" delete_sql := "DELETE FROM orders WHERE id = $1" - b.N = 5000 + b.N = 2000 for x := 0; x < b.N; x++ { var id int64 e := strconv.Itoa(x) + "benchmark@example.org" diff --git a/main.go b/main.go index 89279783..c3b4027b 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ package gorm import ( "database/sql" - "github.com/jinzhu/gorm/dialect" ) @@ -139,7 +138,7 @@ func (s *DB) Delete(value interface{}) *DB { } func (s *DB) Exec(sql string) *DB { - return s.do(nil).exec(sql).db + return s.clone().do(nil).exec(sql).db } func (s *DB) Model(value interface{}) *DB { diff --git a/model.go b/model.go index a008330b..b5cd3d21 100644 --- a/model.go +++ b/model.go @@ -63,47 +63,65 @@ func getStructs(typ reflect.Type) (fs []reflect.StructField) { } func (m *Model) fields(operation string) (fields []*Field) { + if len(m._cache_fields[operation]) > 0 { + return m._cache_fields[operation] + } + indirect_value := m.reflectData() if !indirect_value.IsValid() { return } - for _, filed_struct := range getStructs(indirect_value.Type()) { - var field Field - field.Name = filed_struct.Name - field.dbName = toSnake(filed_struct.Name) - field.isPrimaryKey = m.primaryKeyDb() == field.dbName - value := indirect_value.FieldByName(filed_struct.Name) - field.model = m + structs := getStructs(indirect_value.Type()) + c := make(chan *Field, len(structs)) + for _, field_struct := range structs { + go func(field_struct reflect.StructField, c chan *Field) { + var field Field + field.Name = field_struct.Name + field.dbName = toSnake(field_struct.Name) + field.isPrimaryKey = m.primaryKeyDb() == field.dbName + value := indirect_value.FieldByName(field_struct.Name) + field.model = m - if time_value, is_time := value.Interface().(time.Time); is_time { - field.autoCreateTime = "created_at" == field.dbName - field.autoUpdateTime = "updated_at" == field.dbName + if time_value, is_time := value.Interface().(time.Time); is_time { + field.autoCreateTime = "created_at" == field.dbName + field.autoUpdateTime = "updated_at" == field.dbName - switch operation { - case "create": - if (field.autoCreateTime || field.autoUpdateTime) && time_value.IsZero() { - value.Set(reflect.ValueOf(time.Now())) - } - case "update": - if field.autoUpdateTime { - value.Set(reflect.ValueOf(time.Now())) + switch operation { + case "create": + if (field.autoCreateTime || field.autoUpdateTime) && time_value.IsZero() { + value.Set(reflect.ValueOf(time.Now())) + } + case "update": + if field.autoUpdateTime { + value.Set(reflect.ValueOf(time.Now())) + } } } - } - - field.structField = filed_struct - field.reflectValue = value - field.Value = value.Interface() - field.parseAssociation() - fields = append(fields, &field) + field.structField = field_struct + field.reflectValue = value + field.Value = value.Interface() + field.parseAssociation() + field.parseBlank() + c <- &field + }(field_struct, c) } + + for i := 0; i < len(structs); i++ { + fields = append(fields, <-c) + } + close(c) + + if len(m._cache_fields) == 0 { + m._cache_fields = map[string][]*Field{} + } + m._cache_fields[operation] = fields return } func (m *Model) columnsHasValue(operation string) (fields []*Field) { for _, field := range m.fields(operation) { - if !field.isBlank() { + if !field.isBlank { fields = append(fields, field) } } @@ -236,7 +254,7 @@ func (m *Model) setValueByColumn(name string, value interface{}, out interface{} func (m *Model) beforeAssociations() (fields []*Field) { for _, field := range m.fields("null") { - if field.beforeAssociation && !field.isBlank() { + if field.beforeAssociation && !field.isBlank { fields = append(fields, field) } } @@ -245,7 +263,7 @@ func (m *Model) beforeAssociations() (fields []*Field) { func (m *Model) afterAssociations() (fields []*Field) { for _, field := range m.fields("null") { - if field.afterAssociation && !field.isBlank() { + if field.afterAssociation && !field.isBlank { fields = append(fields, field) } } diff --git a/utils.go b/utils.go index 0d0b48fd..f72dfe16 100644 --- a/utils.go +++ b/utils.go @@ -3,7 +3,6 @@ package gorm import ( "bytes" "database/sql" - "reflect" "strconv" "strings"