I wanted to see what all the Node.js hype was about so I decided to run some head to head load tests using Ghost (Node.js) and WordPress (php). The results were incredible with Ghost soundly trouncing WordPress. It was like watching a starship racing an airplane (well, what I imagine that would be like anyway).
There is a new blogging platform that was recently made available to the general public called Ghost. What’s interesting about Ghost is that it is built on the Node.js platform. If you’re not familiar with Node.js you should read my blog post about it here. If your not familiar with Ghost you can read about this kickstarter project here.
To provide a little background, Ghost is just a blogging platform and nothing more while WordPress is a full up CMS. I wanted to make this comparison as fair as possible so I limited my load testing scripts to executing against only the blog pages. I also wanted to test the “out of the box” experience so I did not make configuration changes to either platform (besides hooking them both up to MySQL). I spun up a single 64-bit RHEL m1.large (reference server sizing image below for specs) instance on Amazon EC2 to host both blogging platforms.
I wanted to test the most common configurations so I used NginX to front end Ghost and used Apache to front end WordPress. Both platforms shared the same local MySQL backend database instance (Ghost comes with SQLite by default but I wanted to make sure I provided a level playing field on the back end).
I had both Ghost (listening on port 80) and WordPress (listening on port 8080) running at the same time but only applied load to one blogging platform at any given time.
That brings me to the load generation portion of this little experiment. I spun up another EC2 instance (64-bit ubuntu, size m1.medium – reference server sizing image above for specs) in the same availability zone in an attempt to minimize network impact on test results. I asked my colleague @dustinwhittle to recommend a load test configuration and he referred me to his blog post about load test tools and recommended I used a combination of Siege and Sproxy.
After I had the blogging platforms installed and tested as working I added an 8 part blog series in plain text (no images) to each site and removed any pre-existing blogs. In WordPress I left the standard URL pattern in place and did NOT implement permalinks so that I would not slow things down by using that feature. I also did not turn on any caching technology for WordPress as I was trying to measure the out of the box experience. Basically I didn’t attempt any sort of tuning at all on either platform.
The other major configuration to note was that I used the AppDynamics machine agent to collect and chart OS metrics during these load tests.
In order to use Siege to test many concurrent connections against many URLs I had to create a list of the URLs in a text file. For this I used Sproxy. Reference slides 20-23 of the following presentation for the details on using Sproxy https://speakerdeck.com/dustinwhittle/agile-performance-testing-checklist
I ran Sproxy against both Ghost and WordPress and ended up with my list of URLs. I modified each of these files to include the exact same list of blog posts so that the load tests would be as similar as possible. You can see the contents of each file below.
So now I was ready to fire up Siege and start hitting each blog with load. Siege is a nice tool that allows you to manipulate some key parameters. The ones I played with the most were the number of concurrent connections (-c) and the delay (-d in seconds) between batches of requests. Here is the command for your reference… siege -v -c 100 -i -t 10M -f urls.txt -d 1
In a word, staggering! I ran siege for 10 minutes with 100 concurrent connections and a 1 second delay between batches of web requests. The results are shown below…
As you can see from the output shown above, Ghost with Nginx outperformed WordPress with Apache by about 678% when looking at total transactional throughput over a 10 minute test. Impressively, the longest transaction response time for Ghost was 2.62 seconds compared the an abysmal 33.41 seconds for WordPress. I repeated these test runs multiple times and got very similar results so I am not going to show the rest of the test results since they are redundant. My goal here was not to run an exhaustive analysis of performance at varying loads but instead to create a substantial load and to see how each platform handled it.
Some other interesting data points to note. During the load test, Ghost ran with only 1 process and Nginx had a total of 2 processes. WordPress and Apache on the other hand spawned a total of ~110 httpd processes which makes sense since Siege was throwing 100 concurrent connections at it. The interesting part is in the CPU data during the load tests. I have plotted Average, Min, and Max CPU utilization on the charts below. You can clearly see that Ghost CPU consumption was about 40% while WordPress consumption was about 70%.
Now don’t think that I have forgot about normal loading patterns. How do things look with a moderate load as compared to the super high load that I placed on these platforms with the 100 concurrent connections test? To find out I dropped the number of concurrent connections to 10 and set the delay between batches of connections to 5 seconds. The results are shown below and are still incredibly impressive for Ghost. WordPress was outperformed in every way possible. Ghost had higher throughput and most importantly the slowest transaction response time was .18 seconds compared to 2.72 seconds for WordPress. From a CPU perspective Ghost only consumed ~4% on average during this test while WordPress consumed ~30% on average.
Update on 10/18/2013 – It’s not fair!!! Apples and Oranges!!!
There have been some who say I’m comparing apples to oranges. To this I say, you’re damn right! In this post I set out to compare the common combinations of Nginx + Ghost and Apache + WordPress. I set out to compare these in their most basic forms, no tuning, no caching, just what you get out of the box. But I understand the outcry and I decided to level the playing field. Some people thought that Apache was a bottleneck so I decided to use Apache as the front end web server for Ghost and to re-run my load tests. I ran multiple tests again but they were all very consistent so I am only going to show the output from one of them (shown below).
The results shown above are interesting. Apache + Ghost was actually slightly FASTER than running Ghost with Nginx. Ghost is still super fast regardless of using Apache or Nginx as the web server.
Ghost is way faster and can handle way more load than WordPress while also consuming much less CPU resource (Ghost also has considerably less functionality than WordPress but that’s not relevant for the purpose of this test). It would be interesting to run Ghost in a 2 process Node.js cluster and see what difference it makes in throughput and CPU utilization. Hmmmm, that sounds like a really good subject for another blog post…
Another interesting topic that I didn’t cover here is monitoring for both of these platforms. In my mind it’s not enough to just observe the behavior of these platforms from the outside. I want to see what’s going on from the inside. In a future blog post I am going to monitor Ghost with Nodetime and will monitor WordPress with AppDynamics. I can’t wait to see how they both look from the inside!
Update on 11/14/2013: Due to popular request I have performed more testing, this time with an opcode cache for PHP. You can read all about it in An Example of How Node.js is Faster Than PHP – Part 2
Here are the links to the information I used to build out my blogging and testing platforms (I used the relevant portions of each article since my configuration was different than what was in each article alone):
How to install Node.js: https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager
How to install Ghost: http://docs.ghost.org/installation/
How to install MySQL: http://www.cyberciti.biz/faq/how-to-install-mysql-under-rhel/
How to install and configure NginX and use MySQL: http://0v.org/installing-ghost-on-ubuntu-nginx-and-mysql/#.Ul26n2RATL4
Where to find Siege: http://www.joedog.org/siege-home/
Where to find Sproxy: http://www.joedog.org/sproxy-home/
More good information on using Siege and Sproxy: http://www.euperia.com/linux/tools-and-utilities/speed-testing-your-website-with-siege-part-two/771