6

While playing with different sort algorithms I was surprised that Groovy closures were performing really poorly. I couldn't find a good answer to this so far, so trying my luck here now ;) Why are Groovy closures so much slower than traditional methods?

Here is a simple example that shows the performance difference. It creates two lists with random numbers and sorts them in reverse order, measuring the sorting time. On my machine and for 10k elements it takes 270ms using a closure and only 50ms using the Comparator implementation.

The timings vary a bit based on the distribution of random numbers. Also I tried Groovy 1.7.4 and 1.8.0, seeing slightly better performance with the latter. But the overall picture stays the same: closures perform poorly.

What can I do to improve closure performance? Besides not using closures, of course ;) Am I missing something or should one not use closures in groovy if performance counts?

def numberCount = 10000
def random = new Random()
def unorderedList1 = (1..numberCount).collect{random.nextInt()}
def unorderedList2 = (1..numberCount).collect{random.nextInt()}
def timeit = {String message, Closure cl->
    def startTime = System.currentTimeMillis()
    cl()
    def deltaTime = System.currentTimeMillis() - startTime
    println "$message: \ttime: $deltaTime"
}

timeit("compare using closure") {
    def comparator= [ compare: { a,b -> return b <=> a }] as Comparator
    unorderedList1.sort(comparator)
}

timeit("compare using method") {
    Comparator comparator = new MyComparator()
    unorderedList2.sort(comparator)
}

class MyComparator implements Comparator {
    int compare(a, b) {return b <=> a}
}
Michael Pollmeier
  • 1,370
  • 11
  • 20
  • 1
    270ms v 50ms doesn't fall into the 'magnitudes' of difference for me. If you want to see some smart use of Groovy where it ends up faster than Java or C++ implementations (not intended as flamebait: watch the video first), look here: http://www.infoq.com/presentations/Groovy-Best-Practices – SteveD Jul 04 '11 at 09:05

2 Answers2

5

Just an update with Groovy 2.0.5 with OpenJDK 1.6 (O6) and JDK 1.7 (J7) on Ubuntu.

I also added two possible implementations:

  • only one closure provided as the implementation of Comparator:
  • a method annotated with @CompileStatic:

    def numberCount = 10000
    def random = new Random()
    def unorderedList1 = (1..numberCount).collect{random.nextInt()}
    def unorderedList2 = (1..numberCount).collect{random.nextInt()}
    def unorderedList3 = (1..numberCount).collect{random.nextInt()}
    def unorderedList4 = (1..numberCount).collect{random.nextInt()}
    def timeit = {String message, Closure cl->
        def startTime = System.currentTimeMillis()
        cl()
        def deltaTime = System.currentTimeMillis() - startTime
        println "$message: \ttime: $deltaTime"
    }
    
    timeit("compare using map of closures") {
        def comparator= [ compare: { a,b -> return b <=> a }] as Comparator
        unorderedList1.sort(comparator)
    }
    
    timeit("compare using one closure") {
        def comparator= { a, b -> return b <=> a } as Comparator
        unorderedList2.sort(comparator)
    }
    
    timeit("compare using method") {
        Comparator comparator = new MyComparator()
        unorderedList3.sort(comparator)
    }
    
    timeit("compare using method with @CompileStatic") {
        Comparator comparator = new MyComparator2()
        unorderedList4.sort(comparator)
    }
    
    class MyComparator implements Comparator {
        int compare(a, b) {return b <=> a}
    }
    
    class MyComparator2 implements Comparator<Integer> {
        @groovy.transform.CompileStatic
        int compare(Integer a, Integer b) {return b <=> a}
    }
    
Groovy 2.0.5                groovy  groovyc 
                            O6      O6  J7
closure map                 258     499 146
one closure                 64      205 48
method                      29      37  32
@CompileStatic method       28      26  22

Note that I don't have an install of the Groovy command line compiled with JDK7 (the one I pulled automatically installs its own OpenJDK6), therefore the lack of a corresponding column.

viphe
  • 689
  • 8
  • 16
4

What can I do to improve closure performance?

Wait. JDK7 will add a new instruction, known as invokeDynamic that is expected to radically improve the performance of dynamic languages on the JVM.

Community
  • 1
  • 1
Dónal
  • 185,044
  • 174
  • 569
  • 824
  • Yup Don is Right :) you can also check this link to find out more about it :D http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html – Ant's Jul 04 '11 at 13:12
  • 3
    As a quick note as to how the `def comparator= [ compare: { a,b -> return b <=> a }] as Comparator` is working, it is returning a proxy class that extends [java.lang.reflect.Proxy](http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html), and has the same signature as `Comparator`. This means that any calls to invoke the `comparator` object will be indirect and therefore take longer. To sort the list, this `Proxy` object is invoked approx `120000` times, hence the difference in timings. – tim_yates Jul 04 '11 at 14:57
  • @tim_yates - that's more like an answer to the question. – Victor Sergienko Jul 05 '11 at 13:17