EDIT: Updated version of spray produces better results!
I have been following Go somewhat in recent history, mostly blog posts about it and friends talking about how awesome it is. It intrigues me. It also irritates me. I feel like I’ve spent all this time learning Scala, and it’s impressive functional/ObjectOriented hybrid style, and now this upstart Go comes in and everyone is all excited about it.
I get all huffy reading this article because I figured that Scala wasn’t getting the love it deserves. People see JVM and go, “EWW JAVA IS SLOW.” Which is a load of crap. The JVM is a very mature Virtual Machine compared to many of the newer language VMs. It still suffers from some monolithic problems, which the so-called Jigsaw project would help with, making it much easier to load only the parts you care about. Thus, the JRE would load up way faster, and could be targeted better for specific environments.
Regardless, I decided that, for giggles of course, I would implement the same benchmarks there on my workstation and see how scala measured up.
Scala, like Go, has first-class functions, and first-class concurrency. Having immutable data structures, and a very rich collections API, it’s easy to parallelize calls, and spread that work across as many CPUs (or computers) as possible. Go’s goroutines are roughly like Scala’s Actors. Although, Akka Actors provide a great deal more functionality and visibility into the work going on.
Scala also has the richest available libraries on the planet. Anything that works in the JVM can be used. This of course is a double-edged sword. There are some very terrible Java libraries out there that do “bad things.” There are some excellent java libraries out there as well. I used a couple of them in this case, but primarily I used pure Scala libraries.
spray-can provides me a very performant HTTP server built on spray-io. Spray-io is a low-level connector hooking Java’s NIO sockets directly to actors. In Akka 2.2, there is an expiremental api that has been built in collaboration with the spray folks to get it directly into Akka. I figured that this was reasonably close to what the Go code was doing. I wanted to take the incoming HTTP request, and service it with an actor, having something else manage my threading and all that noise. Concurrency should be through the roof, and I should be at least as fast as Go and Node.
After setting up a code directory, with all three samples, and firing them all up in order, I was sorely disappointed (used raw links, so as to avoid github helpfully syntax hilighting those for you.)
results for spray-can
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
Server Software: spray-can/1.1-M8 Server Hostname: localhost Server Port: 8000 Document Path: / Document Length: 1048576 bytes Concurrency Level: 100 Time taken for tests: 10.698 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 10487230000 bytes HTML transferred: 10485760000 bytes Requests per second: 934.80 [#/sec] (mean) Time per request: 106.975 [ms] (mean) Time per request: 1.070 [ms] (mean, across all concurrent requests) Transfer rate: 957364.87 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 2 1.1 1 6 Processing: 10 105 11.3 104 215 Waiting: 2 10 9.5 7 110 Total: 13 107 11.2 106 217 Percentage of the requests served within a certain time (ms) 50% 106 66% 108 75% 110 80% 112 90% 117 95% 123 98% 134 99% 142 100% 217 (longest request)
These times aren’t horrible, but there about half as good as node.js and maybe 1/3rd as good as Go.
I figure I must be doing something wrong, perhaps there’s more overhead in spray’s “framework” as the Go and Node.js logic. Perhaps I haven’t done my scala code correctly?