Showing posts with label groovy. Show all posts
Showing posts with label groovy. Show all posts

Wednesday, December 21, 2011

Groovy, Scala, Java, Xtend - an UPDATED stupid comparison

This is an update to yesterday's post, based on feedback I got so far:

Measurement

Henning suggested that the number of iterations is too small:

10000 iterations are probably not enough for a comparison: If I go from 10k to 100k iterations, the Scala version needs 5x time -- the Java version only 2x.

Indeed. Here's what I got when using 1M iterations:

  • Java (the pretty version):
    Took : 245 ms, number of elements : 3000000
  • Xtend :
    Took : 278 ms, number of elements : 3000000
  • Xtend (using switch - see below) :
    Took : 298 ms, number of elements : 3000000
  • Scala (using for comprehensions) :
    Took : 5140 ms, number of elements : 3000000
  • Scala (using while loops) :
    Took : 931 ms, number of elements : 3000000
  • Scala (using while loops and java.util.collections - see below) :
    Took : 315 ms, number of elements : 3000000
  • Groovy (using << operator - see below):
    Took : 1683 ms, number of elements : 3000000

Groovy

Thorsten and Jochen pointed out that I used the wrong operator for adding new elements to a list. I should use '<<' instead of '+=' since the latter would create a new list for the single elements before adding them. I've fixed this and the Groovy program is much faster now.

Jochen also pointed out that the comparison is unfair since, because of the dynamic dispatch Groovy does heavy caching assuming that a method is invoked for the same type of argument most of the time.

My initial idea was to do a comparison where Groovy has a strength (i.e. dynamic dispatch). After all the other examples also do dispatching at runtime. The Java and Scala version don't call another method, but since Xtend does so and is as fast as Java I'd say this extra call doesn't change much.

Jochen also states:

Imagine a class with those three foo methods written in Java. Then in Groovy you can still run your test. I dare to say that in Xtend you cannot make this work without a dispatcher method somewhere - and your dispatch keyword is implying that - thus I assume you cannot make this then work using a Java class.


I think this is actually a big problem with Groovy. Although it's syntax might look a bit like Java it behaves totally different.
In contrast Xtend binds statically just like Java and in fact a multi method in Xtend behaves always the same no matter if you call it from Java or Xtend.

He further says

But let us not stop here... let us assume that you write the base class in Java, where it knows foo(String) foo(Object) and foo(Boolean). Using this class the Integer variant would call the foo(Object) method in your code. Now extend that class in Java with a foo(Integer) method and Groovy would no longer call foo(Object) but foo(Integer).
I doubt you can easily simulate that case.


I can indeed not do that in Java. But I can do that with Xtend and it still behaves as expected no matter form what language you'd call that code.

Scala

Jan said:

I rewrote your little program to use while loops and the java utils collections and the scala version was as fast as java - but nearly as ugly to :-D

Here's the updated Scala version with while and java.util.collections, which is now almost as fast as the Java version:
object Scala extends Application {
  val absoluteResult = new ArrayList[Any]()
  val before = System.currentTimeMillis()
  var i =0
  while (i<1000000) {
    i = i+1
    val result = List("foo", 23, true).iterator
    while (result.hasNext) {
      absoluteResult.add(foo(result.next))
    }
  }
  println("Took : "+(System.currentTimeMillis() - before)
      +" ms, number of elements : "+absoluteResult.size)
  
  def foo(obj : Any) =
    obj match {
          case _:String => "String"
          case _:Boolean => "Boolean"
          case _:Integer => "Integer"
          case _ => throw new IllegalArgumentException()
    }
}

Clojure

Stefan did a nice little example in Clojure. It doesn't use any mutable collections but uses a pure functional style:

(defn foo [x]
(cond
(number? x) "Number"
(string? x) "String"
(= (class x) Boolean) "Boolean"))

(time (println (count 
(map foo 
(apply concat 
(repeat 10000 ["foo" 23 true]))))))
On his machine the Java example takes '31ms' and the Clojure program prints

