Problem
I’m running a fast stress test on two (somewhat) hello world node.js and asp.net-core projects. Both are in production mode and do not have a logger attached to them. The end product is incredible! Even after doing some more work, ASP.NET core outperforms the node.js program, which is only rendering a view.
Node.js, express, and the Vash rendering engine were used.
This endpoint’s code is as follows:
router.get('/', function(req, res, next) {
var vm = {
title: 'Express',
time: new Date()
}
res.render('index', vm);
});
As you can see, it does nothing apart from sending current date via the time variable to the view.
ASP.NET Core is being used, with the default template targeting dnxcore50.
However this app does something other than just rendering a page with a date on it. It generates 5 paragraphs of various random texts. This should theoretically make this little bit heavier than the nodejs app.
This is the action method that was used to create this page.
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
[Route("aspnet-core")]
public IActionResult Index()
{
var sb = new StringBuilder(1024);
GenerateParagraphs(5, sb);
ViewData["Message"] = sb.ToString();
return View();
}
Gorgi Kosev’s idea has been implemented.
npm install -g recluster-cli && NODE ENV=production recluster-cli app.js 8 npm install -g recluster-cli && NODE ENV=production recluster-cli app.js
My eyes can’t believe what they’re seeing! It can’t be true that asp.net core is significantly faster than nodejs in this little test. Of course, this isn’t the sole criteria for comparing the performance of these two web technologies, but I’m curious as to what I’m missing on the node.js side.
As an experienced ASP.NET developer who wants to use node.js in personal projects, this is putting me off a little because I’m concerned about performance. I just want to prove to myself that node.js is faster than asp.net core (in general – as evidenced by numerous other benchmarks) (to encourage myself in adapting node.js).
If you’d want me to include more code snippets, please leave a comment.
.NET Core app time distribution has been updated.
Server response
HTTP/1.1 200 OK
Cache-Control: no-store,no-cache
Date: Fri, 12 May 2017 07:46:56 GMT
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
Server: Kestrel
Asked by undefined
Solution #1
As many others have pointed out, the comparison is incomplete. The async methodology of node.js was groundbreaking at the time of its release. Other languages and web frameworks have since adopted the approaches that they pioneered.
To figure out what the difference meant, create a blocked request that simulates an IO demand, such as a database request. This will exhaust the threadpool in a thread-per-request system, and new requests will be queued until one becomes available. This does not happen with non-blocking-io frameworks.
Consider this node.js server that responds after a one-second delay.
const server = http.createServer((req, res) => {
setTimeout(() => {
res.statusCode = 200;
res.end();
}, 1000);
});
Now, for 10s, let’s throw 100 concurrent connections at it. As a result, we anticipate roughly 1000 requests to be completed.
$ wrk -t100 -c100 -d10s http://localhost:8000
Running 10s test @ http://localhost:8000
100 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.01s 10.14ms 1.16s 99.57%
Req/Sec 0.13 0.34 1.00 86.77%
922 requests in 10.09s, 89.14KB read
Requests/sec: 91.34
Transfer/sec: 8.83KB
As you can see, we’re close to finishing with 922 done.
Consider the following ASP.NET code, which was written as though async/await were not yet enabled, putting us in the node.js launch era.
app.Run((context) =>
{
Thread.Sleep(1000);
context.Response.StatusCode = 200;
return Task.CompletedTask;
});
$ wrk -t100 -c100 -d10s http://localhost:5000
Running 10s test @ http://localhost:5000
100 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.08s 74.62ms 1.15s 100.00%
Req/Sec 0.00 0.00 0.00 100.00%
62 requests in 10.07s, 5.57KB read
Socket errors: connect 0, read 0, write 0, timeout 54
Requests/sec: 6.16
Transfer/sec: 566.51B
62! The threadpool’s limit can be seen here. We could achieve more concurrent requests by tweaking it higher, but at the expense of more server resources.
The decision to avoid halting processing threads for certain IO-bound workloads was that significant.
Let’s fast forward to today, when that influence has spread throughout the industry, allowing dotnet to benefit from its advancements.
app.Run(async (context) =>
{
await Task.Delay(1000);
context.Response.StatusCode = 200;
});
$ wrk -t100 -c100 -d10s http://localhost:5000
Running 10s test @ http://localhost:5000
100 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.01s 19.84ms 1.16s 98.26%
Req/Sec 0.12 0.32 1.00 88.06%
921 requests in 10.09s, 82.75KB read
Requests/sec: 91.28
Transfer/sec: 8.20KB
There are no surprises here; we’ve now matched node. js.
So, what exactly does it all imply?
Your impressions that node.js is the “fastest” are from a different age than we are now. Add to that the fact that node/js/v8 were never “quick,” but instead shattered the thread-per-request approach. The rest of the world has been catching up.
If you want to handle single requests as quickly as possible, instead of creating your own, look at serious benchmarks. If you simply want something that scalable to modern standards, go ahead and use any language you want as long as you don’t block those threads.
Disclaimer: All code and tests were developed on an old MacBook Air on a lazy Sunday morning. Grab the code from https://github.com/csainty/nodejs-vs-aspnetcore/tree/master/nodejs-vs-aspnetcore/tree/master/nodejs-vs-aspnetcore/tree/master/nodejs-vs-aspnetcore/tree/master/n
Answered by Chris Sainty
Solution #2
The overhead of Node Frameworks like Express and Koa is horrendous. The “Raw” Node is much faster.
Although I haven’t tried it, there is a newer framework that comes close to “Raw” Node performance: https://github.com/aerojs/aero
(on that page, see the benchmark)
update: Here are some figures to consider: https://github.com/blitzprog/webserver-benchmarks
Node:
31336.78
31940.29
Aero:
29922.20
27738.14
Restify:
19403.99
19744.61
Express:
19020.79
18937.67
Koa:
16182.02
16631.97
Koala:
5806.04
6111.47
Hapi:
497.56
500.00
The overheads in the most popular node, as you can see. Frameworks for JS are extremely important!
Answered by smerg
Post is based on https://stackoverflow.com/questions/43920942/unexpected-outcome-of-node-js-vs-asp-net-core-performance-test