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
- 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
mapmethod requires an instance of
Function1so I (again, presume) the compiler generates a synthetic instance of
Function1as 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
applymethod. 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:
- Direct invocation vs passing to someone else
- Primitive vs Object argument
- Primitive vs Object result
|Fn improvement over Method|
|Direct||int → int||-6.32%|
|int → str||-9.53%|
|str → int||-4.39%|
|str → str||-1.09%|
|As Arg||int → int||0.31%|
|int → str||0.94%|
|str → int||-0.22%|
|str → str||-0.24%|
- 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.
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