[stupid "message" here]

I Reckon This Must be the Place, I Reckon

A Place that is small, efficient and fast or something.

Not so Fast Node

The real Node vs Apache benchmark.

I came across the Node vs. Apache benchmark the other day and was immediately skeptical. Node is not as fast as the benchmark claims as it was an apples to oranges comparison.

First, here is the Node code:

var sys = require('sys'),
http = require('http');
 
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<p>Hello World</p>');
res.end();
}).listen(8080);

And the comparison PHP code was:

<?php
echo "<p>Hello World<p>";
?>

The first thing I noticed was that the number of bytes transfered would be less for Node for Apache/PHP sends more header data.

Node:

HTTP/1.1 200 OK
Content-Type: text/html
Date: Mon, 16 Mar 2015 15:52:09 GMT
Connection: close
Transfer-Encoding: chunked

12 
<p>Hello World</p>
0

Apache/PHP:

HTTP/1.1 200 OK
Date: Mon, 16 Mar 2015 15:49:55 GMT
Server: Apache/2.2.29 (Win32) PHP/5.4.33
X-Powered-By: PHP/5.4.33
Content-Length: 18
Connection: close
Content-Type: text/html

<p>Hello World</p>

It's only 157 bytes vs. 206, but if it's multiplied by one million... So the first thing I did was to make the Node code the same header-wise just to see what would happen:

var sys = require('sys'),
http = require('http');
 
http.createServer(function(req, res) {
res.writeHead(200, {
'Server': 'Apache/2.2.29 (Win32) PHP/5.4.33',
'X-Powered-By': 'PHP/5.4.33',
'Content-Length': '18',
'Content-Type': 'text/html'
});
res.write('<p>Hello World</p>');
res.end();
}).listen(8080);

Indeed the bytes transfered are identical, and it slowed Node down, but not by much.

The Real Node vs Apache Problem

The real problem with the benchmark is that Apache reads the INDEX.PHP file, processes it through PHP and then writes an access log entry. (Apache also checks for .HTACCESS.) The Node code simply prints a line of text. It does not have anything like the PHP process.

So here is more approximate Node vs Apache benchmark, with both serving up a file INDEX.HTML containing the hello world text and writing a line to a log file:

var sys = require('sys'),
http = require('http'),
fs = require('fs');
 
http.createServer(function(req, res) {
res.writeHead(200, {
'Server': 'Apache/2.2.29 (Win32) PHP/5.4.33',
'X-Powered-By': 'PHP/5.4.33',
'Content-Length': '18',
'Content-Type': 'text/html'
});
var buf = fs.readFileSync('index.html');
res.write(buf);
res.end();

var fd = fs.openSync('node.log', 'a');
fs.writeSync(fd,"::1 - - [16/Mar/2015:11:27:50 -0400] \"GET / HTTP/1.0\" 200 19\n");
fs.closeSync(fd);

}).listen(8080);

And here are my results.

Node:

Server Hostname:        localhost
Server Port:            8080

Document Path:          /
Document Length:        18 bytes

Concurrency Level:      100
Time taken for tests:   18.720 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      2060000 bytes
HTML transferred:       180000 bytes
Requests per second:    534.20 [#/sec] (mean)
Time per request:       187.197 [ms] (mean)
Time per request:       1.872 [ms] (mean, across all concurrent requests)
Transfer rate:          107.47 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   2.2      0      78
Processing:   109  185  55.4    172     703
Waiting:      109  185  55.1    172     703
Total:        109  186  55.4    172     703

Percentage of the requests served within a certain time (ms)
  50%    172
  66%    188
  75%    188
  80%    188
  90%    203
  95%    219
  98%    313
  99%    688
 100%    703 (longest request)

Apache:

Server Hostname:        localhost
Server Port:            80

Document Path:          /
Document Length:        18 bytes

Concurrency Level:      100
Time taken for tests:   8.672 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      2890000 bytes
HTML transferred:       180000 bytes
Requests per second:    1153.10 [#/sec] (mean)
Time per request:       86.723 [ms] (mean)
Time per request:       0.867 [ms] (mean, across all concurrent requests)
Transfer rate:          325.43 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   2.2      0      16
Processing:    31   86  15.0     94     469
Waiting:       16   84  15.3     78     469
Total:         31   86  15.0     94     469

Percentage of the requests served within a certain time (ms)
  50%     94
  66%     94
  75%     94
  80%     94
  90%     94
  95%    109
  98%    109
  99%    125
 100%    469 (longest request)

I used ab -r -n 10000 -c 100 URL so the numbers are smaller. (One million requests takes a long time and I did not want to spend all day on this as I just have a low end Windows laptop.)

Oh yeah. Apache transfered more data as the header data are:

HTTP/1.1 200 OK
Date: Mon, 16 Mar 2015 21:39:17 GMT
Server: Apache/2.2.29 (Win32) PHP/5.4.33
Last-Modified: Mon, 16 Mar 2015 21:35:43 GMT
ETag: "ae000000005a92-12-5116ea1e44dbf"
Accept-Ranges: bytes
Content-Length: 18
Connection: close
Content-Type: text/html

<p>Hello World</p>

Conclusion

Node is slower than Apache when Node approximates what Apache actually does. The original benchmark compared a "print string" loop with whatever overhead http.createServer() introduces, to a "read file, print file, write log" loop. No wonder is was "Fast. Very fast". But it was not an equivalent test.