tags:

views:

181

answers:

3

I'm working on a Scala-based analytics system (http://www.hiringthing.com), and I'm finding that I'm often asking myself the following question. Given a "pure" function with no side effects, if I hit that function twice with the same inputs, can I expect the compiler to reuse the value generated from the first run, or will it go through all the code again. Put another way, is the first example below more efficient than the second?

def add(x: Int, y: Int) = x + y * 10000000000

val a = add(1,2)
do_something(a)
do_another_thing(a)

vs.

def add(x: Int, y: Int) = x + y * 10000000000

do_something( add(1,2) )
do_another_thing( add(1,2) )

If the compiler can indeed optimize the second case, are there limits to the complexity of the function?

What I want to do is avoid running certain math heavy functions multiple times just for the sake of programming convenience...

Thanks

+4  A: 

The scala compiler doesn't attempt to optimize any through calls to functions or methods, unless you specifically use the @inline annotation (and even that isn't gauranteed). That said, the JVM, and the Hotspot JIT compiler in particular, almost certainly will be able to inline the call to "add" in your examples, and will then be able to remove the resulting common-subexpressions.

As always when asking questions about performance and compiler optimizations, no answers should be taken as gospel without extensive, professional benchmarking. Past performance is no gaurantee of future returns. Contents may settle during shipping. If swelling persists after four hours, see your doctor. All models over 18.

Dave Griffith
You forgot the "or 21 where required".
Daniel
+6  A: 

From what I've seen, the Scala compiler doesn't optimize that at all. The JVM may, if it can determine that the results won't change, but often it has no good way to know.

So, generally, if it's a trivial computation, it doesn't make a difference, both because it's trivial, and because the JVM can figure out that it only needs to do it once. If it's complicated, and speed is essential, you should use the val a = method unless you have benchmarks that demonstrate to you that the JVM is smart enough in this case.

Note that sometimes it feels awkward to place vals. There are two ways to get around this. First, note that almost anything can be replaced by the equivalent statement in braces, so the exceptions may be less often than you think. Also, this method may be useful in certain circumstances:

def reuse[A,B](a: A)(f: A => B) = f(a)
reuse(add(1,2))( a => { do_something(a); do_another_thing(a)})
Rex Kerr
Through long Lisp and Haskell tradition, that "reuse" function should probably be called "let"
Dave Griffith
Yes, probably so.
Rex Kerr
+4  A: 

Without an effect system, the compiler simply cannot decide to reuse method (or Function) return values.

As has been the case for most languages throughout the history of computing, it's up to you to remove redundancy from your algorithms.

Randall Schulz