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 
https://raw.githubusercontent.com/amarjeetanandsingh/go_reflection_test/master/pprof_cpu.svg
https://raw.githubusercontent.com/amarjeetanandsingh/go_reflection_test/master/pprof_mem.svg


Source Code
You can find the code at 
https://github.com/amarjeetanandsingh/go_reflection_test



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? 
 



-- 
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+unsubscr...@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 via email to