Groovy collections performance considerations regarding space/time
Asked Answered
A

1

8

What is the performance of Groovys collection methods (regarding space(!) and time) in comparison to plain Java for-loops?

Eg for this use cases:

  • sum() vs. for-loop with variable
  • each() vs. for-loop with variable
  • inject() vs. for-loop with variable
  • collect() vs. for-loop with temporary collection
  • findAll() vs. for-loop with temporary collection
  • find() vs. for-loop with variable

So, considering those results, is it advisable to use for-loops over Groovy-collection-methods in critical environments (eg. Grails-WebApp)? Are there resources regarding Groovy/Grails performance (optimization)?


Using this GBench test I got the following results for CPU-time:

                 user  system      cpu     real

forLoop       2578777      67  2578844  2629592
forEachLoop   2027941      47  2027988  2054320
groovySum     3946137      91  3946228  3958705
groovyEach    4703000       0  4703000  4703000
groovyInject  4280792     108  4280900  4352287



import groovyx.gbench.BenchmarkBuilder

def testSize = 10000
def testSet = (0..testSize) as Set


def bm = new BenchmarkBuilder().run {

'forLoop' {
    def n = 0
    for(int i = 0; i<testSize; i++) {
        n += i
    }
    return n
}

'forEachLoop' {
    def n = 0
    for(int i in testSet) {
        n += i
    }
    return n
}

'groovySum' {
    def n = testSet.sum()
    return n
}

'groovyEach' {
    def n = 0
    testSet.each { n + it }
    return n
}

'groovyInject' {
    def n = testSet.inject(0) { el, sum -> sum + el }
    return n
}
}
bm.prettyPrint()
Angwantibo answered 3/6, 2014 at 7:45 Comment(0)
T
16

Interesting benchmark. No surprise that sum() is slower. Here's how implementation looks like:

private static Object sum(Iterable self, Object initialValue, boolean first) {
        Object result = initialValue;
        Object[] param = new Object[1];
        for (Object next : self) {
            param[0] = next;
            if (first) {
                result = param[0];
                first = false;
                continue;
            }
            MetaClass metaClass = InvokerHelper.getMetaClass(result);
            result = metaClass.invokeMethod(result, "plus", param);
        }
        return result;
}

As You can see it must be generic and uses meta programming. The result is bigger time cost.

The results of the benchmark You pasted are clear and pretty self descriptive. If You really need better performance it seems that better idea is to use for loops.

Thundershower answered 3/6, 2014 at 8:17 Comment(2)
+1 for "If you really need better performance". Code performance shouldn't be the main concern when on, for example, IO intensive operations or DB driven systems. Performance zealots should've woked up already.Jacobjacoba
Amen. If it performs 'good enough' and is easily read and maintained, that is better than faster but noone understands it. Don't start heading towards 0's and 1's until you need it.Interpreter

© 2022 - 2024 — McMap. All rights reserved.