I did a small benchmark comparing APC vs. Memcache. First of all, I'd like to say that in many situations, that would be like comparing apples to oranges. APC is opcode cache, primarily, and memcache is generic memory caching tool. In addition to that, memcache can be scaled to many servers.
Still, for some simple memory caching, primarily to reduce database hits on repeated (summary) queries, I was wondering if APC (more precisely, it's user data caching functionality) would perform better than memcache, because memcache has certain network latency involved, since it is running as a network service and is interfaced through network protocols.
For simple data (strings, arrays, arrays of arrays) -- that are often found on most CMS systems as summary fields, for instance latest topics, latest news, latest whatever, I find the following
- Memcache outperforms APC (user data caching) when number of reads is close to number of writes (ie. below 20 reads for 1 write)
- APC outperforms Memcache when number of reads is greater than 20 for each write, where APC is significantly faster for 100 reads per write or more, situations.
Does anyone have any experience with this, or can anyone perhaps suggest or perform some more thorough benchmarking (for situations not requiring scalable memcaching) - like forced multithreading on more than 2 cores?
Environment:
- OS: Vista 32-bit
- Mem: 2GB
- CPU: Turion64 X2
- PHP: 5.2.4 (as apache module)
- Apache. 2.2
- APC 3.0.15-dev (from PHP 5.2.4 PECL bundle)
- Memcache php_memcache.dll from PHP 5.2.4 PECL bundle
- Memcached for Win32, ver. 1.2.1
Source code follows. You can call the script with (any of the) three params:
loops= number of loops to perform (start with 100)
rows= number of rows of 5-field array with random integer and string data, variable size (start with 20)
reads= number of reads from cache for one write (start with 1-100, or more)
header ('Content-type: text/plain');
$loops= isset($_GET['loops']) ? (int) $_GET['loops'] : 100;
$nrows= isset($_GET['rows']) ? (int) $_GET['rows'] : 20;
$nreads= isset($_GET['reads']) ? (int) $_GET['reads'] : 100;
// Prepare array of arrays (20 rows, 4 fields per row)
echo "Preparing array... ($nrows rows)"; flush();
$rows= array();
for ($i=0; $i<$nrows; $i++) {
// Try with array of arrays (typical for CMS content fields)
$rows[]= array ('var1' => mt_rand(1, 65536), 'var2' => mt_rand(1, 40000000), 'var3' => randomString(mt_rand(5, 32)), 'var4' => randomString(mt_rand(100, 512)));
// Or try with pure strings (uncomment both for mixed rows)
$rows[]= randomString(1, 512);
}
echo " Done!\n"; flush();
// APC loop, 1000 iterations, store 1, read 100
echo "Begin APC loop ({$loops})... "; flush();
$tstart= microtime(true);
for ($i=0; $i<$loops; $i++) {
// Write phase
apc_delete('therows');
apc_store('therows', $rows);
// Read phase
for ($j=0; $j<$nreads; $j++) {
$rows2= apc_fetch('therows');
}
}
$time= microtime(true) - $tstart;
echo " Done: $time sec\n"; flush();
// Memcache loop, 1000 iterations, store 1, read 100
echo "Begin Memcache loop ({$loops})... "; flush();
$memcache= new Memcache();
$memcache->connect('127.0.0.1', 11211);
$tstart= microtime(true);
echo " Connected... "; flush();
for ($i=0; $i<$loops; $i++) {
// Write phase
$memcache->set('therows', $rows);
// Read phase
for ($j=0; $j<$nreads; $j++) {
$rows2= $memcache->get('therows');
}
}
$time= microtime(true) - $tstart;
echo " Done: $time sec\n"; flush();
function randomString($len) {
$str= '';
for ($i=0; $i<$len; $i++) {
$seed= mt_rand (0, 2);
switch ($seed) {
case 0: $str.= mt_rand (0, 9); break;
case 1: $str.= chr(mt_rand(0, 25)+65); break;
case 2:
default: $str.= chr(mt_rand(0, 25)+97); break;
}
}
return $str;
}