forked from mirror/gorm
enhancement: Avoid calling reflect.New() when passing in slice of values to `Scan()` (#5388)
* fix: reduce allocations when slice of values * chore[test]: Add benchmark for scan * chore[test]: add bench for scan slice * chore[test]: add bench for slice pointer and improve tests * chore[test]: make sure database is empty when doing slice tests * fix[test]: correct sql delete statement * enhancement: skip new if rows affected = 0
This commit is contained in:
parent
f4e9904b02
commit
d01de7232b
7
scan.go
7
scan.go
|
@ -237,6 +237,7 @@ func Scan(rows Rows, db *DB, mode ScanMode) {
|
||||||
switch reflectValue.Kind() {
|
switch reflectValue.Kind() {
|
||||||
case reflect.Slice, reflect.Array:
|
case reflect.Slice, reflect.Array:
|
||||||
var elem reflect.Value
|
var elem reflect.Value
|
||||||
|
recyclableStruct := reflect.New(reflectValueType)
|
||||||
|
|
||||||
if !update || reflectValue.Len() == 0 {
|
if !update || reflectValue.Len() == 0 {
|
||||||
update = false
|
update = false
|
||||||
|
@ -261,7 +262,11 @@ func Scan(rows Rows, db *DB, mode ScanMode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
elem = reflect.New(reflectValueType)
|
if isPtr && db.RowsAffected > 0 {
|
||||||
|
elem = reflect.New(reflectValueType)
|
||||||
|
} else {
|
||||||
|
elem = recyclableStruct
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db.scanIntoStruct(rows, elem, values, fields, joinFields)
|
db.scanIntoStruct(rows, elem, values, fields, joinFields)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package tests_test
|
package tests_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "gorm.io/gorm/utils/tests"
|
. "gorm.io/gorm/utils/tests"
|
||||||
|
@ -24,6 +25,45 @@ func BenchmarkFind(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkScan(b *testing.B) {
|
||||||
|
user := *GetUser("scan", Config{})
|
||||||
|
DB.Create(&user)
|
||||||
|
|
||||||
|
var u User
|
||||||
|
b.ResetTimer()
|
||||||
|
for x := 0; x < b.N; x++ {
|
||||||
|
DB.Raw("select * from users where id = ?", user.ID).Scan(&u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkScanSlice(b *testing.B) {
|
||||||
|
DB.Exec("delete from users")
|
||||||
|
for i := 0; i < 10_000; i++ {
|
||||||
|
user := *GetUser(fmt.Sprintf("scan-%d", i), Config{})
|
||||||
|
DB.Create(&user)
|
||||||
|
}
|
||||||
|
|
||||||
|
var u []User
|
||||||
|
b.ResetTimer()
|
||||||
|
for x := 0; x < b.N; x++ {
|
||||||
|
DB.Raw("select * from users").Scan(&u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkScanSlicePointer(b *testing.B) {
|
||||||
|
DB.Exec("delete from users")
|
||||||
|
for i := 0; i < 10_000; i++ {
|
||||||
|
user := *GetUser(fmt.Sprintf("scan-%d", i), Config{})
|
||||||
|
DB.Create(&user)
|
||||||
|
}
|
||||||
|
|
||||||
|
var u []*User
|
||||||
|
b.ResetTimer()
|
||||||
|
for x := 0; x < b.N; x++ {
|
||||||
|
DB.Raw("select * from users").Scan(&u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkUpdate(b *testing.B) {
|
func BenchmarkUpdate(b *testing.B) {
|
||||||
user := *GetUser("find", Config{})
|
user := *GetUser("find", Config{})
|
||||||
DB.Create(&user)
|
DB.Create(&user)
|
||||||
|
|
Loading…
Reference in New Issue