PHP vs node.js: The REAL statistics

When it comes to web programming, I’ve always coded in ASP.NET or the LAMP technologies for most part of my life. Now, the new buzz in the city is node.js. It is a light-weight platform that runs javascript code on server-side and is said to improvise performance by using async I/O.

The theory suggests that synchronous or blocking model of I/O works something like this:

Blocking I/O

I/O is typically the costliest part of a web transaction. When a request arrives to the apache web server, it passes it to PHP interpreter for scripting any dynamic contents. Now comes the tricky part – If the PHP script wants to read something from the disk/database or write to it, that is the slowest link in the chain. When you call PHP function file_get_contents(), the entire thread is blocked until the contents are retrieved! The server can’t do anything until your script gets the file contents. Consider what happens when multiples of simultaneous requests are issued by different users to your server? They get queued, because no thread is available to do the job since they are all blocked in I/O!

Here comes the unique selling-point of node.js. Since node.js implements async I/O in almost all its functions, the server thread in the above scenario is freed as soon as the file retrieval function (fs.readFile) is called. Then, once the I/O completes, node calls a function (passed earlier by fs.readFile) along with the data parameters. In the meantime, that valuable thread can be used for serving some other request.

So thats the theory about it anyway. But I’m not someone who just accepts any new fad in the town just because it is hype and everyone uses it. Nope, I want to get under the covers and verify it for myself. I wanted to see whether this theory holds in actual practice or not.

So I brought upon myself the job of writing two simple scripts for benchmarking this – one in PHP (hosted on apache2) and other in javascript (hosted on node.js). The test itself was very simple. The script would:

1. Accept the request.
2. Generate a random string of 108 kilobytes.
3. Write the string to a file on the disk.
4. Read the contents back from disk.
5. Return the string back on the response stream.

This is the first script, index.php:

<?php
//index.php
$s=""; //generate a random string of 108KB and a random filename
$fname = chr(rand(0,57)+65).chr(rand(0,57)+65).chr(rand(0,57)+65).chr(rand(0,57)+65).'.txt';
for($i=0;$i&lt;108000;$i++)
{
	$n=rand(0,57)+65;
	$s = $s.chr($n);
}

//write s to a file
file_put_contents($fname,$s);
$result = file_get_contents($fname);
echo $result;

And here is the second script, server.js:

//server.js
var http = require('http');	
var server = http.createServer(handler);

function handler(request, response) {
	//console.log('request received!');
	response.writeHead(200, {'Content-Type': 'text/plain'});

	s=""; //generate a random string of 108KB and a random filename
	fname = String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) +
		String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) +
		String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) + 
		String.fromCharCode(Math.floor(65 + (Math.random()*(122-65)) )) + ".txt";

	for(i=0;i&lt;108000;i++)
	{
		n=Math.floor(65 + (Math.random()*(122-65)) );
		s+=String.fromCharCode(n);
	}

	//write s to a file
	var fs = require('fs');
	fs.writeFile(fname, s, function(err, fd) {
			if (err) throw err;
			//console.log("The file was saved!");
			//read back from the file
			fs.readFile(fname, function (err, data) {
				if (err) throw err;
				result = data;
				response.end(result);
			});	
		}
	);
}

server.listen(8124);
console.log('Server running at http://127.0.0.1:8124/');

And then, I ran the apache benchmarking tool on both of them with 2000 requests (200 concurrent). When I saw the time stats of the result, I was astounded:

#PHP:
Concurrency Level:      200
Time taken for tests:   574.796 seconds
Complete requests:      2000

#node.js:
Concurrency Level:      200
Time taken for tests:   41.887 seconds
Complete requests:      2000

The truth is out. node.js was faster than PHP by more 14 times! These results are astonishing. It simply means that node.js IS going to be THE de-facto standard for writing performance driven apps in the upcoming future, there is no doubt about it!

