Beating Node.js with TCL

This is partly a reaction to people who talk as if Node.js is unique, and partly to test my code against something that has seen production use. There are all sorts of problems with doing this sort of comparison, and while I would have liked to compare more servers, used a better environment, performance tuned everything, done more measurements etc. but I think what I have done is enough to prove my point.So just what is my point? That it is possible (in fact rather easy) to write a high performance event driven server in TCL. I also dislike server-side Javascript and really think the use case for things like Node should be cases where there is a lot of code that can usefully be shared between the client and the server (i.e. you have large chunks of the same code running on both), and not for other, to quote the Node website, “fast, scalable network applications”.

I did some very rough and simple testing. I ran both servers (Node and this)  and Apache Bench on my laptop. To ameliorate the effects of running everything locally, used taskset to run the servers being tested on one core, while Apache Bench ran on another. Of course I also shut down anything not needed during testing, and I am aware that by putting both on the same core I am assuming that both would use negligible CPU when not responding to requests (seems reasonable?).

Both servers ran a minimal “Hello World”. I was not familiar with Node but in many ways it was a fairer comparison than I expected: Node is pretty low level, relies on nested callbacks etc. Comparing Node to something like Tornado would be less fair. I used the version of Node that was in the Ubuntu 14.10 repos, so it is not the latest and greatest, but it is likely to be still in production use by many people. Anyway, it is new enough to compare with my code from 2008!

The code for the minimal TCL app:

source dandelion.tcl

proc hello_world {sock headers settings body} {
puts $sock [dict get $::dandelion::first_line 200]
puts $sock “Content-Type: text/plain\n”
puts $sock {Hello World!}
close $sock
return
}

::dandelion::init handler hello_world port 8081
vwait forever

The Node code is the HTTP hello world from How to Node.

I ran Apache Bench for 5,000 requests with varying levels of concurrency, (except for 2000 concurrent requests I raised it to 6000 requests). The graphs show results:

The x-axis of the second graph shows the natural log of the number of concurrent connections. The concurrency numbers are the same as in the first graph (1, 10, 100, 500, 1000 and 2000).

Dandelion, my light, embeddable, TCL server has higher performance at low load and performance declines more slowly. The identical performance with no concurrency probably reflects the performance of Apache Bench and the OS more than the servers.

If you think this may not be a consistent result, take a look at a graph of the worst performance from Dandelion against Node’s best:

I am fairly confident that that single point where Dandelion does worse is the result of a data entry error: the worst Dandelion results at that concurrency (1,000) is well below the range of the others (at any concurrency), and within the range of the Node results, and the best of the Node results at that concurrency looks suspiciously high too (not so clear cut because Node results varied more at the same concurrency, and there were better results at lower concurrencies).

