Archive for the ‘Benchmarking’ Category

Caching wordpress as static HTML using APC

Monday, August 17th, 2009

While trying to speed up my wordpress installation I noticed that there seemed to be a lot of plugins that generate a static home page – they all used complex methods to store the files, checking the last modified times and implementing complex checking methods to see if the content has changed.

It struck me as strange that no one had suggested using APC cache – I don’t know if this is because its not available on all commercial hosting packages (such as dreamhost) or if its just due to lack of knowledge about the apc_store and apc_fetch commands. APC doesn’t just cache PHP opcode, it can cache user defined PHP variables as well.

I’ve come up with a small script that is capable of caching my PHP pages generated by wordpress. It uses the APC cache to store and manage the page data and it guarantees that the page will never be more than a specified number of seconds out of date.

In order to use this plugin you only have to make 2 changes go index.php in the wordpress root. Firstly you place this code at the top of your index.php


<? 
   
if (empty($_POST)) {

        $cache_key md5($_SERVER['REQUEST_URI'] .serialize($_GET));
        
$cache_data apc_fetch($cache_key$cache_result);

        if ($cache_result == true) {
            echo(
$cache_data);
            die;
        }

        ob_start();

    } 
?>

And then place this code at the base of index.php


<?
   
if (empty($_POST)) {
    
$cache_data ob_get_clean();
    
apc_store($cache_key$cache_data300);
    echo(
$cache_data);
   } 
?>

The code works by creating a MD5 hash of the URL and the GET request – this is important as it means that pages that take variables as arguments, such as search, will create their own cached pages. We don’t use the cache if there are POST variables present as we can probably assume that the output of the page is very dynamic. If you don’t want to cache pages that use a GET request as well you could use something like this

if ((empty($_POST)) && (empty($_GET)) {

It would be unwise to cache separate pages for each set of POST data as POST data is generally much larger than data sent via GET.

We use the MD5 of the URI and GET parameters to act as a key for our storage, if we find the key in memory and it hasn’t expired, we can use the data retrieved from the key to display the page – this bypasses all the MySQL and other PHP that was needed to create the original page.

Wordpress Static vs Dynamic benchmarks

If we now look at some benchmarks of before and after we can see the difference. I’ve use ab (that comes with apache) to perform the tests

Without the caching

Requests per second: 8.52 [#/sec] (mean)
Time per request: 1173.343 [ms] (mean)
Time per request: 117.334 [ms] (mean, across all concurrent requests)
Transfer rate: 212.85 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 1
Processing: 579 1161 104.4 1158 1464
Waiting: 234 465 82.6 449 756
Total: 579 1161 104.4 1158 1464
Percentage of the requests served within a certain time (ms)
50% 1158
66% 1186
75% 1225
80% 1238
90% 1289
95% 1315
98% 1407
99% 1413
100% 1464 (longest request)

With caching (tests done on non primed cache)

Requests per second: 136.07 [#/sec] (mean)
Time per request: 73.490 [ms] (mean)
Time per request: 7.349 [ms] (mean, across all concurrent requests)
Transfer rate: 3391.63 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.6 0 4
Processing: 10 72 32.2 77 202
Waiting: 5 32 25.0 26 196
Total: 11 72 32.2 77 202
Percentage of the requests served within a certain time (ms)
50% 77
66% 89
75% 93
80% 98
90% 109
95% 120
98% 136
99% 142
100% 202 (longest request)

You can see that without the caching that the server can serve 10 requests per second and after the caching it can serve 136 requests per second. That’s a 13 times increase in speed. Not bad for a server with only 256mb of RAM and a single core CPU. I’m pretty sure the server could handle even more load but at this point the upstream connection became saturated.

There are however some pitfalls with caching. Currently when users are logged in we cache their page, we need to add a condition to prevent this caching. Its not a problem on my server however as I am the only personal allowed to blog. Other pitfalls are the freshness of data, we can set this to whatever we want (from 1 second to infinity) but its never going to be as “fresh” as a page that isn’t cached.

Caching isn’t for everyone but if you do want your site to run faster, can afford to have some slight inconsistencies in your data and don’t mind waiting a few minutes for a comment to appear then you can achieve some really good results.

Benchmarking SPLFixedArray access speed

Friday, June 5th, 2009

Following a talk at PHPLondon on the SPL library last month I decided to play about with SPL. I was curious as to the difference in speed between SplFixedArray and a normal array particularly when it came to reading from the array.

So I initialized an array of 100,000 elements – I then timed how long it took to access each element in the array, in order. I did this 100 times and took the amount of memory used (as recorded by memory_get_peak_usage) and the total time taken. The benchmarks were done on PHP 5.3 RC2 under Linux (Kernel 2.6)  on a Intel Core 2 Duo 1.83Ghz.

The following were my compile options

CXXFLAGS=”-O3 -fomit-frame-pointer” ‘./configure’ ‘–enable-zip’ ‘–with-mysql=mysqlnd’ ‘–enable-sockets’ ‘–with-mm’ ‘–with-gd’ ‘–with-gmp’ ‘–with-jpeg-dir’ ‘–with-png-dir’ ‘–with-mcrypt’ ‘–with-curl’ ‘–with-openssl’ ‘–with-freetype-dir’ ‘–enable-calendar’ ‘–enable-mbstring’ ‘–enable-exif’ ‘–enable-ftp’ ‘–prefix=/opt/php’ ‘–with-zlib’ ‘–with-bz2′

And now for the results which are quite surprising

CPU time in seconds - SPL vs Normal PHP array

CPU time in seconds - SPL vs Normal PHP array

Memory KB Time Taken (sec)
SPL Non-SPL SPL Non-SPL
Reading using [] 31570 74729 25.28 25.49
Reading using iterator 32000 75008 19.77 13.85
Writing 36096 75008 39.60 55.17
count() 32000 75008 52.37 47.45
sizeof() 32000 75008 51.25 47.21

So it appears that SPL array is faster than a standard array  – by about 0.9% for reads. The SplFixedArray also uses 1/2 the memory of a standard PHP array which is a handy memory saving. SPL fixed arrays are much quicker  to write to compared to standard arrays by about 39%

Both the sizeof and count functions are slower with SPL. However if you have set the size of the array (as you must with a SplFixedArray) then you shouldn’t need to use these. Surprisingly using the iterator is also slower but overall SPL does seem a lot more memory efficient.

So it would seem that SplFixedArrays aren’t good for everything. It’s all swings and roundabouts and you lose flexability in order to gain speed on certain operators. So don’t go replacing all of your arrays with SPL but it’s worth remembering that they are there should you need them.