Prints:
30000
"Elapsed time: 194.046477 msecs"

Xtend

An alternative way to do the dispatch in Xtend is using type guards in switch,
which doesn't make a huge difference performance-wise :

class XtendWithSwitch {
  def static void main(String[] args) {
    val absoluteResult = newArrayList()
    val before = System::currentTimeMillis()
    for (times : 0..1000000) {
      val result = newArrayList('foo', 23, true)
      for (y : result) {
        absoluteResult += foo(y)
      }
    }
    println("Took : "+(System::currentTimeMillis() as int - before)
      +" ms, number of elements : "+absoluteResult.size)
  }

  def static foo(Comparable comp) {
    switch(comp) {
      String : 'String'
      Boolean : 'Boolean'
      Integer : 'Integer'
      default : throw new IllegalArgumentException()
    }  
  }
}

Java


Finally based on Henning's suggestion I beautified the Java version a bit using Guava and static imports:

public static void main(String[] args) {
  List<Object> absoluteResult = newArrayList();
  long before = System.currentTimeMillis();
  for (int i=0; i < 1000000; i++) {
    for (Object y : newArrayList("foo", 23, true)) {
      absoluteResult.add(foo(y));
    }
  }
  System.out.println("Took : "+(System.currentTimeMillis() - before)
    +" ms, number of elements : "+absoluteResult.size());
}

static String foo(Object s) {
  if (s instanceof String) {
    return "String";
  } else if (s instanceof Boolean) {
    return "Boolean";
  } else if (s instanceof Integer) {
    return "Integer";
  } else {
    throw new IllegalArgumentException();
  }
}

Tuesday, December 20, 2011

Groovy, Scala, Java, Xtend - a stupid comparison

Disclaimer: This is probably the worst possible way to compare execution performance. I'm posting it nonetheless since the result was significant. Judge for yourself.

Today I took some time to check out the latest Eclipse plugins for both Groovy and Scala. I mainly wanted to know how nice the tooling is and played around with it a bit. I had been doing some Scala a couple of years ago, but since the Eclipse plugin not even existed those days it was a bit too painful for me to get used to it.

Groovy's tooling looks very good. There are just a few oddities but mainly I think it's well done... in contrast to the Scala plugin (I used 2.0.0 RC4). It holds a lot of surprises. For instance, with the Scala-IDE installed you'll get Scala-related template proposals in the Java and the Groovy editor. Still Scala the language would be my personal choice if I had to decide between the two. Anyway, this post is not about language features or Eclipse-plugin quality, but about what I experienced regarding runtime performance.

I knew Groovy's performance is bad. But I expected the performance problems to diminish, if I use a scenario using dynamic dispatch and mostly untyped objects. So I wrote the following brain dead code:

class Groovy {

  public static void main(String[] args) {
    def absoluteResult = []
    def before = System.currentTimeMillis()
    for (times in 1..10000) {
      def result = ['foo', 23, true]
      for (y in result) {
        absoluteResult += foo(y)
      }
    }
    println("Took : "+(System.currentTimeMillis() - before)
      +" ms, number of elements : "+absoluteResult.size);
  }

  static String foo(String s) {
    'String'
  }
  
  static String foo(Boolean s) {
    'Boolean'
  }
  
  static String foo(Integer s) {
    'Integer'
  }

}

It doesn't do anything interesting but leverages Groovy's built-in way of resolving overloaded methods using the runtime type. I thought it's fair to use this feature in a comparison, also because Xtend's dispatch methods have the same behavior. The result of the code above would be a list containing the string 'String', 'Boolean', and 'Integer' each 10.000 times.

When I run this code on my machine, it prints something like the following:

Took : 2025 ms, number of elements : 30000

A possible Xtend version

This is how I could achieve the same using Xtend:

