Benchmarks

PerfLocale 1.0.7 against 1.0.6 on the same site: how many database queries each page runs, and how much its response time changed, with and without a persistent object cache.

In short

  • Queries without an object cache: most pages run 5 to 14 queries fewer with 1.0.7 than with 1.0.6. Only the cart page, the checkout page and the Store API cart update run more when the cart holds a product variation: 3 or 4 more, for the shared-stock check that keeps the language copies of a product from being oversold.
  • Queries with Redis: every page runs the same number as with 1.0.6, or one more. One more on those cart requests with a variation; on a listing page whose cached page count was just cleared, where 1.0.7 counts the listing’s posts in each language so that hreflang tags point only to pages that exist; and on a home page whose Navigation block has no menu chosen, when the menu it falls back to has a translation.
  • Time: each of two passes compared the two versions on 140 combinations of page and cache state, each against a budget set before the run. One combination was over its budget in both passes: the checkout page with a product variation in the cart, with Redis, in the worst-case setup, at +3.6 and +3.4 ms against a budget of +3 ms (details). The cart page of the same setup was over in pass 1 (+3.1 ms) and within in pass 2 (+2.8 ms). Every other combination was within its budget in both passes.
  • Memory: no page’s peak memory grew by more than 2 MB.

PerfLocale’s own queries

Each bar is one page with PerfLocale 1.0.7. The teal part is the queries PerfLocale ran; the grey part is everything else on the page - WordPress, WooCommerce, the theme and the other plugins. The home pages were requested with an empty cart, the other pages with a cart holding a stock-managed product and a product variation.

With a persistent object cache (Redis, warm)
  • Home page4 of 13
  • Home page /de/4 of 13
  • Category archive /de/5 of 20
  • Product page /de/7 of 28
  • Cart page /de/6 of 17
  • Checkout page /de/3 of 14
Without an object cache
  • Home page15 of 200
  • Home page /de/16 of 200
  • Category archive /de/23 of 243
  • Product page /de/25 of 255
  • Cart page /de/28 of 228
  • Checkout page /de/25 of 224

A query counts as PerfLocale’s when PerfLocale’s code is on the call stack that issued it, so WordPress and WooCommerce functions that PerfLocale calls count too. With Redis, PerfLocale’s queries on these pages look up the languages and translated slugs of the items the page shows and links to, and resolve the translated slug in the address of a category, product, cart or checkout page; with a product variation in the cart, one more finds the variation’s copies in the other languages for the shared-stock check. Without an object cache, most of them are reads from the options table, where PerfLocale keeps its cached data when there is no object cache. That is why a persistent object cache makes the biggest difference here.

With Redis: 1.0.6 and 1.0.7 side by side
PagePerfLocale 1.0.6PerfLocale 1.0.7Whole page 1.0.6Whole page 1.0.7
Home page441313
Home page /de/441313
Category archive /de/552020
Product page /de/772828
Cart page /de/561617
Checkout page /de/231314
Without an object cache: 1.0.6 and 1.0.7 side by side
PagePerfLocale 1.0.6PerfLocale 1.0.7Whole page 1.0.6Whole page 1.0.7
Home page1715207200
Home page /de/1816207200
Category archive /de/2523250243
Product page /de/2725262255
Cart page /de/2028225228
Checkout page /de/1625220224

On the cart and checkout pages without an object cache, PerfLocale’s own count rises by 8 or 9, mostly WordPress loading the variation’s language copies for the shared-stock check, while the whole page rises by 3 or 4: the rest of the page runs 5 queries fewer with 1.0.7.

Response time: 1.0.7 against 1.0.6

Each figure below is how much longer (+) or shorter (−) a request took with 1.0.7 than with 1.0.6: the median shift of the full response time, measured from outside the server. A row covers several pages, in English and German, and shows the smallest and largest shift among them. Every shift has a 95% interval; the median half-width of those intervals is 2.6 ms, so a shift within about 2.5 ms of zero cannot be told apart from no change. The query column is the exact change in the page’s query count.

1.0.7 minus 1.0.6, in milliseconds
PagesObject cacheQueriesPass 1Pass 2
Home, category and product pagesNone−7−5.9 to +0.1−1.8 to +2.5
Home, category and product pagesRedis0+6.2 to +10.6 *−2.1 to +2.8
Cart page, checkout page, Store API cart and cart updateNone−7 to +4−1.9 to +4.5−2.3 to +3.8
Cart page, checkout page, Store API cart and cart updateRedis0 or +1+3.4 to +10.8 *−0.8 to +2.8
Worst case: home, category and product pagesNone−7 or −14−2.5 to +0.1−6.9 to −3.2
Worst case: home, category and product pagesRedis0−1.7 to +1.5−1.6 to +2.9
Worst case: cart page, checkout page, Store API cart and cart updateNone−7 to +4−2.6 to +2.9−4.1 to +1.4
Worst case: cart page, checkout page, Store API cart and cart updateRedis0 or +1−1.8 to +3.6−2.3 to +3.4
Listings: blog pages 2 and 200, date archive page 2, shop page 2None−7−3.1 to +0.2−4.1 to +1.6
The same listings, page-count or hreflang cache just clearedNone−6−3.9 to +1.7 *−3.9 to +1.9 *
The same listings, page-count or hreflang cache just clearedRedis0 or +1−0.8 to +3.1−2.6 to +3.0 *
Search resultsNone−7−0.6 to +0.2 *−8.8 to −7.9 *
Search resultsRedis0+0.1 to +0.8−0.4 to +0.8
Home page with a Navigation block that has no menu chosenNone−13 or −12−5.5 to −3.8−3.5 to −0.6
Home page with a Navigation block that has no menu chosenRedis0 or +1−0.8 to 0.0−1.5 to +2.7 *

* Some or all of these measurements drifted from block to block in that pass (block medians more than 15 ms apart), so their intervals are wider; their query counts are exact either way. In pass 1, on the regular block-theme pages with Redis, every interval includes zero and reaches up to +35 ms; the same pages measured −2.1 to +2.8 ms in pass 2. The two search figures of pass 2 have intervals from about −32 to +12 ms.

The budgets were set before the run. Home, category and product pages with something in the cart, and the search and navigation rows: +3 ms without an object cache, +2 ms with Redis, and at most 1 query more than 1.0.6. Cart page, checkout page and Store API requests with a variation in the cart: +10 ms and 40 queries without an object cache, +3 ms with Redis. Listings with a cleared cache and no object cache: +5 ms on blog page 2, +15 ms on blog page 200 and shop page 2. Every request: at most 2 MB more peak memory, the only budget of the remaining rows. A row is over its budget when the response time, or the PHP time inside the server, moved by more than the budget with its 95% interval entirely above zero, or when its query count or memory went over the limit.

The row over budget in both passes

The checkout page in the worst-case setup, in English, with Redis, took 3.6 ms longer with 1.0.7 in pass 1 (95% interval +1.2 to +5.7 ms) and 3.4 ms longer in pass 2 (+2.0 to +5.1 ms), against a budget of +3 ms. The extra time is the shared-stock check: when WooCommerce counts the stock that pending orders hold for the variation in the cart, PerfLocale makes it count the variation’s copies in every language, so two shoppers cannot buy the last units through different language copies. Finding those copies costs, on that page with Redis, one more database query and 26 more Redis requests. In the regular setup, where the variation is the first of its product’s variations, the same pages stayed within the budget.

Worst case with Redis: every cart row, 1.0.7 minus 1.0.6 in milliseconds (95% interval)
RequestQueriesPass 1Pass 2
Checkout page13 → 14+3.6 (+1.2 to +5.7), over+3.4 (+2.0 to +5.1), over
Checkout page /de/13 → 14+2.7 (−0.7 to +5.5)+2.2 (−0.2 to +4.6)
Cart page16 → 17+3.1 (+0.1 to +6.2), over+2.8 (+0.3 to +5.6)
Cart page /de/16 → 17+1.4 (−1.3 to +3.9)+2.6 (+0.8 to +5.0)
Store API cart update16 → 17+2.3 (+0.6 to +3.9)+2.9 (+0.8 to +5.0)
Store API cart update /de/16 → 17+2.3 (+0.1 to +4.3)+0.1 (−2.0 to +2.7)
Store API cart11 → 11+1.3 (−0.7 to +3.1)−0.6 (−2.5 to +1.9)
Store API cart /de/11 → 11−1.8 (−3.1 to +0.1)−2.3 (−3.4 to −0.9)

