Benchmarks

Hammer on a product catalog page

I tested on an otherwise idle WooCommerce / Bookshop staging site with 1,350 products. A benchmark of product catalog page 75 out of 76 showed this number of pageviews per second, higher is better.

  • 13.2 with no persistent object cache.
  • 18.0 with this SQLite Object Cache (36% more than no cache).
  • 15.0 with the Redis Object Cache running on localhost (25% more than no cache).
  • 11.3 with the Redis Object Cache running on a dedicated machine (14% less than no cache).

These benchmarks were run on Ubuntu 22.10, WordPress 6.0.1, php 8.0.27, and SQLite 3.37.2, php-fpm 8.1, redis 6.0.16, and Apache 2.4.54. I used ab, the Apache HTTP Server Benchmarking Tool.

Benchmarking work is ongoing. If you run benchmarks please post the results in the support forum.

It’s surprising that using redis on its own dedicated machine slows things down rather than speeding them up. It’s possible I’m not measuring things correctly. And this is surely a distorted test workload. Again, benchmarking work is ongoing.

Load test with many transients

I created an object cache load test. It works by setting a large number of transients, then getting them, then deleting them. Transients go into the persistent object cache rather than the site database if a cache is provisioned on the site, so this is a meaningful, if stylized, test of cache throughput.

The results, comparing this plugin’s performance with the Redis Cache by Till Krüss, are here. Lower numbers are faster. Times per operation are in microseconds.

set_transientget_transientdelete_transient
SQLite Object Cache, without APCu39660349
SQLite Object Cache, with APCu3708349
Redis Cache839856826
Docket Cache98672187
atec APCu Cache2264

It is remarkable how much faster this plugin fetches data from the cache than the Redis Cache does. That is probably because the php service processes must issue network requests and wait for responses while using Redis. When using SQLite, the php service process runs native sqlite.org code in the SQLite3 extension, which finds the cached item in a table. Because much of that table is likely already resident in the operating system’s RAM-cached file system, the retrieval operations are fast. This performance boost is consistent with the observations of SQLite’s developers.

Docket cache has comparable performance to SQLite Object Cache without APCu support, Lookup operations are significantly faster in SQLite Object Cache with APCu support.

atec APCu Cache is fastest. But it does not appear to be compatible with WP-CLI.

APCu Caching

Notice also that the APCu Cache and Object Cache 4 Everyone running in disk mode are extremely fast.

The APCu Cache is a php feature using similar technology to the opcode cache that speeds up the loading of php files. It is very fast, undoubtedly because it uses straightforward memory data structures and doesn’t require serialization. It is basically a persistent associative array.

It does have a limit on its number of slots — the maximum number of elements it can store. That limit is approximately 4096 elements, which is too small for an object cache on a site with more than a hundred or so posts or pages. It limits the amount of space as well, typically to 32M.

On Windows machines, the APCu cache is not shared between php worker processes, which futher limits its usefuless.

I wonder whether a three-level cache approach might work? The first level is RAM, the second would be APCu, and the third SQLite. This would need to be a write-through cache. Any writes would need to be placed both in APCu and SQLite.

Igbinary

This plugin also uses the Igbinary extension (when it is available) in place of php’s standard serialize() capability, because it generates a more compact storage format. The Redis cache uses serialize(). That accounts for some, but not all, the performance gains.

The test

The test is pretty cheesy as benchmarks go.

This WordPress shortcode handler performs the tests and emits how long they took.

function cachebenchmark( $atts, $content, $shortcode_tag ) {
	ob_start();
	$out   = array();
	$count = 10_000;
	$start = microtime( true );

	for ( $i = 0; $i < $count; $i ++ ) {
		$name = 'hackhack' . $i;
		set_transient( $name, [ $name, $i, str_pad( 'test', $i, 'x' ) ], 3600 );
	}
	$now   = microtime( true );
	$out[] = 'set_transient ' . number_format( ( $now - $start ) * 1_000_000 / $count, 0 ). 'μs';
	wp_cache_flush_runtime();
	$start = $now;
	for ( $i = 0; $i < $count; $i ++ ) {
		$name = 'hackhack' . $i;
		$data = get_transient( $name, 'missing' );
	}
	$now   = microtime( true );
	$out[] = 'get_transient ' . number_format( ( $now - $start ) * 1_000_000 / $count, 0 ). 'μs';
	wp_cache_flush_runtime();
	$start = $now;
	for ( $i = 0; $i < $count; $i ++ ) {
		$name = 'hackhack' . $i;
		delete_transient( $name, 'missing' );
	}
	$now   = microtime( true );
	$out[] = 'delete_transient ' . number_format( ( $now - $start ) * 1_000_000 / $count, 0 ). 'μs';
	$out[] = ob_get_clean();
	return implode( ' ', $out );
}

