return generic slice from a function

104 views
Skip to first unread message

amarjeeta...@gmail.com

unread,
May 30, 2020, 5:45:28 AM5/30/20
to golang-nuts
Hi

I have a db package, which needs to return a slice of struct(of user,order etc type).
In the user pkg, I want the slice of user. To achieve this, I have tried 4 ways, As shown below. 


Attempt 1: Create a callback func using reflection 

create the user obj & callBack func using reflection & call the callback func with new doc as parameter. This way looks clean to use in user pkg but using reflection is costly. (please see the benchmark @ bottom of this mail)
package db

func QueryResults_CallBack_ReflectionFunc(callBackFuncI interface{}) error {
   
defer client.Close()

   
funcType := reflect.TypeOf(callBackFuncI)
   
docType := funcType.In(0).Elem()
   callBackFunc := reflect.ValueOf(callBackFuncI)
   
arg := make([]reflect.Value, 1)

   
doc := reflect.New(docType)
   
for client.HasMore() {
     
if err := client.ReadDocument(doc.Interface()); err != nil {
         
return err
      }
     
arg[0] = doc
      callBackFunc.Call(arg)
   
}

   
return nil
}


and at the user pkg side just pass a call back function like...
package user

func
GetUsers_CallBack_ReflectionFunc() []User {
   
users := make([]User, 0, 5)
   
db.QueryResults_CallBack_ReflectionFunc(func(user *User) {
     
users = append(users, *user)
   
})
   
return users
}




Attempt 2: Pass the callback func & user object 

Pass the callback func & user object  from user to db pkg, so that, the db pkg doesn't have to create one using reflection, thus saving cost of using reflection
package db

func QueryResults_CallBack_ObjNFunc(doc interface{}, callBackFunc func(interface{})) error {
   
defer client.Close()

   
for client.HasMore() {
     
if err := client.ReadDocument(doc); err != nil {
         
return err
      }
     
callBackFunc(doc)
   
}
   
return nil
}



and in user package it can be used as -
package user

func GetUsers_CallBack_ObjNFunc() []User {
   
users := make([]User, 0, 5)
   
db.QueryResults_CallBack_ObjNFunc(&User{}, func(userI interface{}) {
     
u := userI.(*User)
     
users = append(users, *u)
   
})
   
return users
}


Attempt 3: Pass the db client in a callback func
package db

func QueryResults_CallBack_DBClient(callBackFunc func(DbClient) error) error {
   
defer client.Close()

   
for client.HasMore() {
     
if err := callBackFunc(client); err != nil {
         
return err
      }
   
}
   
return nil
}


and in user pkg, it can be used as - 
package user

func GetUsers_CallBack_DBClient() []User {
   
users := make([]User, 0, 5)
   
db.QueryResults_CallBack_DBClient(func(client db.DbClient) error {
     
user := User{}
     
if err := client.ReadDocument(&user); err != nil {
         
return err
      }
     
users = append(users, user)
     
return nil
   })

   
return users
}




Attempt 4: Expose the db client

package db

func QueryResults_ExposeClient() DbClient {
   
return client
}


and in user package, handle the read document logic like...
package user

func GetUsers_ExposeClient() []User {
   
users := make([]User, 0, 5)

   
client := db.QueryResults_ExposeClient()
   
for client.HasMore() {
     
user := User{}
     
client.ReadDocument(&user)
     
users = append(users, user)
   
}
   
client.Close() // responsibility of caller to close the client

   return users
}


Benchmark

~/go/src/testReflection master* 5s go test ./user  -cpuprofile cpu.prof -memprofile mem.prof -bench='BenchmarkGetUser*' -run=^a -benchmem

BenchmarkGetUsers_CallBackReflectionFunc-12         4426987               263 ns/op             432 B/op          4 allocs/op

BenchmarkGetUsers_CallBack_ObjNFunc-12              6293131               186 ns/op             384 B/op          2 allocs/op

BenchmarkGetUsers_CallBackClient-12                 7858377               147 ns/op             320 B/op          1 allocs/op

BenchmarkGetUsers_ExposeClient-12                   10273468              106 ns/op             320 B/op          1 allocs/op



PProf svg 


Source Code



Based on the performance which approach would you suggest? 
Or any optimisation you can suggest for CallBack_ReflectionFunc()?
Or maybe you can purpose a better way? 
 



Robert Engels

unread,
May 30, 2020, 8:53:35 AM5/30/20
to amarjeeta...@gmail.com, golang-nuts
You are dealing in double digit nano differences on an IO call - it makes no difference. Use whichever code is easier to work with and understand. 

On May 30, 2020, at 4:45 AM, amarjeeta...@gmail.com wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/2570eb2e-1b46-4c3a-8792-8064acd7e5db%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages