Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy : closures significantly slower than methods?

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}
}
like image 926
Michael Pollmeier Avatar asked Jul 03 '11 11:07

Michael Pollmeier


2 Answers

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.

like image 65
Dónal Avatar answered Oct 28 '22 07:10

Dónal


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.

like image 32
viphe Avatar answered Oct 28 '22 06:10

viphe