This ran on an Ubuntu 22.04 virtual machine wth 16GiB of memory and 6x 3.2GHz AMD Ryzen 7 cores, under a VirtualBox hypervisor on Windows 11. Versions: SQLite: 3.37.2 php: 8.3.16 Server: Apache/2.4.52 (Ubuntu) Plugin: 1.4.1 igbinary: available.

Use on a FreeBSD site

Something’s wrong on a site hosted on nearlyfreespeech.net. (They give each site its own FreeBSD jail.) Each SQLlite operation (lookup, save, etc) seems to take about 5 milliseconds. That’s obviously too long for the plugin to be useful.

Using gzdeflate on serialized objects

Using gzdeflate( igbinary_serialize ( $object )) seems to be extremely slow on large objects like the dashboard feeds. We probably shouldn’t do it.

nminp1p5medianmeanp95p99maxrangemadevstdev
Serialize times1295.535.565.7818.93241.10143.103,247.9923,603.0623,597.53410.972,084.61
Unserialize times1902.342.362.477.1813.6162.9198.62125.56123.2111.5718.78
Times in microseconds for a serialize operation

Other Benchmarks Requested

If you have benchmark results, or advice on better tests, please share them in a comment here or on the plugin’s support forum.

7 thoughts on “Benchmarks”

  1. I’ve developed a repo for object cache benchmarks at https://github.com/staticweb-io/wordpress-cache-benchmarks

    Running on a 16-core machine running NixOS 25.11 with 32 PHP-FPM workers.

    Time in seconds to serve 5000 requests on a default WordPress install (`just bench`):

    sqlite-object-cache 12.545063041919999
    atec-cache-apcu 12.76612289392
    snapcache-memcached 13.542692088799999
    wp-redis 13.562496480499998
    no-plugins 13.880502926260002
    redis-cache 14.43989147696
    sqlite-object-cache-apcu 18.661694814260002
    litespeed-memcached 24.14998094594
    litespeed-redis 24.18210881068
    eac-object-cache-apcu-sqlite-memopt 32.71374302427999
    eac-object-cache-apcu 32.73003208813999

    APCu performs poorly at this level of concurrency.

    I also made a benchmark that creates 1000 posts and measures time to serve 5000 random requests. However, this is more or less a cache miss test. I intend to eventually change this to a more realistic workload where some URLs are requested much more than others. But the current results are still interesting:

    sqlite-object-cache 16.09493740976
    no-plugins 17.346618568740002
    sqlite-object-cache-apcu 28.369004206739998
    litespeed-memcached 28.6442691662
    litespeed-redis 28.902872719740003
    atec-cache-apcu 29.001154921659996
    snapcache-memcached 29.03592353496
    wp-redis 29.111517081500004
    redis-cache 29.24047174072
    eac-object-cache-apcu 33.87384849525999
    eac-object-cache-apcu-sqlite-memopt 34.992606179359996

    Reply
    • Thanks for this. I wonder why APCu is slower under this workload? I’ll have to investigate.

      If you could tell me a bit more about how you set up your virtual users it would be great. Thanks again.

      Reply
  2. Hey Ollie, any new updates on this benchmark? Just wondering because my server has Redis enabled and I will either use it or yours, thank you.

    Reply
    • There are many host-dependent variables in benchmarking redis vs. SQLite3. So, I don’t think any benchmark I ran would be very informative for your case, unfortunately.

      Till Kruess’s redis cache plugin’s built-in performance measurement stuff isn’t as fine-grained as mine, so I don’t have good numbers for hoow well his works.

      My suggestion: try redis for a day or two, then try SQLite, and see whether you can tell whether one or the other gives a better user experience.

      Reply
      • Further benchmarking shows that the round-trip time request time to redis (or memcache/d) dominates performance. With SQLite (or the OPcache approach used by Docket Cache) cache lookups can be satisfied in-process, so they’re faster.

        An external cache server is required when using multiple load-balanced web servers, however.

Leave a Comment