So does this prove that everyone should be using TCL? No, Node wins when you need to share code with the front end, and there are event driven servers in many other languages. What it does prove is that there is room for TCL event driven servers, and it has satisfied me that Dandelion can perform well. I do no know how its performance compares to TCLHttpd or Wub, which are undoubtedly more sophisticated, let alone to all the other event driven web app servers around (Twisted, Tornado, Lighttpd (which is scriptable in Lua so could serve as an app server on these lines) etc.

It also may encourage me to dust off the Dandelion code, give it a decent name (I am terrible at coming up with names, so suggestions welcome), and put it in a repo (Fossil for TCL code?). Is there a use case for it (answers welcome)?

I also feel that TCL has missed an opportunity somewhere. Its creator was a proponent of event driven programming. It has had an event driven web and app server for at least 15 years but now, when everyone is using event driven internet servers (not just for the web) it is rarely used and few people even consider it.

20 thoughts on “Beating Node.js with TCL

  1. Lighttpd with Lua is outdated by a large margin. Compare with openresty.org instead to see how bad Tcl would perform… Plus, Tcl is a truly awful language – programmed in it for 3 years. No match to the clean language design of Lua. That’s the proper competitor to node.js.

  2. I do not see what the advantage of openresty on Nginx would be against Lighttpd with Lua. Lua on Lighttpd has a huge advantage because it is a standard Lighttpd module, not a third part add one.

    In any case, Lighttpd + Lua was something I just threw in to illustrate the range of options. Lua would be a long way down my list of choices: I would probable use Python because I know better than anything else, but I think there are lots of languages that could be used. Anything with green threads that scale OR a decent callback system: Haskell, Erlang, Python, Scala…..

    “TCL is awful” is a very subjective comment – it really means “I do not like TCL”. Plenty of people love TCL. I prefer it to Lua, personally.

  3. Should have stopped at, “I don’t know node”.

    Using node without multicore is like test driving your new plane by doing donuts on a runway. There is really nothing for the reader to take away from this.

  4. “””Using node without multicore is like test driving your new plane by doing donuts on a runway.”””

    Seems like you know nothing about Node either.

    His comparison is very fair and extends to multiple cores with the same result.

    And there’s nothing special about Node and multicore (not to mention that node apps are single-threaded anyway).

  5. This is like comparing a Toyota Camry with a garage built custom car build for speed. Of course the custom car will go faster, but the Toyota Camry is popular because it’s got a lot of extra features. Yeah, Node, RoR, Django probably won’t beat a custom built C http server, but who cares, the main purpose of those tools is to get a production ready webapp up and running, not to be the fastest server. If I want a fast server I’d hand write one in C, I wouldn’t do it in Node.

  6. Not really – Node code must be a lot more optimised than a simple app server I wrote a while back in limited time. If anything Node is the custom car.

    The first text on the home page of the Node website says “Node.js® is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network application” so speed is obviously a reason people use Node. Node advocates frequently talk as though its architecture is unique: whereas there are lots of event driven web app servers to choose from. My point is that there are lots of options to choose from, not that people should use a particular one.

    Yes, most of the time I would not use my own TCL server – I use Django for most things.

    The point of this test is that TCL is an option. If you like TCL and are doing something which means writing most of the code from scratch anyway, or you have an existing code base, or TCL has good libraries for what you want to do, or you are doing the heavy lifting in C and want glue code and a web server…

  7. @TestTest: The point is that you can build a framework based on these technologies… and it would yield the same advantages.

    I built a web framework in C++. Insanely faster and lighter than everything else I know of.

    When you have a framework that allows you to build a fast server, using the same development flow you’d get from Node/RoR/Django/etc, what are those frameworks good for ?

  8. A simple benchmark ran on your local laptop can’t be used to determine technology X us better than Y. First of all node wasn’t design to share code between the client and the server but instead to provide an event driven platform and abstract multi threading program one layer deep. Javascript was the choose language since it had natively callback support which matched exactly with Ryan’s idea of an event driven async platform, I suggest you read a bit more about the history of node

    Taking everything else aside, what’s the ecosystem for tcl? Does it support json natively? Does it have a package manager? How does the stdlib looks like? What about database drivers? You can’t judge a platform just on its speed based on questionable benchmarks, there is much more to it.

  9. I did not say that was the reason Node was written, I said it was the use case for it that I agree with. If you just want an event driven platform, there are plenty of others to choose from: Node’s USP is that it can share code with the front end.

    Also, what advantage does Javascript have over other languages that support callbacks either natively (like TCL) or through an extension (Python + Twisted)?

    If you read the post in full you will see that I do not think the benchmark proves anything more than that TCL should not be ruled out on performance grounds. How well fitted it is for a particular application will depend on that application. To answer your specific questions: it has JSON support in the standard library (and the parser is a C extension, so will be fast), it does have a package manager, a reasonable standard library. I am not up to date on TCL database drivers. You would really get better answers to these questions on comp.lang.tcl.

  10. Diogo thinks that a programming language should have a package manager as a minimum requirement, and that’s why he will never be able to solve real problems, only assemble solutions written by others.

    The programming profession really is full of lazy people these days.

  11. Great article. Can you post the scripts you used to run the benchmark? I would like to run a similar test with a custom web server we have built.

  12. “I also dislike server-side Javascript” – Okay. Why? Honestly, the benchmarks that you present confirmed exactly why you shouldn’t try to use TCL to “outperform” Node – minimal improvement from a language with a fraction of the community and support.

  13. While what you’ve done is proven that TCL is as capable as Node (and even more so, possibly) is show that people have choices. But Node is accessible to a lot more individuals, as compared to TCL. Unix shell scripting is powerful, but tools have been written where the shell scripting falls down. At a core level, you have proved your point. But it shouldn’t be construed that Node is a poor choice for server development. I disagree with your statement that “I also dislike server-side Javascript and really think the use case for things like Node should be cases where there is a lot of code that can usefully be shared between the client and the server.” Meteor is attempting to do this and, IMO, fails. Trying to execute the same code (server and client) is going to fail miserably unless the code is written to only use the basic functions of the language. Things like accessing the filesystem aren’t available natively on the client, so if you wrote code to access it on the server it will fail on the client. It has its uses. It may not be the best server option, but I’d say that it’s still a better option than Netscape SSJ. The old days of SSJ are beyond us, with a program like Node. It runs on Chrome’s V8 (arguably one of the fastest, if not THE fastest, JS engines available) so it performs well when written well.

    It’s possible to write crappy software in any language. I could write one in TCL, I’m sure. But what does this prove? Honestly not much.

    I’m not of the camp that says Node is unique. But it is valuable. I can write Java and C/C++, but why would I when I can use Node which is MUCH FASTER to get up and running.

  14. Yeah, the new crowd stay reinventing things from the past in ways that range from horrible to (uncommon) amazing. Tcl was used for agent-oriented programming & AI in the early-to-mid 90’s where the code moved to the data to save bandwidth and accelerate performance. Around same time, it was used in web app framework using AOLserver. AOL was one of the highest traffic websites, which implied its server and TCL were more than an option. Those that favored getting things done over latest and greatest continued to use it in high end networking gear, app customization, shell-scripts, and so on with many platforms all using same language.

    Now, I have something better than Tcl for almost everything and a number are even more readable (esp Python stuff). Yet, the main advantage of Tcl is it’s ridiculously simple to the point that anyone can implement it anywhere. Add all the existing code and you have one of the most portable programming solutions ever invented. Makes for a great future-proofing tool if used judiciously.

  15. @Fernando: its all in the post and the link to the Node code.

    @Will, the point is that there are multiple viable choices and TCL may suit some use cases. Personally I would probably use Python for most things people use Node for, and if Python was not good for a particular case I would look at multiple solutions before picking JS.

    @Brett, I think we agree on most things, but you like JS more than I do. What I really wanted to do was remind people that they do have a lot of choices. I would not use C or Java for web apps either (but I may use other JVM languages).

    @Nick, agree entirely.

  16. Can you run this again using io.js instead of node? (You won’t need to edit your code) We noticed huge performance gains as iojs I’d using the latest V8 engine and it’d be interesting to see if that closes the gap.

  17. @Diogo a simple benchmark run on a laptop is not expected to be scientific, but it is nevertheless very useful and informative.

    I once sat in a programming class taught by Brian Kernighan, who ran an informal shootout between a bunch of languages—Java, C, perl, FORTRAN—mostly to put those performance differences in perspective. It was incredibly educational, even though it wasn’t run on 100 different platforms and analyzed using ANOVA or whatnot.

  18. tcl is a very thin language, using it to put a layer over epoll and then discovering its faster than node is a “shrug”. You’d expect that. It doesn’t even implement real garbage collection, AFAICT. So, your tiny garbage-less demo app – runs like hell. Start adding some features, like code, and then see whether v8 or tcl run better as the garbage accumulates. Also, node/libuv has good async support for a lot more than what you can do with epoll, which is basically just network connections, like kicking blocking filesystem i/o out onto a thread pool. Change your demo app to read a bit of text from FS… does dandelion deal with that? How robust is its HTTP parser? Does it handle upgrade to websockets, multipart streaming APIs for huge data, etc.? As other posters have said, anyone can write a faster binding to epoll in a lower-level language, or just C, than node… but node makes as lot more i/o than just sockets async. And “how do I install node on ubuntu” is a weird question to ask in forums, but I’ll answer: iojs.org and nodejs.org both have pre-compiled linux tarballs. Download, untar, run.

  19. Sam, I asked the person who asked me to try io.js whether it was an easy install on Ubuntu – he wanted it done, so I do not see why the onus should be on me to look around the io.js site? If its not in the Ubuntu repos, its a reasonable question. Also, this is not a forum, its a blog. More to the point, its my blog and I will ask whoever I like, whatever I like on it!

    It depends what you mean by “proper” garbage collection…

    If you want file system reads using a thread pool, TCL has threading and a thread pool package.

    The problem with making a test more realistic, is that then you have to make sure that both implementations are equally optimised, and you really need to test a range of tasks (because two very different languages may excel at different tasks), etc.

    This is not supposed to be a general test of TCL vs JS for event driven web apps: its purpose is much narrower as I explained in the post (and countless times since!). If you want a more realistic test something like pulling some data from a database and returning it as JSON, or a minimal chat app etc. would be better, and you would probably test Node against a more mature and complete (and optimised!) TCL server like Wub or TclHttpd.

    Frankly, if anyone thinks that this means they should use Dandelion instead of Node on the basis of my tests, they are complete idiots. If, on the other hand, someone thinks “um, I have a project for which I need an event driven server and TCL looks like a good fit for the task in hand, and it looks like it can perform reasonably, lets do some further testing” that would be a sensible thing to take away from this post.

Comments are closed.