Why is Clojure so slow?
martinsprogrammingblog.blogspot.de
martinsprogrammingblog.blogspot.de
It's important to get these distinctions right! Some people, unfortunately, form impressions from headlines and in this case it's extremely inaccurate. If you read the article you'll get the truth - there are startup time problems causing issues with using Clojure for command-line programs.
That's much less damning overall than "Clojure is slow."
> How fast is Clojure at running your code once it finally has got going? ... Clojure is on average 4x slower than Java and 2x slower than Scala.
That's pretty freaking slow.
It's all relative.
[1] http://shootout.alioth.debian.org/u64q/which-programming-lan...
I did an experiment once, testing out a simple web-service on the JVM and Scala (with Scalatra) yields the same performance as Java (with Jax-RS), while Clojure (with Noir) is only 2x to 3x as slow and JRuby (Sinatra) is only 4x-5x as slow as Java.
You seem to be complaining that the programmers were not forced to write slow programs :-)
>>I did an experiment once...<<
And you used libraries.
That said, they are fun to look at. I just had a wtf-moment looking at this:
http://shootout.alioth.debian.org/u32/performance.php?test=n...
~20 seconds vs ~20 minutes??
http://shootout.alioth.debian.org/u64q/benchmark.php?test=fa...
Note that the "alternative" Lisp SBCL and Java 7 programs both outperform Fortran.
Of course I agree with you on wtf-moments. WTF makes Ruby take an hour, and SBCL take 10 seconds? That's two orders of magnitude! But no matter, I imagine a more clever Ruby programmer could reduce that, or just call out to a native library.
A program that simply switched according the command line arg and then printed:
3968050
Pfannkuchen(12) = 65
:would also out perform :-)>>I imagine a more clever Ruby programmer could reduce that...<<
Is "a more clever Ruby programmer" some kind of equivalent to "a sufficiently smart compiler"? :-)
>>or just call out to a native library<<
When is a Ruby program fast? When it's written in C ;-)
Of course making programs do less can improve speed, and a great way of doing that is compile-time computation via macros! You can finish the program before it's even run.
> Is "a more clever Ruby programmer" some kind of equivalent to "a sufficiently smart compiler"?
No, since we assume human intelligence here. :P As the Graphics Programming Black Book puts it in the Chapter 1 title, "The Best Optimizer is between Your Ears".
> When is a Ruby program fast? When it's written in C ;-)
I'm going to use this one.
Maybe "a more clever Ruby programmer" always drops-down to C?
Maybe there's only a more clever Rails programmer :-)
Any benchmark is only useful if...
http://shootout.alioth.debian.org/dont-jump-to-conclusions.p...
2) "unfairly" targeted at low-level languages
Don't make "unfair" accusations -- say why you think that.
3) ~20 seconds vs ~20 minutes??
Did you mean vs ~20 hours?
Because there seems to be a bias in selecting the problems solved in the benchmark games: to be "fair" they should be randomly selected from a pool of all possible problems solved with computer programs. Or, maybe, the frequency of these problems in the real world should be taken into account?
Of course, they're not actually unfair since they hide nothing. The error is in the interpretation, e.g. "My program will be faster if I write it in language X instead of Y."
The nice link you posted sums it up well: "Programming languages are compared against each other as though their designers intended them to be used for the exact same purpose - that just isn't so."
edit: Spelling..
So don't say that they are!
2) "The nice link you posted sums it up well"
I agree - but then I wrote those words.
3) "there seems to be a bias in selecting the problems solved in the benchmark games"
You still haven't said anything that suggests they are "targeted at low-level languages".
3) GP? Your opining about "selecting the problems" doesn't support the claim "targeted at low-level languages" anymore than it supports the claim targeted at high-level languages :-)
As you noted the important thing to remember is that timing measurements are not promises, and they aren't general answers to the question - Will my program be faster if I write it in language X?
I suppose in a vacuum it is indeed (assuming the measurements are general), but in my actual use the speed of Clojure has been more than sufficient. People like to view programming language speed as a black and white issue in the context of blog and HN posting, but in many uses runtime and startup speed considerations are only part of the puzzle.
This may be acceptable, depends on where you're coming from and what you're needs are. For instance if you are coming from Ruby or Python because you want much better performance and you think the JVM is awesome for that, then Clojure may not be a good choice. On the other hand if you want a modern Lisp that's also reasonably fast and that has access to the JVM, then Clojure is a really good choice.
Personally that's why I like Scala, but you know, the best language is whatever makes you happier and more productive.
That's why I used "tax" which I think is very suitable.
So in this case tax is exactly the right word.
Others on this thread have pointed out that the Alioth benchmark takes startup time into account. Yes, this imposes a startup penalty on Clojure.
More importantly, the implementations of each individual benchmark vary significantly in performance quality. High-performance Clojure requires a couple of tricks in type hinting, using unchecked arithmetic, and preferring native Java arrays.
I looked at a couple of the benchmarks, and the mandelbrot example uses those tricks. Notice the performance there: http://shootout.alioth.debian.org/u64/performance.php?test=m...
Notice that Java 7 and gcc run at about the same speed, and Clojure is only about 2.2x slower (than C). Scala is about 1.9x slower (than C). gcc is about 1.5x slower than Intel Fortran.
To make the benchmark more fair: (1) all timings should disregard startup time; and (2) all JVM languages should have the opportunity to run the benchmark a few thousand times before timing it. Otherwise, it measures JVM startup time, and then it measures how long it takes the JIT to achieve maximum optimization. By comparison, the C and Fortran code runs at full speed almost out of the gate.
All-in-all, considering that Clojure is an extremely high-level language I consider its performance impressive [1]. Yes, the inner loops need to be coded in a slightly un-idiomatic manner, but you can do all this in the comfort of your REPL, which makes the process of making optimizations reasonably painless.
[1] Don't forget to scroll down the mandelbrot results and look at the stellar performance of other popular high-level languages.
Please take that Clojure mandelbrot program, make repeated timing measurements without restarting the JVM and then report how those times compare to cold start on your computer.
The mean "warmed" times for the Java mandelbrot program were actually slower than the reported cold start time for the same program.
Judging by the invocation noted at the bottom of http://shootout.alioth.debian.org/u64/program.php?test=mande..., the comments about Java in the FAQ do not apply to the Clojure code. The benchmark seems to have been invoked straight from the command line.
- 2831.323 ms for what workload? The benchmarks game measurements are made at 3 different workloads; but the times that matter are those for the largest workload, in this case N=16,000. So please show the times for N=16,000.
- the benchmarks game measurements are made with output redirected to /dev/null
- both the clojure and java programs are invoked straight from the command line, and include start-up. The Help page provides additional "warmed" measurements for the fastest Java programs, for comparison - because sometimes the JVM startup costs are larger in the mind than they are when measured :-)
I ran mandelbrot for 4000 cycles, just invoking the function which does the work twice and wrapping each call in Clojure's (time ...) form. This all happened in an AOT-compiled .class file, which guaranteed a cold start. For what it's worth, I didn't bother tuning the GC or any other JVM parameters — I suspect I could have made it run a bit faster by manipulating generation sizes.
That reduced workload only runs the program for 1/10th the time of the workload shown on the benchmarks game website.
Run the program for N=16,000 and see that "JVM startup costs and JIT costs amount to nothing" even for these "short, synthetic benchmarks".
(Incidentally, the "usual" cold start measurement shown on the website is the best of 6.)
If we can make working websites and even games in Python, we can make them in Clojure too. What kills Clojure for small scripts and not-long-running applications is the startup time and that can mostly be attributed to JVM. Also, the memory footprint of a JVM process tends to grow a lot over time.
I see Clojure rising above specific platforms, though. JVM is in a slow death spiral. CLR might have some traction on Windows. If Python got it platform resettled on something more sophisticated than CPython, that might be a good ecosystem for Clojure.
Did you not read the article? Quote: "What we can see is that Java itself accounts for 0.35s of the startup time, but unfortunately Clojure adds another second(!) on top of that."
So no, it can not be mostly attributed to JVM.
Also, JVM in a low death spiral? What gives you that impression, the growing number of languages that target it? The fact that new versions continue to receive improvements, such as better support for dynamic languages in v. 7?
For me the more obvious comparison would be with other Lisps. Which, back in the dim past when I was using 'em, were often damn fast.
But as fogus said, speed is only one piece of the puzzle. http://news.ycombinator.com/item?id=4223562
How so?
In most enterprises, regardless what we think about the JVM, it is still quite healthy.
It's not all gloom and doom though; Attila Szegedi is at Oracle now, working on Nashorn - something exciting for Java 8.
The recent lawsuit kind of highlights that. Google is riding on years of development and refinement of Java IDEs and on mountains of available open-source libraries. They jump-started an Android community from zero by building an alternative VM for targeting Java source code, while giving the finger to Sun/Oracle and their licensing.
And I don't think they are so stupid as to not realize this.
The problem I see with Oracle is that if they push too much the community, companies might abandon it the same way they did with Delphi when Borland did too many mistakes. On the other hand, I can speak from my experience in the enterprise world, corporations love Oracle.
If it takes me 3 months to deliver a given program in Clojure and 6 to deliver its Java equivalent, the Clojure one already has 3 months of lead. Assuming the Java one is twice as fast, it'll take 45 days to catch up.
Development time is expensive, computers are cheap and get twice as fast every year or so.
While a long startup time is annoying, it can certainly be optimized out if someone focuses enough attention to the low level aspects of the runtime.
> Development time is expensive, computers are cheap and get twice as fast every year or so.
It's a bit funny that you're citing Moore's law when Clojure is specifically designed to overcome its breakdown and get out ahead of that lagging curve. (Paraphrasing an early talk from Rich Hickey: "The hardware guys are punting!!")
You can turn that right around as fuel for your original point, that Clojure and it's approach to concurrency buys you tons of developer productivity, compared to whacking about in the weeds with Java. I totally agree there, those higher level features are valuable and worth something, but they do not cost nothing.
But I don't think this calculation makes much sense in the first place. It's simply not that linear and depends on many other things, for instance whether it's a throughput or response time problem, the relative value being the first to market versus being the best, etc.
Indeed. With a feature set dynamic enough, the lead will mount up.
All I'm saying is that at some point it becomes way more expensive than paying developers to optimize or rewrite in a faster language, which is exactly why Google and Facebook are doing so much work in C++, not exactly a language known for developer productivity.
And obviously there are many places where you can't scale your way out of a response time or battery usage issue because you're not the one buying the machine.
So I totally disagree with your assumption that developer productivity always trumps runtime efficiency. It is also my experience that the productivity advantages usually ascribed to some (mostly dynamic) languages is way overblown. But that's another debate.
I agree there are cases where only the leanest and meanest code will do, but my point is that those cases are very rare.
So does the ClojureScript compiler basically just embed a Clojure interpreter in every file? I'd be interested to see the code prior to optimization.
http://lists.nongnu.org/archive/html/chicken-users/2011-03/m...
http://lists.nongnu.org/archive/html/chicken-users/2011-03/m...
He pretty much starts off by talking about making Clojure "leaner", faster at starting up etc.
He mentions stuff like a "production" jar with less metadata, hoisted evaluator and even some kind of tree shaking ala ProGuard.
Another approach to the problem would be similar to FastCGI: keep one Clojure server process running and execute scripts on it.
It's still hard to understand why aren't Oracle working on something like that. It's not as if they don't care about desktop at all -- JavaFX is going to be part of Java8 and they are even working on a new packaging tool-chain for it. JVM's start-up time and inability to allocate memory when needed (as compared to up-front way it's done now) are the major reasons why Java/JVM is (still) a bad solution for desktop and cli applications.
http://java.sun.com/developer/technicalArticles/Programming/...
Care to elaborate? Are you saying that the JVM can't malloc()? :)
Such complicated memory management is really efficient and works great for server apps. On the client -- not so much. In that case I would rather prefer a slower GC that just uses malloc/free. Why? Because from end-user perspective that's not really great when a simple app grabs a 100-200mb of memory. If all apps were like this you wouldn't be able to run many of them at the same time. (Especially on the older hardware.) Moreover for a complex JVM apps 500mb+ of allocated RAM might be a requirement. [2]
Great example of how things should work is .NET/Mono. Both of the .NET runtimes allocate memory only when it's actually needed and start-up performance is great too. All things considered JVM and CLR are very similar runtimes and there is no reason for a more client-oriented JVM implementation not to be possible.
[1] http://www.quora.com/How-does-garbage-collection-work-in-the...
[2] Eclipse for instance recommends to configure jvm to allocate at least 500mb up-front. (This is done via -Xms and -Xmx parameters.)
Note that this addresses JVM startup overhead, but still not Clojure startup overhead (two are separate).
I never understood why the JVM folks didn't get along to develop a JIT cache. That means the first time I start a Java program it would run normally slow. But from the second run on it would use the native cache and run immediately fast with native performance. That would eliminate many performance problems of Java.
I know that there already is a solution which uses a Java server to serve the application as client which reduces the startup time but this is not very convenient to use.
The slowness of Clojure is a typical problem of all languages which are based on JVM. Racket Scheme, for instance, which is a Lisp like language but NOT based on JVM, needs just 0.062s to print "Hello World" (compiled) on my system.
"spends 95% of the startup-time loading the clojure.core namespace (the clojure.lang.RT class in particular) and filling out all the metadata/docstrings etc for the methods. This process stresses the GC quite a bit, some 130k objects are allocated and 90k free-d during multiple invokes of the GC (3-6 times), the building up of meta data is one big source of this massive object churn."
That's correct but even without this startup time Clojure is significantly slower than other functional languages. Look at SBCL and Racket in
http://shootout.alioth.debian.org/u32/which-programming-lang...
That doesn't mean that I don't like Clojure. I am even considering it for a business project. But Clojure is definitely unsuitable for small apps (shell scripts etc.)
Btw the benchmark listing doesn't take LuaJIT into account. This JIT is the fastest I have ever encountered, way ahead of JVM regarding startup time.
EDIT: sorry, probably your referred to http://publib.boulder.ibm.com/infocenter/java7sdk/v7r0/topic...
"Da AOT-Code über verschiedene Programmausführungen hinweg bestehen bleiben muss, ist die Leistung von mit AOT generiertem Code nicht so gut wie die von mit JIT generiertem Code."
Clojure runs quite fast, in my experience. I have written an web app http://rssminer.net, in Clojure (and some Java). On a small VPS(512M RAM, 1 core CPU), It can handle about 300 request per second, On my desktop, about 2000 req/s. Which is not slow, at least.
The persistent data structures Clojure use is fast too. I did some test a long ago, It's roughly the same speed as Java collections.
I'm not sure if they went through with it though
You can read a lot of information about this part of Clojure in the book "Practical Clojure". I'm reading it and I'm learning some stuffs about Clojure.
To conclude, Clojure is fine for "long" program running.
For most programs that aren't compute intensive, you won't notice a penalty, but the same is true with ruby or python.
(def x 1) ; x = 1 (def x 2) ; x = 2 now
(defconstant y 1) ; y = 1 (defconstant y 2) ; Exception
Also a way of fixing functions would be good, so that no lookup is required. Calling such a fixed function has no overhead, it would be a direct call.
(def ^:const PI 3.14)
As for function lookup, I recommend reading this thread: http://news.ycombinator.com/item?id=2928285 Also, vars are by default static (but can be made dynamic with ^:dynamic).
But I would like to have real constants. A final class with a static final field (and potentially type information), or something like that. This would give the optimal lookup time, as the JVM would have the direct address.
When I (defonce x 1) I can still (def x 2), without restarting the JVM. I want this to not be possible with a defconstant.
All code constantly assumes that the objects behind Vars will change. Let’s say we have `(defn foo [] 1)`. The caller of (foo) will first lookup the address in RAM of the compiled function, and then jump to it. Because of this dynamicy we can redefine foo at runtime: `(defn foo [] 2)`. All callers still function, and will now get the result `2`. In statically compiled languages this would not happen, because `foo` is translated to the direct address of the first function. The concept of replacing functions at runtime doesn’t exist in that way.
But I would like to see an optional “static” programming feature: I want to be able to mark functions as final. This would be nice after major development has happened. A function object that Clojure created could then not be changed any longer, without restarting the JVM. But such functions can be called directly, without any overhead.
#'user/value
user=> (macroexpand '(value (+ 1 3)))
4