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.
introduces, to a "read file, print file, write log" loop. No wonder is was "Fast. Very fast". But it was not an equivalent test.createServer()