Recently at work we were playing around with the following quiz question asked by IBM https://www.research.ibm.com/haifa/ponderthis/challenges/May2015.html
After a bit of effort a colleague and I have arrived at two solutions, one in GoLang https://gist.github.com/walesey/e2427c28a859c4f7bc920c9af2858492#file-main-go-L57 and the other in Java https://gist.github.com/boyter/42df7f203c0932e37980f7974c017ec5#file-puzzle-java-L63 with the performance critical method for both being playGames in Java and game in GoLang (both linked in above).
The Go program is almost a literal copy of the Java one, and yet its runtime is ~6 seconds whereas the Java one is about ~26 seconds (on my local machine). Similar numbers were replicated on a few other machines with the Go program being about ~5x faster.
The Go program is compiled using 1.7.5 and Java using version 1.8.0_65 both running on macOS Sierra 10.12.3 on a late 2013 retina Macbook Pro with 2.6GHz i5 CPU.
Why is it that the Go program is 5x faster then the Java one when most benchmarks indicate that Java should about the same runtime? It is just basic math in a loop so it seems that they should run about the same time. I could understand a second or so for the JVM start time, but this seems off.
Both programs use pretty much the same loop. All of the possible permutations of game results are created and iterated over for each starting amount of money. It just seems that for any number of looping operations in the main loop that Go is running rings around Java.
I understand that this is a "micro" benchmark, but I am wondering why exactly the Go code is massively outperforming the Java code. Is it just that Go for simple loops/math is more efficient and hence faster? Is it able to unroll the loop perhaps (although this seems unlikely to produce such a massive difference)?
If not how should you structure a Java program to get the most performance out of a simple loop and math operation?
EDIT - Thanks to Dolda2000 I have modified the Java version. It is now about the same speed as the GoLang version. Indeed the issue was the the games were created causing the Java version to have to simulate more games to determine if the game went long enough. With the changes it is running in about ~6 seconds now and has restored my faith in Java.
Update - Here is an expanded essay which discusses the background of this question in further detail.
Golang vs Java: Performance Though it allows Java to run on any platform, this virtual machine reduces its speed. Golang has the upper hand. In Golang, testing is easy, and the user experience is better. Golang is quick because it is similar to 'C'.
Fast compilation times is a major design decision for Go, so one can expect they take a large amount of steps in order to fulfill it. They tailored all of their syntax and language semantics so as to facilitate fast compilation. One of the main things that make Go compile fast is its dependency management.
Java is the older and more widely used programming language. It is object-oriented, has a larger community—thus library, and relies on the Java virtual machine (JVM). Go, or Golang, is newer, supports concurrency, is more readable, and is not object-oriented.
Go makes it easier (than Java or Python) to write correct, clear and efficient code. Choosing a programming language isn't easy. The separate features of a language may look great at first, but it takes time and experience to spot the drawbacks.
As it turns out, your programs aren't as equal as you believe them to be. I instrumented them to see how many games (that is, individual betting rounds) they simulated, and while the Go version simulated 1 612 629 805 games, the Java version simulated 12 323 903 502 games, almost an order of magnitude more.
On my machine, turning off multithreading for more predictable results, the Java program clocked in on around 75 seconds, and the Go program on 12.5 seconds. Matching that against the total runtime, it appears that the Java program is actually slightly faster per simulated game, at about 6.1 ns, as compared to 7.8 ns for the Go program.
Not sure yet why they simulate such vastly different numbers of games, though. Perhaps the way the Go version generates the rounds simply happens to find a lot more quicker terminations.
EDIT: Actually, that last guess makes a lot of sense. The Go version starts off by modulating the initial rounds of a game, while the Java version starts off by modulating the last rounds of a game (in other words, looking at the list of rounds as a list of increasing 11-digit base-3 numbers, the Go version is little-endian, while the Java version is big-endian, so to speak), so the Java version will have to simulate through a lot more identical starts to get to the variations that terminate. I haven't tried to verify this hypothesis, but I'm sure enough about it that I don't feel the need to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With