Earlier today I was debating what programming language to use for a small, number-intensive project. I tend to use a variety of languages based around what the project is for and what constraints are in play. For example, most of my “freeform” work is usually in Python just because I like it the best. In some platforms at work, though, I’m constrained to using Java (and old Java at that.) For other things, I use JavaScript (e.g. creating custom GitHub Actions.) If I need a standalone binary blob, I typically use Go because it’s the least painful to me.
While I knew that decent work had been done to improve the performance in Python over the past few iterations, I figured a stupid little check might be fun just to see how different languages compare. As a quick and probably not at all objective test, I decided to see how quickly applications could sum all numbers from 1 to 1,000,000,000.
Note: This test is running on a MacBook Pro with an M2 Pro processor.
Python 3.8
I started with Python 3.8 not because I had any plans to use it (I typically only use 3.8 when I’m writing packages for systems that I know only have 3.8, like some older versions of Ubuntu; everything else I do is in 3.11 or 3.12) but just to baseline what performance looks like in older versions of Python.
So that’s… not terrific. Python 3.8 takes just shy of 1 minute to complete. Let’s see how newer versions stack up.
Python 3.12
Python 3.12 has some solid performance benefits, though it’s not still nowhere near some other languages, even interpreted ones.
Python 3.12 finishes in 30 seconds. A good deal better than Python 3.8, but we can do much better.
Node 21.7.1
JavaScript executing under Node 21.7.1 honestly did much better than I expected. I had no clue that JavaScript runtimes were so fast these days. Not that it makes me want to use JavaScript.
My JavaScript code finished in about 8 seconds. This is a good bump up from what Python was capable of doing.
Java 17
I next tried in Java 17.0.10, which is what I’d be most likely to do it in (though in this case I would likely have sprung for Java 21… no Spring Framework pun intended.)
Java 17 knocks it out of the park in .35 seconds. Not bad at all considering that it’s bytecode going through the JVM. Let’s see what a native binary can do.
Go 1.22.1
I figured Go 1.22.1 would easily be the fastest of the bunch.
Upon seeing these results, I was surprised that I wouldn’t be able to just eyeball the numbers. Java 17 finished in .352445 seconds. Go finished in .358153 seconds. Java was actually faster by 0.005708 seconds!
Takeaways
Does any of this mean anything? Not really. This is a very niche test of performance that I did more for my own interest than because it actually means anything about the performance of one language or another. A couple of things certainly did surprise me, though. First off, I’m still surprised at how snappy Node is as a JavaScript runtime; I had no idea it could give that kind of performance. And considering there are faster runtimes that people are working on, I can see why so many people are using JavaScript for every task under the sun… though I personally still hate writing it.
Even bigger, though, is how quick the JVM is. While the performance for Java and Go are both quite good, the fact that Java isn’t executing directly is really impressive… and makes me feel slightly better about using Java, which I prefer to go for a lot of work that I do simply because I prefer object-oriented languages… and hate always using reflection in Go.