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:
Bexanderthebex 2022-06-01 11:50:57 +08:00 committed by GitHub
parent f4e9904b02
commit d01de7232b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 1 deletions

View File

@ -237,6 +237,7 @@ func Scan(rows Rows, db *DB, mode ScanMode) {
switch reflectValue.Kind() {
case reflect.Slice, reflect.Array:
var elem reflect.Value
recyclableStruct := reflect.New(reflectValueType)
if !update || reflectValue.Len() == 0 {
update = false
@ -261,7 +262,11 @@ func Scan(rows Rows, db *DB, mode ScanMode) {
}
}
} else {
elem = reflect.New(reflectValueType)
if isPtr && db.RowsAffected > 0 {
elem = reflect.New(reflectValueType)
} else {
elem = recyclableStruct
}
}
db.scanIntoStruct(rows, elem, values, fields, joinFields)

View File

@ -1,6 +1,7 @@
package tests_test
import (
"fmt"
"testing"
. "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) {
user := *GetUser("find", Config{})
DB.Create(&user)