

Clojure's edge on Node.js - swannodette
http://dosync.posterous.com/22397098

======
felixge
Node's requests/second scale linearly with the amount of processes used. Here
is an example for doing that:

<http://gist.github.com/430932>

So node would probably outperform the given clojure example by 13.5K req/s vs.
8.5k req/s (YMMV).

It is also worth noting that clojure library used (aleph), does not seem to
support streaming the response.

The most interesting aspect is clojure's support for futures, and Javascripts
lack of such. If you're willing to trade speed for cheap syntactical pleasure,
clojure has an advantage here : ).

edit: Who down voted this / why?

~~~
swannodette
Clojure should also scale _very well_ on a real 4 core machine (it's the JVM
after all). And I wouldn't be surprised if was neck-to-neck with Node.js.
Without numbers it's just talk (I don't have access to a real four core
machine so I can't say myself).

Also I note that your code is very verbose for managing multiple cores while
the Clojure version is pretty much identical to the original Node version.

So much for cheap syntactical pleasures, eh?

~~~
felixge
The clojure example is using a library. There are libraries for node that make
the process handling just as easy.

Anyway, I think you are mistaken if you believe a clojure http server could
outperform it's node counterpart. I mean node is really just a set of air-thin
Javascript bindings to some pretty good C code / native system calls. Node's
http parser is also written in C.

~~~
icey
Claims are pointless without backing benchmarks. Being written in C is no
guarantor of performance.

~~~
felixge
<http://developer.yahoo.com/yui/theater/video.php?v=dahl-node>

Shows node to outperform nginx for serving a static 1 MB buffer.

~~~
icey
What does that have to do with performance under multiple processors / cores?

~~~
felixge
Node's scales linearly if you add a process per additional core (see my
original link). That does allow for a certain extrapolation from a single
process/cpu benchmark.

Anyway, you are correct - a real benchmark between node / clojure would be
needed to proof my argument. But I think reasoning about the underlaying
machinery is a better use of my time in this case. Bugs aside, there is no
reason to believe Clojure could perform faster if it ends up doing more work
under the hood. And from what I can tell, there are many more layers of
abstraction in between clojure's http implementation and the native system
calls, than there are for node.

------
IsaacSchlueter
That's not really a valid test. NodeJS can easily make use of multiple cores
with child processes, but by design, it's a very low-level API, so that stuff
isn't used by the HTTP server by default. In other words, it doesn't _assume_
you want to use multiple processes for your HTTP server. Maybe you want to use
them for something else. (Like, if you have an HTTP server and an IRC server
sharing the same box, and you want to make sure they are isolated from one
another, or something.)

Check out <http://github.com/kriszyp/multi-node> or
<http://github.com/extjs/Connect> for examples of web servers that will blow
Clojure out of the water in this benchmark.

