TL;DR: My simple web API is
50X faster (and uses 10X less memory) after being rewritten in #Golang
API vs Pat/Go).Update: +James Tucker
pointed out that jmeter might not be fast enough to benchmark my Go web API and I should have used wrk. I tried as per his suggestions and he's right, I'm getting a much higher throughput. I originally reported a 10X improvement, it really was a 50X improvement.
4 threads and 20 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.88ms 8.41ms 36.76ms 78.48%
Req/Sec 0.99k 97.22 1.00k 99.05%
93852 requests in 20.00s, 19.15MB read
I've always liked #Golang
and I'm impressed by how much/fast the Go core team keep on improving the language, implementation, and documentation. Things are much easier now in Goland.
With version 1.1 and 1.1.1 being released I wanted to see how easy it would be to start using Go in my daily workflow. By that I mean that until now, I only "played" with Go, I wrote a few small apps for fun but no nothing mission critical.
My goal was to write 2 web json APIs, one using Rails and a port in Go. The API would be simple and do a few things:
* extract the Authentication header from the request
* verify it against the DB (mysql)
* and render a json representation of the user object.
The Rails app was written in no time but the Go one took a bit longer. I chose to use the Revel framework since I like Play (Scala framework) and Revel is a port. Unfortunately that was a mistake. The lack of documentation, the odd features and the lack of simple cross platform deployment made me give up the framework approach. To be honest, I should add that +Andrew Gerrand
(easily) convinced me that I should keep it simple and just stick to pure Go. (discussion over on #shitfire
Rewriting the few things and conventions I like took me less than an hour or so, I ended up using +Blake Mizerany
's Pat package (Sinatra like pattern muxer https://github.com/bmizerany/pat
My code is simpler, more flexible and easily cross compiles.
The next step was to benchmark the 2 APIs under load. I knew that Go was going to be considerably faster and I knew that benchmarking on my own machine wouldn't yield scientific results. That said, I fired up jmeter and run 20 threads/users making 1000 queries and looked at the results.Go web API:
4691.79 requests/s (see update)
Memory usage: 10MBsRails (Rails 4, Ruby 2.0, production mode, puma web server):
Max: 178,552ms (442ms most of the time but big spike around 15,000 requests)
Throughput: 88.9 requests/s
Memory usage: 117MBs
(server was warmed up and the benchmarks were run multiple times, delta stayed within 5 ms)
The numbers in themselves aren't that important. Running the same benchmark on a linux machine and on AWS will obviously yield slightly different numbers. What's interested in that the performance difference between Go and Ruby in this specific use case is 10X.
10X is a LOT. There is no way I could tweak the Ruby code to get close to the Go numbers. For many use cases, it wouldn't matter, but if performance is important, then choosing Go might make a lot of sense. Note also that the response time is more than 10X faster with Go.
Finally, here are more scientific and detailed comparison benchmarks (Rails, Sinatra, Revel, JRuby etc..): http://www.techempower.com/benchmarks/#section=data-r5&p=3q