Agreed that the nodejs ecosystem isn’t that widely developed yet, and most node modules for things like db connectivity, network access, utilities, etc. are actively being developed. But still, after seeing these results, its a no-brainer. Any extra effort spent in developing node.js apps is more than worth it. PHP might be still having the “king of web” status, but with node.js in the town, I don’t see that status staying for very long!

Update

After reading some comments from the below section, I felt obliged to create a C#/mono version too. This, unfortunately, has turned out to be the slowest of the bunch (~40 secs for 1 request). Either the Task library in mono is terribly implemented, or there is something terribly wrong with my code. I’ll fix it once I get some time and be back with my next post (maybe ASP.NET vs node.js vs PHP!).

Second Update

As for C#/ASP.NET, this is the most optimum version that I could manage. It still lags behind both PHP and node.js and most of the issued requests just get dropped. (And yes, I’ve tested it on both Linux/Mono and Windows-Server-2012/IIS environments). Maybe ASP.NET is inherently slower, so I’ll have to change the terms of this benchmark to take it into comparison:

public class Handler : System.Web.IHttpHandler
{
    private StringBuilder payload = null;

    private async void processAsync()
    {
        var r = new Random ();

        //generate a random string of 108kb
        payload=new StringBuilder();
        for (var i = 0; i &lt; 54000; i++)
            payload.Append( (char)(r.Next(65,90)));

        //create a unique file
        var fname = "";
        do{fname = @"c:\source\csharp\asyncdemo\" + r.Next (1, 99999999).ToString () + ".txt";
        } while(File.Exists(fname));            

        //write the string to disk in async manner
        using(FileStream fs = File.Open(fname,FileMode.CreateNew,FileAccess.ReadWrite))
        {
            var bytes=(new System.Text.ASCIIEncoding ()).GetBytes (payload.ToString());
            await fs.WriteAsync (bytes,0,bytes.Length);
            fs.Close ();
        }

        //read the string back from disk in async manner
        payload = new StringBuilder ();
        StreamReader sr = new StreamReader (fname);
        payload.Append(await sr.ReadToEndAsync ());
        sr.Close ();
        //File.Delete (fname); //remove the file
    }

    public void ProcessRequest (HttpContext context)
    {
        Task task = new Task(processAsync);
        task.Start ();
        task.Wait ();

        //write the string back on the response stream
        context.Response.ContentType = "text/plain";
        context.Response.Write (payload.ToString());
    }

    public bool IsReusable 
    {
        get {
            return false;
        }
    }
}

References

  1. https://en.wikipedia.org/wiki/Node.js
  2. http://notes.ericjiang.com/posts/751
  3. http://nodejs.org
  4. https://code.google.com/p/node-js-vs-apache-php-benchmark/wiki/Tests

