41

First of all I would like to make it clear that this is not a language-X-versus-language-Y question to determine which is better.

I have been using Java for a long time and I intend to keep using it. Parallel to this, I am currently learning Scala with great interest: apart from minor things that take some getting used to my impression is that I can really work very well in this language.

My question is: how does software written in Scala compare to software written in Java in terms of execution speed and memory consumption? Of course, this is a difficult question to answer in general, but I would expect that higher level constructs such as pattern matching, higher-order functions, etc, introduce some overhead.

However, my current experience in Scala is limited to small examples under 50 lines of code and I haven't run any benchmarks up to now. So, I have no real data.

If it turned out that Scala does have some overhead wrt Java, does it make sense to have mixed Scala / Java projects, where one codes the more complex parts in Scala and the performance-critical parts in Java? Is this a common practice?

EDIT 1

I have run a small benchmark: build a list of integers, multiply each integer by two and put it in a new list, print the resulting list. I wrote a Java implementation (Java 6) and a Scala implementation (Scala 2.9). I have run both on Eclipse Indigo under Ubuntu 10.04.

The results are comparable: 480 ms for Java and 493 ms for Scala (averaged over 100 iterations). Here are the snippets I have used.

// Java
public static void main(String[] args)
{
    long total = 0;
    final int maxCount = 100;
    for (int count = 0; count < maxCount; count++)
    {
        final long t1 = System.currentTimeMillis();

        final int max = 20000;
        final List<Integer> list = new ArrayList<Integer>();
        for (int index = 1; index <= max; index++)
        {
            list.add(index);
        }

        final List<Integer> doub = new ArrayList<Integer>();
        for (Integer value : list)
        {
            doub.add(value * 2);
        }

        for (Integer value : doub)
        {
            System.out.println(value);
        }

        final long t2 = System.currentTimeMillis();

        System.out.println("Elapsed milliseconds: " + (t2 - t1));
        total += t2 - t1;
    }

    System.out.println("Average milliseconds: " + (total / maxCount));
}

// Scala
def main(args: Array[String])
{
    var total: Long = 0
    val maxCount    = 100
    for (i <- 1 to maxCount)
    {
        val t1   = System.currentTimeMillis()
        val list = (1 to 20000) toList
        val doub = list map { n: Int => 2 * n }

        doub foreach ( println )

        val t2 = System.currentTimeMillis()

        println("Elapsed milliseconds: " + (t2 - t1))
        total = total + (t2 - t1)
    }

    println("Average milliseconds: " + (total / maxCount))
}

So, in this case it seems that the Scala overhead (using range, map, lambda) is really minimal, which is not far from the information provided by World Engineer.

Maybe there are other Scala constructs that should be used with care because they are particularly heavy to execute?

EDIT 2

Some of you pointed out that the println's in the inner loops take up most of the execution time. I have removed them and set the size of the lists to 100000 instead of 20000. The resulting average was 88 ms for Java and 49 ms for Scala.

13
  • 5
    I imagine that since Scala compiles to JVM byte code, then the performance could theoretically be equivalent to Java running under the same JVM all other things being equal. The difference I think is in how the Scala compiler creates the byte code and if it does so efficiently.
    – maple_shaft
    Commented Jan 24, 2012 at 18:52
  • 2
    @maple_shaft: Or maybe there's overhead in Scala compile time? Commented Jan 24, 2012 at 19:03
  • 1
    @Giorgio There is no runtime distinction between Scala objects and Java objects, they are all JVM objects that are defined and behave per the byte code. Scala for instance as a language has the concept of closures, but when these are compiled they are compiled to a number of classes with byte code. Theoretically, I could physically write Java code that could compile to the exact same byte code and the runtime behavior would be exactly the same.
    – maple_shaft
    Commented Jan 24, 2012 at 19:12
  • 2
    @maple_shaft: That's exactly what I am aiming at: I find the above Scala code much more concise and readable than the corresponding Java code. I was just wondering if it would make sense to write parts of a Scala project in Java for performance reasons, and what those parts would be.
    – Giorgio
    Commented Jan 24, 2012 at 20:28
  • 2
    The runtime will be largely occupied by the println calls. You need a more compute-intensive test. Commented Jan 24, 2012 at 22:03

4 Answers 4

39

There's one thing that you can do concisely and efficiently in Java that you can't in Scala: enumerations. For everything else, even for constructs that are slow in Scala's library, you can get efficient versions working in Scala.

So, for the most part, you don't need to add Java to your code. Even for code that uses enumeration in Java, there's often a solution in Scala that is adequate or good -- I place the exception on enumerations that have extra methods and whose int constant values are used.

