So as for the first part of this experiment, I created a simple function library called testCache.php. It's contents look like this:
<?php
// php functions to store and fetch to/from apc cache
function fetch($key){
return apc_fetch($key);
}
function store($key, $data, $ttl){
return apc_store($key, $data, $ttl);
}
?>
And then I wrote a simple script to iteratively load up the user cache with values, re-testing their values and retrieving or re-populating depending on the cache state:
<?php
// php test script to load/retrieve from cache in apc
include '/export/www/testCache.php';
for($i=0; $i<200; $i++){
$j=2*$i;
if(!fetch($i)){
store($i,$j,5);
}
}
?>
The second version of this experiment takes an object oriented (OO) approach. In this version, I have used a helper class with two public methods (store and fetch). This approach doesn't explicitly instantiate a testCache object, but statically calls its methods.
<?php
// CacheManager class to allow fetching/storage to/from apc cache
class CacheManager{
public function fetch($key){
return apc_fetch($key);
}
public function store($key, $data, $ttl){
return apc_store($key, $data, $ttl);
}
}
?>
And this time the test code is modified to use the somewhat oddly-named 'scope resolution operator' - allowing us to call those methods without instantiating the CacheManager class. Of course I ensured that the cache was flushed before any of these load tests were conducted.
<?php
// Test code to run under load against CacheManager store/fetch methods
include '/export/www/testCache.class.php';
for($i=0; $i<200; $i++){
$j=2*$i;
if(!CacheManager::fetch($i)){
CacheManager::store($i,$j,5);
}
}
?>
I also created a third version of this test. Again taking the OO approach, This test instantiates a testCache object from the testCache class and calls the methods within it.
<?php
// Test code to run under load against CacheManager store/fetch methods
class CacheManager{
private function fetch($key){
return apc_fetch($key);
}
private function store($key, $data, $ttl){
return apc_store($key, $data, $ttl);
}
}
?>
In order to load these different versions of the apc storage/retrieval code, I used the seige utility, calling the script with a statement like:
$ siege -c 10 "http://localhost/cachetest.php" -b -t300s > /tmp/seige.log
That would load the url, holding 10 concurrent connections for a period of 300 seconds. The results (in order as above) were as follows:
Results 1
Transactions: 17357 hits
Availability: 100.00 %
Elapsed time: 299.99 secs
Data transferred: 0.00 MB
Response time: 0.17 secs
Transaction rate: 57.86 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 9.99
Successful transactions: 17357
Failed transactions: 0
Longest transaction: 0.36
Shortest transaction: 0.01
Results 2
Transactions: 15476 hits
Availability: 100.00 %
Elapsed time: 299.55 secs
Data transferred: 0.00 MB
Response time: 0.19 secs
Transaction rate: 51.66 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 9.99
Successful transactions: 15476
Failed transactions: 0
Longest transaction: 0.37
Shortest transaction: 0.01
Results 3
Transactions: 75326 hits
Availability: 100.00 %
Elapsed time: 299.76 secs
Data transferred: 0.00 MB
Response time: 0.04 secs
Transaction rate: 251.29 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 9.96
Successful transactions: 75326
Failed transactions: 0
Longest transaction: 0.94
Shortest transaction: 0.00
Well - I'm very surprised to see the full-blown object-oriented approach to be performing so well. I would have thought the continuous object instantiation (which is known to be an expensive operation) would slow that third test down. I have since tried this with apc completely disabled and used a simple array-based cache storage approach instead. Strangely, the results showed even better performance still from the OOP approach. I'll have to figure out why.
christo