Google paper comparing performance of C++, Java, Scala, and Go [PDF]
days2011.scala-lang.org
days2011.scala-lang.org
"We find that in regards to performance, C++ wins out by a large margin. However, it also required the most extensive tuning efforts, many of which were done at a level of sophistication that would not be available to the average programmer.
Scala concise notation and powerful language features allowed for the best optimization of code complexity. The Java version was probably the simplest to implement, but the hardest to analyze for performance. Specifically the effects around garbage collection were complicated and very hard to tune. Since Scala runs on the JVM, it has the same issues.
Go offers interesting language features, which also allow for a concise and standardized notation. The compilers for this language are still immature, which reflects in both performance and binary sizes."
[citation needed]
Personally, I often do an initial write of a program first in Python and rewrite in C/C++ if necessary. Usually, the first unoptimized C++ implementation is 10-100x faster than an optimized Python implementation.
I don't find that to be true. I use more sophisticated algorithms in "higher level" languages because I can implement them in about the same time it takes me to implement less sophisticated algorithms in "lower level" languages.
> Personally, I often do an initial write of a program first in Python and rewrite in C/C++ if necessary. Usually, the first unoptimized C++ implementation is 10-100x faster than an optimized Python implementation.
Yes, but the c/c++ rewrite gets to take advantage of what you learned in the python AND you only do the c rewrite when you need the speed and are willing to pay the time penalty to get it. (There's some extra cost to the c version otherwise you'd always do c and never do python.)
These are factors not related directly to the language, but level of understanding, time, and money. An experienced C++ programmer can probably implement the 'better' algorithm equally quick in C++.
There's some extra cost to the c version otherwise you'd always do c and never do python.
Not necessarily. In an existing project in a non-C language, the C implementation will require you to write and maintain a binding as well. That could be a good reason to use Python/Ruby/Prolog/whatever by default, unless it's not fast enough.
While there are programmers who can implement a given algorithm faster in C++ than Python, I suspect that they're the exception (among folks who know both).
> In an existing project in a non-C language, the C implementation will require you to write and maintain a binding as well.
If there's no c penalty, other languages wouldn't have been used as often for said existing projects to begin with.
My view is that each language has both strengths and weaknesses, so different languages are better suited to different tasks depending on factors like available personnel, existing sw, ease of implementation (to reach an acceptable level of performance).
There are programmer performance factors directly related to language differences.
The mainstream example is efficiency of DSLs. Try writing a complex regexp in a standard regexp DSL that originates from Perl and in plain C/C++.
The less known example is writing complex business logic in Clojure vs Java. The very fact that Clojure version lets you examine much more business logic in one screen without scrolling puts much less pressure on programmer's concentration and memory.
Thus the very ability to write complex business logic without lots of bugs in tight time constraints depends on a language.
The C++ Bubblesort will have better worst-case and best-case performance, but worse average-case performance, for large inputs.
But this is all irrelevant, because in Python, you'll be using Timsort[1], which I'm pretty sure is implemented in C anyway. In C++, you'll be using std::sort unless you have a template allergy. If you use the Gnu stuff, that will be Introsort[2]; if MSVC, Quicksort.
[1]http://en.wikipedia.org/wiki/Timsort [2]http://en.wikipedia.org/wiki/Introsort
I believe this is a different definition of "tuned" than most people use.
std::vector<LargeStruct> things = GetABunchOfThings(); // Why does this take so long?
// Oh well, time to sort
std::sort(things.begin(), things.end(), LargeStructComparator());
...and then your program takes forever and your profiler tells you that 99.9% of your time is spent in LargeStruct's copy constructor. And then you refactor to use pointers and teach the comparator to dereference them, whatever, it's fine, but the naive Python version will just work.Of course, after c++0x becomes widespread this problem will go away. Yay move constructors.
It's also likely that the optimizer will have GetABunchOfThings construct its result once in the caller's "things". Lambdas are C++0x, so maybe move constructors (another C++0x feature) will be employed instead.
I am a fan of C++, so I liked the first part (that C++ wins, which is not really surprising). However it was not clear if the sophistication they talk about is regarding the Google's internal data-structures or the list of optimizations listed in Section VI-D? Many of these optimizations are not big surprises to a C++ programmer (where possible use hash_map, vector instead of list, initialize data structure outside of loop and try to reuse, and so on).
Overall, I do greatly appreciate the effort and their sharing the results.
In fact, looking at the list of optimizations on page 9, the only one that involved a low-level understanding of the machine and resulted in a significant improvement was (unless I missed something) the use of InlinedVector (and this is only barely such a case - lots of C++ codebases have a similar class).
By the way, std::list::size() is defined to be O(1) (see http://www.kuzbass.ru:8086/docs/isocpp/lib-containers.html#l...). While it's possible, of course, to define a linked-list where the size operation is O(n), that's not an implementation of std::list. Hence, there was no difference when std::list::empty() was used - although I agree it's better to use it, since it's more meaningful.
Scala without any optimization is still better than Java with all the ninja-skills black magic applied. Scala optimized is pretty good - "just" 2.5x slower than C++
Does that mean the java version would have been just as fast? It seems in general though that Java is just as fast as C++ unless you are an ultimate C++ expert.
If you read the paper, you'll note that the Scala author significantly changed the structure of the algorithm to conform with the way Scala does things (recursion, etc). So it's sort of an apples-to-oranges comparison as far as Scala's concerned, too bad they couldn't write ugly Scala code that would give a better comparison.
Note that Jeremy deliberately refused to optimize the code further, many of the C++ optimizations would apply to the Java version as well
It sounds like Jermey didn't spend much time tuning the Java version either.
They note in the paper this distinction, but it should be marked in the table, because I'm sure 99% of people don't see that. Sloppy!
"... the benchmark also used a HashMap<Type, Integer> when it should have just stored a primitive int in Type; it also used a LinkedList where it should have used an ArrayDeque."
http://jeremymanson.blogspot.com/2011/06/scala-java-shootout...
Pentium IV - really? Let's see - we've had Core, Core 2, Nehalem and Sandy Bridge since then, not to mention 3 in-between process shrink versions of the same architecture.
Perhaps we could pass the hat around so that Google could afford a Sandy Bridge workstation and discover what these interesting results look like on an architecture that dates from some point in this decade - or at least, at some point in the previous decade.
This stuff actually, really, truly makes a difference. And not necessarily in favor or against any particular one of these languages...
Then, measure the algorithms that run on one core, anyway, on the P4 and the latest Core iX. Your slow languages won't be faster than your fast ones just because the quoted changes were introduced to the processors in between.
It's actually quite hard to know what a given piece of code will do on a given microarchitecture even if on average it runs everything X% faster - you may find you're the bit of code that bites the big one and run X% slower on the new microarchitecture (e.g. you were depending on branch mispredicts being cheaper than they are) or suddenly your code runs way faster than competing codes (e.g. you're the superstar running 2X% faster because a sudden increase in ILP exposes that you've got a main loop full of independent operations).
On the other hand, the processors of 2000 are quite a bit different to the processors of 2011, and anyone who thinks that they can prove that this is irrelevant to the experiment in question by pure reasoning (rather than experimentation) is a moron, plain and simple.
Having tracked individual embodiments of algorithms over architectures from the P4 to Sandy Bridge, I can assure you that tradeoffs between particular approaches on an given problem vary wildy based on changes in microarchitecture.
Overall, there's a steady improvement and perhaps these languages are all gaining exactly the same (despite a substantial improvement in effective ILP from P4 to Core 2)... but I don't know that for sure and neither do you, despite your sophomoric self-assurance.
If only they could do the experiments on a processor from the last 5-6 years...naturally someone else could do the work for them, but why bother?
This is much more interesting considering our current reality.
I once gained 10% speed up on two cores by changing just two lines of our optimization tool written on Haskell.
You're right that there may not be much improvement, but I've seen the google dense_hash_map do better sometimes than the basic SGI hash_map.
Edit: the code repo has the unoptimized versions. Oh well, at least I can get the satisfaction of seeing the 30% myself :)
afaict Yes, they left the internal version out completely (besides the changelog).
Notice C++ Dbg has the same wc -l as C++ Opt.
Here is the output of sloccount:
SLOC Directory SLOC-by-Language (Sorted)
595 java_pro java=595
591 java java=591
488 cpp cpp=488
328 python python=328
0 go (none)
0 go_pro (none)
0 scala (none)
0 scala_pro (none)
generated using David A. Wheeler's 'SLOCCount'.
Compare it to the paper. Benchmark wc -l
C++ Dbg/Opt 850
Java 1068
Java Pro 1240
Scala 658
Scala Pro 297
Go 902
Go Pro 786 Benchmark GZ Bytes Factor (wc -l Factor from paper)
Java Pro 5198 1.65x 1.9x
Java 4403 1.40x 1.6x
C++ 4229 1.34x 1.3x
Go 3768 1.20x 1.4x
Go Pro 3259 1.03x 1.2x
Scala 3138 ==== ===
Python 2755 0.87x ????
Scala Pro 1929 0.61x 0.5x Bench files blank comment code
cpp 4 145 236 501
go 5 153 239 546
go_pro 5 132 233 457
java 8 151 352 605
java_pro 8 157 351 772
python 2 110 204 334
scala 2 104 224 384
scala_pro 2 59 62 230Challenge to the pythonistas: rewrite the python version so it uses the C++ code under the hood using scipy weave
Edit:
The Python version can be found at http://code.google.com/p/multi-language-bench/source/browse/...
http://code.google.com/p/multi-language-bench/source/browse/...
No, there are no ctype libraries in there.
http://code.google.com/p/multi-language-bench/
I confess that I do not know a lot about Scala, but it looks like some of the functional aspects of the language allow for some savings in lines of code.
If I had the time right now, I doubt that it would be too difficult to shrink the python version a bit. Just taking a glance at it, I don't see a single list comprehension throughout their code. I am sure there are some other language features that could probably have been better leveraged as well.
Just out of curiosity, where did you get the 626 loc and the 297 loc from - I tried looking but I can't find it anywhere. Though that could just be a product of my lack of sleep right now.
I suspect the author gives it out as a reference implementation.
Of course, in most cases, you're right. Given an infinite pool of very good programmers and infinite time, C will almost always be faster than most other languages. But in reality, you have a very limited of maybe not so good programmers, and the project needed to be done yesterday :)
http://www.elis.ugent.be/JavaStats
http://www.bestinclass.dk/index.clj/2010/02/benchmarking-jvm...
http://isthisclojure.blogspot.com/2011/02/benchmark-clojurej...
https://docs.google.com/present/view?id=0AS8emH3-FLt3ZGRtbWJ...
Examples of benchmarks gone awry:
http://stackoverflow.com/questions/6146182/why-is-my-scala-c...
http://groups.google.com/group/scala-language/browse_thread/...
http://download.oracle.com/docs/cd/E15289_01/doc.40/e15060/t...
A less popular language - or one that is usually used for less-critical code - will suffer.
C++ wasn't always so fast. See Kernighan and Pike, "The Practice of Programming" for a simple example where Java surprisingly beats C++.
I would love to use Digital Mars D or Scala for scientific applications if they had compilers/runtimes as good as C++.
Perhaps one day the LLVM backend would be useful as a universal backend for many less popular languages.
Looks like C++ is still one of the performance kings among current programming languages. Can't say I enjoy using it though.
It also looks like Go and Scala have a long way to go on compiler optimization. Not surprising, since they're young languages compared to C++ and Java, and it's a decent amount of work to optimize the higher-order constructs they provide.
It'll take a pretty significant shift to change this. Something like 100+ core cpus each running slow, might be such a shift.
Being surprised that C++ is faster than newer languages which provide all types of abstraction is like being surprise assembly is faster than C++.
All the C++ hate is blown way out of proportion. C++ is a great tool when you need performance. Tuning C++ isn't that difficult either. Contrary to what a commenter said above, steering clear of C++ at all costs is not what you should be doing. You should know when and how to use C++. It's useful more often than many people here seem to think. Especially if you develop for mobile devices.
edit: I just got to section VI of the paper, which does some gc tuning. In my playing, I also found about a 20% speedup by changing the HavlakLoopFinder.UnionFindMode class to being a statuc class.
Although you shouldn't necessarily think "Oh, so that's how that's done in this language."
http://code.google.com/p/multi-language-bench/source/browse/...
Can't wait to see Modula-3's numbers…
"they"? The space monsters who run the universe?
for(std::vector<std::string>::iterator it = vec.begin(); it != vec.end(); it++)
you can just do:
for(std::string x : vec)
So awesome!
for (auto &x : vec) { }
Using https://github.com/Rip-Rip/clang_complete you can use smart context-aware completion with vim/emacs.
For example if you had this code:
vector<string> vec; for (auto &x : vec) { x
and then if you typed period (.), it would show members from std::string. I'd say it works better than Intellisense (it's very precise and you use the same parser for code completion and final compilation).
Thanks for the tip, will update my copy and try it out this weekend! :D
On the other hand, it does compile fast.
Submissions must be in English and at most 12 pages total length in the standard ACM SIGPLAN two-column conference format (10pt).
Nearly all academic papers in computer science and engineering look like this. Not to say they've converged on an optimal format, but it wasn't exactly a creative decision on the part of these authors.