As for what to watch out for, here are some things.

  • If you use the enrich my library pattern, always convert to a class. For example:

    // WRONG -- the implementation uses reflection when calling "isWord"
    implicit def toIsWord(s: String) = new { def isWord = s matches "[A-Za-z]+" }
    
    // RIGHT
    class IsWord(s: String) { def isWord = s matches "[A-Za-z]+" }
    implicit def toIsWord(s: String): IsWord = new IsWord(s)
    
  • Be wary of collection methods -- because they are polymorphic for the most part, JVM does not optimize them. You need not avoid them, but pay attention to it on critical sections. Be aware that for in Scala is implemented through method calls and anonymous classes.

  • If using a Java class, such as String, Array or AnyVal classes that correspond to Java primitives, prefer the methods provided by Java when alternatives exist. For example, use length on String and Array instead of size.

  • Avoid careless use of implicit conversions, as you can find yourself using conversions by mistake instead of by design.

  • Extend classes instead of traits. For example, if you are extending Function1, extend AbstractFunction1 instead.

  • Use -optimise and specialization to get most of Scala.

  • Understand what is happening: javap is your friend, and so are a bunch of Scala flags that show what's going on.

  • Scala idioms are designed to improve correctness and make the code more concise and maintainable. They are not designed for speed, so if you need to use null instead of Option in a critical path, do so! There's a reason why Scala is multi-paradigm.

  • Remember that the true measure of performance is running code. See this question for an example of what may happen if you ignore that rule.

4
  • 1
    +1: A lot of useful information, even on topics that I still have to learn, but it is useful to have read some hint before I get to look at them.
    – Giorgio
    Commented Jan 24, 2012 at 22:15
  • Why first approach uses reflection ? It generates anonymous class anyway, so why don't use it instead of reflection ? Commented Oct 1, 2013 at 15:28
  • @Oleksandr.Bezhan Anonymous class is a Java concept, not a Scala one. It generates a type refinement. An anonymous class method that does not override its base class cannot be accessed from the outside. The same is not true of Scala's type refinements, so the only way to get at that method is through reflection. Commented Oct 1, 2013 at 17:13
  • This sounds quite terrible. Especially: "Be wary of collection methods -- because they are polymorphic for the most part, JVM does not optimize them. You need not avoid them, but pay attention to it on critical sections."
    – matanox
    Commented Mar 27, 2016 at 20:03
21

According to the Benchmarks Game for a single core, 32 bit system, Scala is at a median 80% as fast as Java. The performance is approximately the same for a Quad Core x64 computer. Even memory usage and code density are very similar in most cases. I would say based on these (rather unscientific) analyses that you are correct in asserting that Scala adds some overhead to Java. It does not appear to add tons of overhead so I'd suspect the diagnosis of higher order items taking up more space/time is the most correct one.

1
18
  • Scala performance is very decent if you just write Java/C-like code in Scala. The compiler will use JVM primitives for Int, Char, etc. when it can. While loops are just as efficient in Scala.
  • Keep in mind that lambda expressions are compiled to instances of anonymous subclasses of the Function classes. If you pass a lambda to map, the anonymous class needs to be instantiated (and some locals may need to be passed), and then every iteration has extra function call overhead (with some parameter passing) from the apply calls.
  • Many classes like scala.util.Random are just wrappers around equivalent JRE classes. The extra function call is slightly wasteful.
  • Watch out for implicits in performance-critical code. java.lang.Math.signum(x) is much more direct than x.signum(), which converts to RichInt and back.
  • Scala's main performance benefit over Java is specialization. Keep in mind that specialization is used sparingly in library code.
5
  • a) From my limited knowledge, I have to remark, that code in the static main method can't be optimized very good. You should move the critical code to a different location.
  • b) From long observations I would recommend not to do heavy output on performance test (except it is exactly what you like to optimize, but who should ever read 2 Million values?). You're measuring println, which isn't very interesting. Replacing the println with max:
(1 to 20000).toList.map (_ * 2).max

reduces the time from 800 ms to 20 on my system.

  • c) The for-comprehension is known to be a little slow (while we have to admit it's getting better all the time). Use while or tailrecursive functions instead. Not in this example, where it is the outer loop. Use the @tailrec-annotation, to test for tairecursiveness.
  • d) Comparing with C/Assembler fails. You don't rewrite scala code for different architectures for example. Other important difference to historic situations are
    • JIT-compiler, optimizing on the fly, and maybe dynamically, depending on input data
    • The importance of cache misses
    • The rising importance of parallel invocation. Scala today has solutions to work without much overhead in parallel. That's not possible in Java, except you do much more work.
3
  • 2
    I have removed the println from the loop and actually the Scala code is faster than the Java code.
    – Giorgio
    Commented Jan 24, 2012 at 22:27
  • The comparison with C and Assembler was meant in the following sense: a higher-level language has more powerful abstractions but you may need to use the lower-level language for performance. Does this parallel hold considering Scala as the higher-level and Java as the lower-level language? Maybe not, since Scala seems to deliver performance similar to Java.
    – Giorgio
    Commented Jan 24, 2012 at 22:30
  • I wouldn't think it would matter much for Clojure or Scala but when I used to play around with jRuby and Jython I would have probably written the more performance critical code in Java. With those two I saw a significant disparity but that was years ago now...could be better.
    – rlperez
    Commented Jan 25, 2012 at 12:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.