Skip to content

500X Faster Caching than Redis, Memcache & APC

DON ★ PABLO edited this page Jun 20, 2022 · 1 revision

500X Faster Caching than Redis, Memcache & APC

In all sincerity, this is how it should be interpreted. In this article, I'll show how "PHP file caching" was implemented in the application logic at Graphiq, which resulted in reads from the cache being completed at an incredible speed. This speed was gained by lowering the amount of time it needed to fetch data from the cache.

Utilizing the PHP engine's in-memory file caching (opcache) to cache application data in addition to the program's source code is the key component of the method. HHVM has supported this way for quite some time, although PHP has just recently begun to do so, beginning with the release of PHP 8 this year. HHVM has been doing so since quite some time. The technique is still regarded "functional" with PHP versions that are older than 8, it is simply run more slowly.

This technique is speedier than Redis, Memcache, APC, and any other PHP caching solutions since all of those other solutions must serialise and unserialize objects, often by making use of PHP's serialise or json encode methods. This approach, on the other hand, serialises and deserializes the data just once, making it substantially quicker than the alternatives. In contrast, none of these tasks are required while using this strategy. If we save PHP objects in the file cache memory between several queries, we may be able to fully eliminate the necessity for serialisation.

Here’s how we “set” a value in the cache:

function cache_set($key, $val) {
   $val = var_export($val, true);
   // HHVM fails at __set_state, so just use object cast for now
   $val = str_replace('stdClass::__set_state', '(object)', $val);
   // Write to temp file first to ensure atomicity
   $tmp = "/tmp/$key." . uniqid('', true) . '.tmp';
   file_put_contents($tmp, '<?php $val = ' . $val . ';', LOCK_EX);
   rename($tmp, "/tmp/$key");
}

And here’s how we “get” a value from the cache:

function cache_get($key) {
    [@include](http://twitter.com/include) "/tmp/$key";
    return isset($val) ? $val : false;
}

Now let’s store a value in both our PHP file cache and in APC to compare:

$data = array_fill(0, 1000000, ‘hi’); // your application data here
cache_set('my_key', $data);
apc_store('my_key', $data);

And see how they perform:

// note: make sure you run this on a separate request from cache_set to ensure PHP's opcache will actually cache the file

$t = microtime(true);
$data = cache_get(‘my_key’);
echo microtime(true) - $t;
// 0.00013017654418945
$t = microtime(true);
$data = apc_fetch(‘my_key’);
echo microtime(true) - $t;
// 0.061056137084961

We were able to get a string array containing one million rows in less than one tenth of a millisecond, which is about four hundred and fifty times faster than APC. Given that larger objects will have more performance gains that approach infinity as they become larger, the figure 500X is rather arbitrary. However, after using this strategy, the caching performance of our actual production objects increased by a factor equivalent to two orders of magnitude.

Since there is no speed benefit associated with string storage, it is vital to remember that PHP file caching should largely be used to store arrays and objects rather than strings. This guideline should always be kept in mind. When dealing with shorter strings, APC actually provides somewhat better speed than PHP itself. This is because APC use the include() function, which involves some more effort.

Production Implementation

The production version of our programme utilises a little larger amount of code than the preceding example demonstrates. In addition to adding support for expiry and a cache clear function, we had to develop our own multi-server distributed clearing mechanism. This was a huge obstacle for us. Data stores like Redis and Memcache handle such tasks automatically for their consumers. In addition, cache keys must be checked or encoded in line with the filesystem's specifications to guarantee that the filename's characters are correct.

The setup of PHP's opcache is a further part of production that needs your attention (or HHVM .ini configuration). The value you choose for the opcache.memory consumption parameter must be more than the total of the sizes of any code files and data you want to keep in the cache. Both the total number of code files and the total number of keys that you desire to cache must be more than the opcache.max accelerated files value you provide. If this is not the case, you will be unable to cache as many keys. Even if these settings are not modified appropriately, the cache will continue to function correctly; nevertheless, its performance may decrease as a consequence.

Clone this wiki locally