Skip to content

Loupe Search benchmark

Performance of the Loupe Search engine versus WordPress core search, measured with the harness in bin/benchmark.sh and bin/benchmark.php.

  • At real scale Loupe is faster on every query and the gap widens with the corpus: the front-end search page went from parity at ~600 docs to 1.58× faster at ~55,600 docs.
  • Core search cost is flat (~15 ms) regardless of the term — it is a LIKE '%term%' full-table scan. Loupe scales with result fanout instead, so its advantage grows as content grows.
  • Loupe also returns results core cannot: the typo query loerm ipzum returns 75 hits in Loupe and 0 in core.
Terminal window
# WP_CLI can point at any wp binary or wrapper (Local by Flywheel shown here).
WP_CLI="bash /path/to/wp-cli-local/scripts/wp" \
bin/benchmark.sh --url=http://plugins.local/loupe/ --count=5000 --iter=20 --compare-frontend

Useful flags: --skip-seed, --http, --rate=<limit> (throttle HTTP transfer), --json=<path>, --count, --iter, --warmup, --post-types.

  • Engine benchmark — times WP_Loupe_Search_Engine::search() / search_advanced() in-process via wp eval-file. The result cache is disabled for the run (loupe_search_max_cacheable_query_length → 0), so every iteration exercises the engine. Latency is wall-clock (hrtime) over N iterations after a warmup; min / median / p95 / max / avg are reported.
  • Engine vs core — under WP-CLI, Loupe’s posts_pre_query hook is not registered, so a raw WP_Query with s= runs core’s default MySQL LIKE-based search. Same box, same data, same process.
  • Front-end — times GET ?s=lorem with Loupe active, deactivates loupe-search network-wide so core search runs, times it again, then reactivates (guaranteed by a shell trap). This includes full page render, so the engine is only part of the number.
  • WordPress multisite on Local by Flywheel, subsite blog_id=4 (plugins.local/loupe/), loupe-search 1.2.6, SQLite-backed index.
  • Indexed post types: post, page, book.
  • Content: pre-existing FakerPress “latin” posts/pages plus wp post generate posts (empty content, titles like Post N).
  • 20 iterations, 5 warmup per scenario. Numbers vary run-to-run by a few percent.
scenario~600 docs~5,600 docs~55,600 docs
single common term1.121.265.45
two terms2.683.629.91
exact phrase1.461.641.27
typo tolerance2.722.9710.19
rarer term3.042.123.69
filter by author1.612.5210.64
sort by title0.680.793.86
terms facet1.932.7312.65
highlight title2.374.0219.60

Avg ms per query; speedup is core ÷ loupe (>1 = Loupe faster).

queryloupe hitscore hitsloupe avgcore avgspeedup
lorem ipsum90732.632.541.0×
“lorem ipsum”001.382.151.6×
loerm ipzum7502.632.020.8×
consectetur67712.071.180.6×
queryloupe hitscore hitsloupe avgcore avgspeedup
lorem ipsum90733.214.001.2×
“lorem ipsum”001.463.322.3×
loerm ipzum7503.013.761.2×
consectetur67712.202.471.1×
queryloupe hitscore hitsloupe avgcore avgspeedup
lorem ipsum907310.1716.201.6×
“lorem ipsum”001.9816.098.1×
loerm ipzum7509.8716.241.6×
consectetur67713.8215.124.0×

Core search is essentially flat at ~15 ms for every query — the cost of a full LIKE '%term%' scan — while Loupe scales with result fanout.

Front-end search (GET ?s=lorem, full page render)

Section titled “Front-end search (GET ?s=lorem, full page render)”
corpusLoupe activeLoupe off (core)ratio
~600 docs45.42 ms46.16 ms1.02×
~5,600 docs45.49 ms60.97 ms1.34×
~55,600 docs41.97 ms66.21 ms1.58×

Why the single word “lorem” is excluded

Section titled “Why the single word “lorem” is excluded”

The single-word lorem query is left out of the comparison above because its hit counts are not comparable: core reports 90, Loupe reports 2.

Measured on the corpus:

lorem: substring=88 whole-word=1 contain-dolorem=87

The Faker “latin” content contains the word dolorem (and doloremque) in 87 docs, but almost never the word lorem.

  • Core runs LIKE '%lorem%', a blind substring match, so dolorem and doloremque both match → 90 hits, ~88 of them false positives.
  • Loupe matches the token lorem, which is a whole word in only ~1–2 docs → 2 hits. This is more correct, not a miss.

Because the two engines are counting different things, comparing their latency on lorem is meaningless, so the row is dropped.

The two-term lorem ipsum query is OR-based; ipsum is a real word in ~77 docs, so the union is ~90 real word matches — kept because both engines agree on scope. For the cleanest per-row speed comparison, use consectetur (a real whole word in ~71 docs for both engines: 4.0× at ~55,600 docs), or seed real prose by piping text into wp post generate --post_content so query terms are whole words in both engines.