class Xtend {
  def static void main(String[] args) {
    val absoluteResult = newArrayList()
    val before = System::currentTimeMillis()
    for (times : 1..10000) {
      val result = newArrayList('foo', 23, true)
      for (y : result) {
        absoluteResult += foo(y)
      }
    }
    println("Took : "+(System::currentTimeMillis() - before)
           +" ms, number of elements : "+absoluteResult.size)
  }

  def static dispatch foo(String s) {
    'String'
  }
  
  def static dispatch foo(Boolean s) {
    'Boolean'
  }

  def static dispatch foo(Integer s) {
    'Integer'
  }
}

On a first glance it looks very similar. Note, the dispatch keyword, which turns a set of overloaded methods into dynamically dispatched methods (i.e. the default Groovy behavior).
In contrast to the Groovy version this one is fully statically typed and as it turned out much faster. (Again as you see my measurement technique is everything but professional, but given such a big difference I dare to say it's faster).

It prints something like:

Took : 43 ms, number of elements : 30000

I also did a Scala version:


object Scala extends Application {
  val absoluteResult = MutableList[Any]()
  val before = System.currentTimeMillis()
  for (times <- 0 until 10000) {
    val result = List("foo", 23, true)
    for (y <- result) {
      absoluteResult += foo(y)
    }
  }
  println("Took : "+(System.currentTimeMillis() - before)
      +" ms, number of elements : "+absoluteResult.size)

  def foo(obj : Any) =
    obj match {
          case _:String => "String"
          case _:Boolean => "Boolean"
          case _:Integer => "Integer"
          case _ => throw new IllegalArgumentException()
    }
}

I got this kind of result:

Took : 130 ms, number of elements : 30000

And finally a Java version:

public class Java {
  public static void main(String[] args) {
    List<Object> absoluteResult = new ArrayList<Object>();
    long before = System.currentTimeMillis();
    for (int i=0; i < 10000; i++) {
      List<Object> result = new ArrayList<Object>();
      result.add("foo");
      result.add( 23);
      result.add(true);
      for (Object y : result) {
        absoluteResult.add(foo(y));
      }
    }
    System.out.println("Took : "+(System.currentTimeMillis() - before)
      +" ms, number of elements : "+absoluteResult.size());
  }

  static String foo(Object s) {
    if (s instanceof String) {
      return "String";
    } else if (s instanceof Boolean) {
      return "Boolean";
    } else if (s instanceof Integer) {
      return "Integer";
    } else {
      throw new IllegalArgumentException();
    }
  }
}

Which prints:

Took : 40 ms, number of elements : 30000

Thursday, October 20, 2011

Xtend - Type-safe Groovy-style Builder API

In this post I want to focus on two cool language features which are both interesting for themselves but even more so in combination.

The Implicit Variable 'it'

If you name your variable 'it' all its members are accessible without qualifying them with the variable reference (just like 'this').

Example:

def appendFoo(StringBuilder it) {
  append('foo')
}

Also note that variables with the name 'it' are the only ones which can be overridden in lexical scopes. So you could for instance write the following, where the lambda expressions argument shadows the outer variable it.

def foo(List<String> it) {
  foreach[ it | toUpperCase ]
}

Sugar For Lambdas With One Argument

Many languages have special syntactic sugar for lambdas with just one argument. So does Xtend. The usual syntax for a closure with one argument is :

  [Type arg | expression-using-arg ]

But as lambdas with one argument are a common case, you can leave out the paramter declaration as in :

  [expression-using-it]

And of course the name of the single argument will be 'it'.

Closures As Last Arguments

We added another feature, which is common in many other popular languages: If you invoke a method where the last argument is a closure you can place the closure after the actual method invocation.

Example:

  newArrayList('a','b','c').foreach [ toUpperCase ]

Groovy-style Builder Syntax

Using these three language enhancements in combination allows for defining statically-typed groovy-style builder APIs. Check the screencast to see it in action.

Interesting? See xtend-lang.org for more cool features.