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() { | ||||
| 		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) | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Bexanderthebex
						Bexanderthebex