Koazee vs Go-Linq vs Go-Funk

293 views
Skip to first unread message

Iván Corrales Solera

unread,
Dec 4, 2018, 2:49:36 AM12/4/18
to golang-nuts
Hey all,

I am working on Koazee, a library to deal with slices in a functional way. Since I published the very first release I was asked me for publishing a benchmark comparison with other existing and matured frameworks. that provide similar functionality. 

I hope you find useful this article:  Koazee vs Go-Linq vs Go-Funk 

Iván Corrales Solera

unread,
Dec 4, 2018, 2:51:59 AM12/4/18
to golang-nuts



comparison.png


In the above graph we can observe the result after running the implemented benchmark tests for these 3 frameworks

Marko Ristin-Kaufmann

unread,
Dec 4, 2018, 3:05:52 AM12/4/18
to Iván Corrales Solera, golang-nuts
Thanks, Ivan! These numbers are very helpful! Could you at least give us a hint why your library is faster than the other two? As far as I can see, all three libraries in the benchmark use reflection.

--
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.
For more options, visit https://groups.google.com/d/optout.

Iván Corrales Solera

unread,
Dec 4, 2018, 6:27:14 AM12/4/18
to golang-nuts
Thanks for reply Marko,

Sure!  I think the approach of making validation smarter and generating code for primitive types was the key. 

Actually when you ask me for benchmark the first time, the performance was terrible! 

If we have a look at how operation Filter is implemented:

Filter operation
We find 3 files (this is basically the same for all the operations)

In the filter.go, my input is a reflection type and obviously I am required to do a validation in order to avoid an uncontrolled panic

I guess my validation function is smarter than other libraries, Why? Because of I cache some info


func (op *Filter) validate() (*filterInfo, *errors.Error) {
item := &filterInfo{}
fnType := reflect.TypeOf(op.Func)
if val := cache.get(op.ItemsType, fnType); val != nil {
return val, nil
//...... Validations for input
item.fnInputType = fnIn.Type()
cache.add(op.ItemsType, fnType, item)
return item, nil
}

By this "smart cache" I avoid to evaluate eternally the same things. I mean,
If my stream contains string's and the filter func looks like func(string)bool I only evaluate once 
because the output will be always the same

The dispatcher.go has been generated. This has been generated from repository koazee-gen

What do I achieve with this generated code? That my operation are applied over a function that will work with primitive values instead of doing it with reflection
For example,

func filterString(itemsValue reflect.Value, function interface{}) interface{} {
input := itemsValue.Interface().([]string)
output := make([]string, 0)
fn := function.(func(string) bool)
for i := 0; i < len(input); i++ {
if fn(input[i]) {
output = append(output, input[i])
}
}
return output
}

Since I have a function for any primitive type (or pointers too) the performance doesn't suffer the reflection cost


I've also put a lot of effort to find the best way to do any operation with the best performance. 

For example, for reverse operation  I could do

func reverseInt16Ptr(itemsValue reflect.Value) interface{}{
input := itemsValue.Interface().([]*int16)
len := len(input)
output := make([]*int16, len)
for index := 0; index < len(input); index++ {
output[index]= input[len-1-index]
}
return output
}


but my generated function looks like below

func reverseInt16Ptr(itemsValue reflect.Value) interface{}{
input := itemsValue.Interface().([]*int16)
len := len(input)
output := make([]*int16, len)
for index := 0; index < (len/2)+1; index++ {
output[index], output[len-1-index] = input[len-1-index], input[index]
}
return output
}


I think is the best approach. I reduce a half the time of elements to be evaluated

I am learning a lot of Go (and enjoying too)  with all your requests and suggestions and to be honest is so valuable when you achieve good results.


On roadmap , I would like to publish a richer set of operations for v0.0.3 (I know my provided set of operations is poor)  and for v0.0.4  find a way to improve the performance for complex structs (I got a idea..)

Iván Corrales Solera

unread,
Dec 4, 2018, 6:29:00 AM12/4/18
to golang-nuts
I would also state that Koazee is the only one that doesn't force you to cast data in your filter functions nor sort... because I understand the argument as an interface

instead of expect a function with arguments as the other 2 libraries do.

Robert Engels

unread,
Dec 4, 2018, 9:34:16 AM12/4/18
to Iván Corrales Solera, golang-nuts
Shouldn’t this code be index = index+ 2

for index := 0; index < (len/2)+1; index++ {
output[index], output[len-1-index] = input[len-1-index], input[index]
}
And the loop needs to go to Len - you are only copying half the elements....

Robert Engels

unread,
Dec 4, 2018, 9:39:53 AM12/4/18
to Iván Corrales Solera, golang-nuts
Sorry, I see it is going from both ends... too early :)
--

Iván Corrales Solera

unread,
Dec 4, 2018, 9:42:30 AM12/4/18
to ren...@ix.netcom.com, golan...@googlegroups.com
No worries! ahaha It's ok
Reply all
Reply to author
Forward
0 new messages