Saturday 26 October 2013

Scala: Methods vs Functions

It's a bright, windy Saturday morning. Sipping a nice, warm coffee I find myself musing over the performance implications of subtleties between methods and functions in Scala. Functions are first-class citizens in Scala and represented internally as instances of Functionn (where n is the arity); effectively, an interface with an apply method. Methods are methods are methods; they are directly invocable in JVM-land.

So what differences are there that could affect performance? I can think of:

  • Because functions are traits with abstract type members, in JVM-land those abstract types will be erased to Objects. I presume this means boxing for your primitives like int and long (unless scalac has any tricks up its sleeve like @specialized)
  • When passing a method to a higher-order function, methods will need to be boxed into a Function. For example, in
    def method(s:String) = s.length
    List("abc").map(method)
    the map method requires an instance of Function1 so I (again, presume) the compiler generates a synthetic instance of Function1 as a proxy to the target method.
  • The JVM can invoke methods directly. To invoke a function, it will have to first load up the Function object and then invoke its apply method. That's an extra hop.
  • Probably more.

So those are some of the differences between functions and methods in Scala. Let's see how they perform. There's an awesome little micro-benchmarking tool called ScalaMeter. It takes about 2 min to get started with it. I decided to test 1,000,000 reps along three axes:

  1. Direct invocation vs passing to someone else
  2. Primitive vs Object argument
  3. Primitive vs Object result

The Results

Fn improvement over Method
Directint → int-6.32%
int → str-9.53%
str → int-4.39%
str → str-1.09%
As Argint → int0.31%
int → str0.94%
str → int-0.22%
str → str-0.24%
What can we see?
  • It seems that there is a boxing cost for functions' i/o.
  • It seems that there is a slight cost when invoking functions directly.
  • It seems that there is no real cost boxing methods into functions.

Conclusion

If you're like one-week-ago-me (pft he's idiot!), you might think you're helping the compiler out by writing functions instead of methods when their only use is to be passed around. Well it doesn't appear to be so.
Just use methods and let your mind (if you're lucky enough to have its cooperation) worry about and solve other things.

Code and raw results available here: https://github.com/japgolly/misc/tree/methods_and_functions