How this was measured

  • Site: a disposable copy of our largest test site, rebuilt from a fresh database dump on 2 October 2026 and run in isolation (its own MySQL and Redis, no outgoing HTTP, WP-Cron off). Four languages (English, Spanish, German, French) with language prefixes in the URL, more than 10,000 blog posts, and a WooCommerce shop with 2,000 extra products that have German copies and a variable product with 30 variations in all four languages, each variation managing its own stock. In the worst-case setup the variation in the cart is the 30th of the 30 in every language.
  • Themes: Twenty Twenty-Five, with WooCommerce’s mini-cart in the header, for the home, category, product, cart, checkout, Store API and navigation rows; Storefront for the listings and search results.
  • Software: WordPress 7.1.1, WooCommerce 10.9.4, PHP 8.2 with OPcache, MySQL 8.0.35, Redis 7.0 with the Redis Object Cache plugin. Also active: Advanced Custom Fields, Meta Box, Pods, Elementor, Beaver Builder, Contact Form 7, WPForms Lite, Action Scheduler and two small test add-ons of our own.
  • Versions: PerfLocale 1.0.6 as released and the 1.0.7 release candidate, each installed in turn on the same database, which was restored before every block of requests. The site starts on 1.0.6, so 1.0.7 runs its upgrade in a warm-up request that is not measured. A third PerfLocale build ran in the same blocks for reference; it is not reported here.
  • Requests: the versions took turns in 18 blocks per configuration (6 for the listings with warm caches), a third of them per version, in the order C, B, A, A, B, C, three times over. In each block the web server was restarted, each page got 3 warm-up requests and 6 measured ones, and one more request recorded every query with the code that issued it. The time is the full response time measured by the client; the shift is the Hodges–Lehmann estimate with a 95% block-bootstrap interval. The query count is the most common count of a page’s requests; in 271 of the 280 rows it was the same in every request.
  • Cache states: no object cache, with PerfLocale’s and WooCommerce’s caches as the warm-up requests left them; the same with the listing page-count or hreflang cache cleared before every request; and Redis, warm or with those caches cleared.
  • Two passes: the whole run was done twice, one pass after the other, on the night of 2 to 3 October 2026, on a quiet machine (PHP and MySQL on separate CPU cores, load checked before every run).
  • Noise check (A/A): before each pass, 1.0.6 was compared with a second copy of itself on six pages. Every page had to come out within 1.5 ms and the pooled interval had to include zero. In pass 1 the first check failed narrowly (pooled −0.9 ms with an interval of −1.7 to −0.1 ms, one page at 1.6 ms); as the plan requires, it was run again after a 20-minute wait and passed (pooled −0.4 ms, largest page 1.2 ms) before anything else was measured. In pass 2 it passed at the first attempt (pooled −0.3 ms, largest page 1.0 ms).
  • Sensitivity check (+2 ms): a 1.0.7 build was compared with the same build plus a 2 ms pause in every request. The pooled shift had to come out between +1 and +3 ms with its interval above zero, and every page between 0 and +4 ms. It came out at +2.0 ms (interval +0.9 to +3.0) in pass 1 and +2.2 ms (+0.9 to +3.6) in pass 2, with every page between +1 and +3 ms in both.

Why shifts and not page times? A page’s time on this lab says little about your server. The difference between two versions measured in turns on the same machine does, within the intervals shown. Query counts come out the same on any hardware, so you can check those on your own site.

Your numbers will differ: every theme and plugin adds queries and time of its own. To count them on your site, use the snippet in Measuring your own numbers.