12 comments

  1. Sean says:

    Hi Prahlad,Probably time to do another update with ReactPHP enabling event looped, asynchronous I/O. According to other benchmarks I’ve seen should make it on par with NodeJS speedwise.

  2. mpolci says:

    Your results seems excessive so I repeated the tests using Ubuntu 14.10 with its packaged versions of apache, php5 and corejs. My result for node.js is similar to your, about 40 seconds, but for php I obtained 68 seconds.

  3. Prahlad Yeri says:

    Did you use the stock version of apache or you tweaked/twisted the apache2 configuration settings?

  4. Prahlad Yeri says:

    Well, I frankly think that ReactPHP is nothing but a face-saving effort by Facebook inc. of using a wrong product to begin with (PHP). Though PHP apps ARE scalable, maintaining the code-base in the long run is a huge night-mare.

  5. Sean says:

    I don’t see how a PHP codebase is more of a huge nightmare than say Python or Node. If you are trying to maintain an old PHP4-based monolith, yes that would be a nightmare. But using a modern framework and/or utilizing micro-service architecture, I would think most popular languages (Ruby/Python/PHP) would be on par with each other on maintainability.

  6. Prahlad Yeri says:

    It’s not the size of codebase that bothers me with regard to PHP, but its language characteristics. To begin with, PHP is not a thoughtfully designed language. It tries to implement the C style terse language syntax, but is poor in design. Lack of consistency in function names (bin2hex vs strtolower), argument order (($haystack, $needle) versus ($needle, $haystack)) are just some examples of design nightmares that a PHP programmer has to live it.Now, you might say that a programmer worth his salt should be able to code in any given language, and quite possibly, you might have easily trained your memory muscles to adjust for PHP’s incorrect design. But that doesn’t change the fact that PHP has so many of these glaring faults that it is cumbersome for a new programmer to easily grasp it. At the same time, PHP also makes it too easy for a script kiddie to write insecure code that lead to devastating results.Reference: http://eev.ee/blog/2012/04/

  7. Sean says:

    Yes, inconsistencies are definitely pet peeves and hopefully PHP7 will clean all those up. Otherwise from a PM/CTO/Business owner perspective PHP is great. Plenty of highly qualified PHP coders out there with many years of experience whom you can hire at a little discount compared to other languages. As well as plenty of other businesses with PHP projects looking for solution providers.But if you are relying on script kiddies you will have nightmare no matter what language you go with.

  8. Bostjan Skufca says:

    I used your code to verify the benchmark (do fix the < chars please:), and here are my results:- node 0.12.5, best result: 12.9s (varied up to 15s), 15 %cpu- Apache 2.4 + PHP 5.6.13, best result: 16.0s (varied up to 16.4s), 100 %cpuThings to note:- Apache was fully configured, with max limit of 250 clients;- Apache was production-ready, with request logging and .htaccess parsing enabled, directory permissions etc;- Node was just the code you wrote + “node test.js” command;- Apache (or PHP, but I suspect Apache) has some locking issues I believe: occasionally benchmark stuck at around 99% for no reason, skewing results up to 40s or so, while server was not showing any CPU or IO activity;- OS Slackware 14.1, Apache and PHP custom built (no performance-related patches, opcache enabled)- CPU was i7-3770, 32GB ram (80% free)- using keepalive HTTP functionality made no difference (requests are too slow compared to TCP session initialization- node does not support multi-core operation (or are my information stale)?Short conclusion:For this test, PHP used all available CPU resources while node.js only used 1+1 core (one core doing the real work and another one waiting on IO). This is an important benefit of using Node.js.The result changes drastically when you take random content generator out of the picture – replace it with constant 108.000 bytes of text in variable $s (or ‘s’ in Node), and bump number of requests from 2k to 20k to get useable measurements:- PHP: 98percentile falls at 16ms per request, – Node.js: 98percentile falls at 60ms per request, 20% cpu- PHP: 3.2s to perform all 20k requests- Node.js: 5.7s to perform all 20k requestsThis is somewhat expected: implementations of random number generators differ a lot.In this second test the glitches of PHP/Apache were more frequent: while majority of requests ended in under 16ms, some requests took 20 seconds to complete, again without any CPU or IO activity.b.

  9. Indio John says:

    The best thing about Node is it is platform independent.We can run it on any platform and it’s more faster than PHP.I think this is the reason my clients are also switching from PHP to Node.

  10. Peter Cab says:

    It’s strange.Your results are too different than others.Why is this so?

  11. Ved Raj says:

    Great Post Indeed Prahalad yeri!But i would like to add further to this, Since the invention of the internet in 90s, PHP has always been there to take it to the next level. But with the advancements in technologies, PHP apps has became a primary target to security threats. To overcome these security issues and other low points of PHP, many alternatives have been launched in past few years, but nothing comes as close as Node.js. Today Node.js is quickly gaining popularity and is known for it’s smart performance.Here are some of the key points that may differentiate the two and help you choose a better programming language for your web app –Reasons To Choose PHP- See more at: http://www.valuecoders.com/

  12. Bostjan Skufca says:

    I posted a lot of info, which part is your question referring to?If about RNG, then please keep in mind that generating pseudo-random sequences in PCs is only _that_ fast and usually can not compare with reading from network or disk.

Leave a Reply

Your email address will not be published. Required fields are marked *