A very popular topic in the DevOps, Developer, and Cloud communities focuses on what language to use. As we all know there aren’t less languages out there, but more day by day. But how do you make a choice between what language makes the most sense?
Today we had a discussion which lead to a relatively simple and limited in scope gunslinger shoot out between a couple of more popular modern languages (NodeJS and Go). Between these languages Kenny and myself (Clint) went at it and built some pretty straight forward tests. These tests included the following aspects.
- Iterative performance for single and multi-threaded apps
- Docker containers and portability
- Cloud Foundry
So where did all of this begin? Within the EMC CODE Stack channels we had a thread that started started based on the following Reddit link What do people mean when they say Python is good for “prototyping”. Although the statement doesn’t directly say that Python is only good for “prototyping”, it of course raises some good questions out there that may help understand strengths and weaknesses among common and emerging languages.
Before going deeper, take a look at the recent rankings from Redmonk among languages. Make sure to pay attention to the “trends worth noting” relating to Swift and Go.
So would anyone actually make the case that Python was good for prototyping and bad for production? Probably not, heck it’s #4 in terms of popularity. A better way to put it might be that Python is good for prototyping and other languages may be better for production. What is it that makes a language good for prototyping?
- Huge longstanding community (lots of relevant contributions to take advantage of)
- Comprehensive standard libraries (include lots of useful functions and methods)
- Interpreted language (possibly minimize coding and real-time compiles)
- Magic factor (high level coding that keeps things simple for developer)
All of these contribute to what some call the magic factor in a language. This may be easily seen and talked about by having quicker development, less lines of code, and seemingly cleaner code. But don’t be fooled!
Less lines != (Better code || Cleaner code)
So don’t take this the wrong way, Python is a great interpreted language with a huge community and a huge amount of production uses. In addition it can be compiled and made super efficient with extra work. But in terms of lines of code, if you can remove some of the magic under the covers and expose more of the code in the application then you have more control and thus can ensure better predictability. So the argument here starts drifting for production uses from interpreted languages with magic, towards languages that are low level, compiled, and that have great levels of predictability. More lines of code may also mean that your code is developer friendly where even the generalist programmer can understand the code.
So who bit the dust today? To say we got definitive results is probably a stretch. We did however compile our testing, packaging, and results here and in Github repositories for reference!
Between NodeJS and Go we built a simple web application that would deploy to Cloud Foundry, in Docker containers, or run locally that would respond to requests with the result of counting to 1 billion. In addition we added multi-threaded counts to test the efficiency of the language in architectures with multiple cores.
So what were the results? From a pure metric perspective, Go was the winner. But why don’t you see for yourself? You can also Google for the comparison that may explain some of the why. You may be asking who cares? Would I actually iterate a billion times? In the grand scope of what a language does this may mean nothing and probably depends on your use case. But there it is.. for something as basic as iterating a for loop on a single processor, Go is around 2x faster. Extrapolate that out and these types of efficiencies can lead to 2x better response times and 2x better consolidation.
Go1Billion at run.pivotal.io (400ms for 1 Billion iterations)
NodeJS1Billion at run.pivotal.io (1000ms for 1 Billion iterations)
So that’s the iterative performance comparison. What else is interesting? Dependencies and footprint.
When firing up the applications on run.pivotal.io we were able to see some basic stats. For the memory footprint, the Go version weighted in at 10MB and the NodeJS version was 55MB. Big deal? Not only does it represent a bigger memory footprint (5x bigger), but it likely will yield extra layers in getting to the CPU (high level statement). How about the size of the container? 35MB for Go versus 32MB for NodeJS. Wait what? Bigger for Go? This actually makes sense and brings us to dependencies.
Since Go is actually a compiled language, it is transferred as code and compiled to a binary file. The application for Go represents 4MB in itself, which makes up for the difference in size (and possibly build packs too). But dependencies are a bigger topic!
How about running these in Docker containers? Well in the case of NodeJS the dockerfile/nodejs image is 500MB (more stuff to update and maintain as well). Talk about some bloat! In the case of the Go application, it has a different story. You could do a like-for-like situation with and leverage the golang Docker image. You would then compile the binary in the container and run the code there or even compile the binary on your own system.
But that’s not very special. How about Go and statically compiled binaries? This means there are possibly no dependencies. In addition there are plenty of cross platforms and architecture capabilities (Linux/OS X/Windows/FreeBSD and 64/32/ARM).
So the value here is that you can very easily create a statically compiled binary that is then loaded into a scratch Docker image or can be ran on most Linux kernels directly. Scratch references that it is a completely empty Docker image with no dependencies. The total size of the minimum Docker container for the Go1Billion app is 5MB!
One more note on the bloat.. If you’re living in the container world now, you may be appreciating some substantial benefits around deploying apps. But at the same time, there may as well be an explosion of contained dependencies that are being used to satisfy your apps. Meaning, since deploying containers is so easy across any Linux distribution, you probably have more distributions and things to consider for updating than you did before. The general bloated container that mimics a traditional app and dependencies looks like OS+Middleware+App at a minimum, and on the contrary with statically compiled binaries, it is simply the App in a scratch container. Keep this in mind! We can do better than treating containers like linked clone VMs (VMware term)!
So what’s the big picture?
Be a polyglot developer. Challenge yourself and apply your developer knowledge to multiple languages! Right language for the right task, watch out for magic. More lines of code may not necessarily be a bad thing! Statically compiled binaries in containers and languages that focus on efficiency are in.