When leveraging multiple cores, NodeJS is closer to nginx in terms of
performance than it is to Jetty or Django. (Of course, nginx uses almost no
memory, and NodeJS has this big honkin VM juggling JavaScript contexts, so
it's not as space-efficient.)

~~~
barrkel
Multiple processes are an inefficient way of making use of multiple cores.
Either you pay memory overhead to duplicate state, or you pay IPC overhead to
get access to shared state of some kind.

~~~
sedachv
In many cases it's more efficient precisely for the reasons you mention. Data
duplication removes locking and cache coherence penalties. If you structure
your program to communicate by message passing over IPC (for example, via a
database) you get those benefits, plus possibly more efficient mutual
exclusion mechanisms (CAS, tuned MVCC, etc).

Also, don't forget that GC penalties are going to be paid by all threads in a
process.

~~~
barrkel
Multiple threads in a single process doesn't preclude data duplication in any
way; and optimal IPC is always going to be slower than optimal in-process
message passing. I wouldn't consider bringing a database into the
communication channel to ever approach optimality.

As to GC, that presumes that you are using GC. For a request/response style
server application, there are more optimal techniques than GC: make sure all
request-associated allocations come out of a per-request heap, and you can
free that heap en masse, in one go, once that request is done with.

------
mhd
The main problem both systems have is that they're relatively immature.
node.js is still undergoing heavy development, with a few breaking changes
every couple of releases (although it's slowing down in that regard). And
aleph basically just came out, right?

So I'd regard both as "things to watch" or bootstrapping solutions, not
something I'd use for my Global Thermonuclear War management system.

~~~
prospero
I wrote Aleph this weekend, so yes, it's very new. However, right now it's
just a few hundred lines around Netty, which is anything but immature.

I'm certainly not claiming it's flawless, or that there won't be any breaking
changes in the future. However, I think this is a great example of one of
Clojure's strengths: even brand new projects tend to have solid, well-tested
foundations.

~~~
mhd
Well, the same could be said about node.js and libev, to be honest.

------
cageface
Threads aren't going to scale to the same number of concurrent clients as an
evented system though. Isn't that the whole point of things like node? Serving
huge numbers of relatively lightweight requests?

~~~
donw
If you're doing thread-per-request, than no. If you've got an event-driven I/O
system backed by a thread pool for executing parallel requests, than yes.

I set up a Rack adapter through Jetty 7 on JRuby that works exactly like this.
The SelectChannelConnector is event-driven I/O, and the QueuedThreadPool
handles requests in parallel.

The result? 6k req/sec on my laptop for a simple Rack app, basically the same
as what's shown in these examples, since my machine is quite a bit slower, and
I've clocked 10k/sec on faster machines.

(By the way, if you're interested, the Rack handler is in a gem called
'mizuno', and is at <http://github.com/matadon/mizuno>)

~~~
gizmomagico
_If you're doing thread-per-request, than no._

Pro-tip: _This_ time, you were looking for the word "then". But "than" really
exists too, and is even used sometimes (when comparing things)!

Yeah, downvote away.

~~~
donw
I don't usually make that mistake; I might be losing a little off the top end
of my English ability, since I'm living in Japan right now.

But you're correct. :)

And I also don't downvote anything except outright spam; everybody has the
right to an opinion, whether or not I agree with it.

------
jherdman
That's pretty neat. I've been meaning to learn about Clojure. This is just the
thing I needed to encourage me to get off my duff.

------
sjs
This looks really nice! Our server is based on Node right now but we've had
the idea of porting to Erlang later. Maybe Clojure + Netty is another option
to explore.

------
ericb
I'm not familiar with node.js. What engine is used to run the stand-alone JS?
Is it inevitable that is it limited to a single core?

~~~
ionfish
Node uses Google's V8 virtual machine, which they built for Chrome. JavaScript
is single-threaded, so yes, unless you spawn a child process it'll be limited
to a single core.

~~~
ericb
Is being single-threaded part of the JavaScript spec?

~~~
ionfish
Only implicitly: no threading model is documented in the ECMAScript Language
Specification [1], and no widely-used interpreter implements threading. The
new Web Workers specification adds something like threading to JavaScript in
the context of the browser [2], and Node intends to add support for this on
the server as well [3]. Have a look at [4] for a nice article on threading in
JavaScript.

[1] [http://www.ecma-international.org/publications/files/ECMA-
ST...](http://www.ecma-international.org/publications/files/ECMA-
ST/ECMA-262.pdf)

[2] <http://www.whatwg.org/specs/web-workers/current-work/>

[3] <http://nodejs.org/>

[4]
[http://odetocode.com/blogs/scott/archive/2007/01/08/javascri...](http://odetocode.com/blogs/scott/archive/2007/01/08/javascript-
and-threading.aspx)

------
Periodic
I initially had Javascript off, so I figured it was part of the joke when the
space for the shortest Node.js web server was empty. When the shortest Clojure
web server was also zero characters, I got suspicious.

Don't forget to add a non-Javascript version for people without Javascript
(rare) and bots (common).

------
c00p3r
btw, did anyone have a look at node's sources? It is terrible mess.

Actually, using something like nginx-pr (a-la apache's apr) would be much
better solution than this bunch of hacks.

But people never listen. ^_^

------
kouPhax
Not a massive fan of clojures syntax though - that example is like bracket
city!

~~~
compay
Syntax aesthetics shouldn't be an important metric for technology choices.

~~~
Calamitous
Why not? Aesthetics improve readability and maintainability, (probably)
trading out for less raw speed. If I'm less concerned about performance than
maintainability, why _shouldn't_ aesthetics play a large role in my decision?

~~~
compay
Because it can lead you to make arbitrary choices. People usually disagree
vehemently on matters of aesthetics. I didn't say it shouldn't play any role,
it just shouldn't be an important one. Focus too much on syntax over semantics
and you're bikeshedding.

~~~
LaPingvino
I get lost easily in non-lisp-code nowadays =x The unwritten standard for lisp
indentation works out quite readably

