9 ms·
Is Scala really Faster than Java?
- ColinWright 15y agoThis sentence stood out for me ... My basic (starter) understanding of Functional programming tells me that there should be more object churn in a functional language ... and thus Scala should have trouble with GC not the Java implementation. My experience is that in an OOP language, objects are brought forth, used, then discarded, oft-times requiring GC. My experience of well-implemented FP languages data streams through functions that have, in the large part, been optimised away. I don't have deep or broad experience, and it's ceretainly true (as I see it) that naive implementations provoke lots of GC. But it didn't seem obvious to me that OOPLs should provoke less, and it does seem reasonable that FPLs might easily provoke less. I'd be interested to hear some of our more experienced HNers comment on this.
- gaius 15y agoI can't find the link right now, but there was a blog post from one of the F# team at MS, saying the CLR team couldn't believe the rate at which he was allocating and deallocating memory, and some tuning needed to be done on the CLR to support F# performance. If you are working with recursion and folding rather than for-loops, under the covers you will be doing one malloc() per iteration, you will definitely need some sort of clever memory management rather than relying on just the OS's implementation.
- Peaker 15y agoWhy would there be one malloc() per iteration if you have tail-recursion? Also, isn't malloc() a misnomer for stack allocations?
- jules 15y agoTail recursion has nothing to do with garbage collection in F#. You're going to have one allocation per iteration if you do e.g. a map over a list. In an imperative language you'd probably mutate the collection in place.
- eru 15y agoUnless, and I guess that's what ColinWright alluded to, you have extensive stream fusion that will get rid of intermediate data structures.
- jules 15y agoThat's something else entirely compared to tail call optimization, but yes some functional languages (e.g. Haskell) do this. I don't think F# does stream fusion though.
- loup-vaillant 15y agoI'd be surprised if the current implementation of F# didn't do anything of this sort. But as a language, I understand fusion is more difficult in ML than in Haskell: that's because ML is strict and non pure. Therefore, some of the assumptions you could do in a non-strict, pure language don't hold, and it may not be unconditionally true that ∀f, ∀g, map g ∘ map f == map (f ∘ g) because f or g could perform some side effect, or fail to terminate. (My example may be bogus, but I'm sure we can find some real one.)
- eru 15y agoFailure to terminate exists in Haskell, too.
- loup-vaillant 15y agoSure, but the order of evaluation do change something. For instance, infinite lists won't ever terminate, but taking the first 42 elements of an infinite list will terminate in Haskell, and not in ML. If you want to fuse the whole thing, it won't change the Haskell program, but it may change the ML one, by making it terminate an return the correct result! That's not an optimization any more, that's a language feature.
- 15y ago
- gaius 15y agoIn this case, I believe not: http://java.sun.com/docs/books/jvms/second_edition/html/Overview.doc.html#6654 http://java.sun.com/docs/books/jvms/second_edition/html/Over... You would not be using the stack for this.
- sandGorgon 15y agoCopy-on-write data structures (like the ones Clojure uses) may help mitigate some of the object churn.
- lemming 15y agoOnly in some (read-heavy) workloads - in general I'd expect persistent data structures to generate more garbage, since mutability by definition allows you to modify and reuse nodes. Edit: thinking about this on the way back from lunch, read-heavy workloads give you the best case for persistent data structures, but that's still the same as for mutable structures, i.e. no object churn. I can't think of any cases where persistent structures would create less garbage.
- jriddycuz 15y agoI think you'd be surprised. Persistent data structures allow the VM or compiler to determine when things need to be copied. Mutable structures require programmers to worry about copies, so they will make copies when they feel they might be necessary. In very well-defined cases a smart programmer can optimize this to a point where it probably uses less memory than a persistent data structure, but in the average case (and with the average programmer), the VM will likely do a better job of knowing when to copy and when to not bother.
- lemming 15y agoYou're right, I would absolutely be surprised. Persistent data structures intrinsically involve copying on updates, and no amount of compiler cleverness can avoid that. I can see the possibility for compiler magic with stream fusion, for example, but I'm not seeing it for data structures. The only win I can see is if you have multiple clients of your data structure holding references to multiple versions, but even there the compiler doesn't help you. Am I missing something?
- eru 15y ago> Persistent data structures intrinsically involve copying on updates, and no amount of compiler cleverness can avoid that. The compiler magic can help you there. You are looking for linear types.
- jmreardon 15y agoHigh object churn shouldn't be a problem for the JVM, whether it is running Scala or Java code. Allocation lots of little objects and then discarding them shortly after is a use case the JVM is well optimized for. You would have to allocate an extraordinary number of such objects to see any real slowdown. The Oracle JVM uses generational garbage collection[1]. Glossing over some details, this garbage collector divides objects into generations depending on how long they've been around, and also keeps track of references that cross generations. Objects that are newly created go into the first generation. If the objects don't refer to objects outside of the first generation, it is very cheap for the VM to determine they are in fact garbage. When the garbage collector gets run, objects that a still live get copied to another generation. Once the still live objects are copied out, the VM doesn't need to do any cleanup to get rid of the dead objects, it simply marks the whole memory block the first generation was in as available. [1] Not that other JVMs don't, but I only know the official one.
- gaius 15y agoOracle have put an enormous amount of work into the JRockit GC to support determinism. It's very clever: you can say to it, no GC pauses longer than X milliseconds, and it will adjust its throughput to guarantee that. So you might get lower overall performance, but it will be predictable.
- daniel_solano 15y agoIt all depends on the context. In code that is not performance-sensitive, a bit of object churn isn't a problem. However, there are places where a lot of object churn can really be problematic. In particular, I find that Android applications tend to be somewhat sensitive to this. However, even in desktop or server applications, a lot of object churn in a tight loop can be bad.
- bad_user 15y agoThat's because when people talk about the JVM's performance, they refer to Oracle's Java SE, not Android and not Apache Harmony.
- 15y ago
- gaius 15y agoThinking logically if some language is built on some other language then the derived language can be at most as fast as the underlying language That is not true. Why? Because the C compiler optimizes at compile time, and the VM optimizes at compile time and at runtime. It can be much smarter at inlining, loop unrolling, etc.
- vetler 15y agoAlso, Scala is not "built on" the Java language, as far as I know. It compiles to bytecode, just as the Java language does.
- gaius 15y agoYep, at this point in history Java-the-language and Java-the-VM are related in name only.
- thomaslangston 15y agoCould you elaborate? It is my understanding that 99.9% of Java syntax is valid Scala syntax. How does that correlate with your statement?
- JoachimSchipper 15y agoThe compilation path is Scala -> JVM bytecode [-> x86 assembly], not Scala -> Java -> JVM bytecode [-> x86 assembly]. At least, that's what I think your parent comment was saying.
- msmith 15y agoNo, Java and Scala syntax are quite different. I can really only think of a few cases where they share syntax.
- ominous_prime 15y agoI wonder where people are getting this (you're not the first person I've heard this from)? The syntax is not compatible at all. You can write programs in the same style, e.g. object oriented with inheritance, global state, side effects, and so on; but the similarity ends there.
- brown9-2 15y agoIt's a gigantic stretch to read an article that benchmarks languages implementing a single type of function and conclude "It shows Scala to be faster compared to Java". Thankfully the author realizes this in his conclusion - "it seems that difference in implementations is the cause for difference in the time taken by Java/Scala programs".
- eru 15y agoHe could probably try to justify: Scala idioms to structure code tend to be faster than Java idioms.
- eevilspock 15y agoOr argue that the Scala language provides more information to the static compiler allowing it to produce better byte code. Has anyone done that analysis?
- cb372 15y agoIn general, the Java static compiler generates pretty poor-performing bytecode, no matter how much useful info you give it. This is by design, and relies on HotSpot to provide the necessary optimisations at runtime. I'm no expert but I would assume that the Scala compiler is quite similar. Not much point in generating highly optimised bytecode when HotSpot will do the job for you.
- jbooth 15y agoCan nobody on planet earth read? That Google paper specifically said that they significantly refactored the algorithm to be more "scala-like" and got a much cheaper implementation than the canonical way it was implemented in other languages. So, no, Scala is not faster than Java and probably won't be for a long time. But yes, picking your algorithm is more important than language microbenchmarks.
- loup-vaillant 15y agoYour phrasing is slanted. Citing the abstract of the paper: > The implementations each use the languages’ idiomatic container classes, looping constructs, and memory/object allocation schemes. It does not attempt to exploit specific language and run-time features to achieve maximum performance. Meaning, they went out of their way to write idiomatic Scala and idiomatic Java. No performance tweak in Java nor in Scala. The way you phrased it make it sound like they paid more attention to their Scala implementation. If you really think that, say so. Beware though: that would be a direct accusation of intellectual dishonesty. To me, Google's paper looks like clear (though not iron strong) evidence that idiomatic Scala is is faster than idiomatic Java, with the chosen implementations. Shortening that into "Scala is currently faster than Java" doesn't seem such a stretch. Now if you want to compare idiomatic Java written in Java, and idiomatic Java written in Scala, fine, but I don't think it's a fair comparison.
- scott_s 15y agoFrankly, their discussion is a bit confusing because they did have optimized versions discussed in the text. And the optimized Scala version did make fundamental changes to the algorithm. They could have kept a better division between the two - idiomatic versus optimized - in both their results and their discussion. So, jbooth is not accusing them of intellectual dishonesty. And if I consider the following three suppositions: jbooth's understanding of the paper was not complete, your understanding of the paper was not complete, and the people who wrote the paper are smart and hard working; then I simply conclude that performing and writing about this kind of study in a clear manner that will confuse none is very, very hard.
- fizx 15y agoMy experience with Java and Scala in high profile, high traffic apps is that they are more or less exactly the same speed. Scala has historically had a much less performant (and buggy) collections library, so I've often used java collections from scala, but it's gotten better.