Skip to the document
DocsReference

Performance

Measured, not asserted. The method is at the bottom so you can re-run it and disagree.

Head to head #

Same box, same data, same probe: 20,000 attachments, 200 folders (20 roots × 9 children), 16,000 assignments. FileBird 6.5.8 seeded into its own tables with the identical structure, so both trees answer the same question. 2 vCPU, MariaDB, PHP-FPM 8.3.

Media page (list)Media page (grid)wp/v2/media?per_page=40
Core only5 q · 14 ms5 q · 13 ms82 ms
+ this plugin7 q · 13 ms7 q · 14 ms116 ms
+ FileBird13 q · 20 ms13 q · 18 ms92 ms
Both14 q · 19 ms14 q · 21 ms123 ms

Where it wins #

This plugin adds 2 queries to the media library screen. FileBird adds 8. Four times fewer, on the page every media task starts from — and the page time is indistinguishable from core's. It costs roughly nothing to have installed, while FileBird costs about 5 ms and 8 queries on every load.

That gap is the no-build-step decision showing up as a number. FileBird ships roughly 765 KB of React into your admin; this ships plain JavaScript and hand-written CSS.

Where it loses #

This plugin adds 34 ms to wp/v2/media. FileBird adds 10. They are three times better than us on the call the grid actually waits for.

The reason is not flattering but it is understandable: our taxonomies are REST-visible, so core assembles term data for all 40 attachments in the response. FileBird's folders live in custom tables and are not in wp/v2/media at all, so there is nothing for them to slow down. Their advantage here is the direct consequence of the thing we criticise them for — the folders are invisible to the API.

That does not excuse 34 ms. It is an N+1 shape: term data for a page of attachments should be one primed cache, not per-item lookups. Open item, not yet fixed, and published here rather than left out.

The folder tree #

vergeml/v1/tree, 20,000 attachments:

FoldersQueriesHandlerPayload
200422 ms18.8 KB
2,000479 ms189 KB

Flat query count across a tenfold increase in folders. The tree is O(1) in queries. Handler time grows with payload serialisation, which is linear and expected.

FileBird has no equivalent endpoint to compare against — it builds its tree into the page rather than serving it, which is why the comparison above is page-level.

Query counts agree across environments #

The same endpoints, measured on two different VPS with different operating systems, different PHP versions two releases apart, different database versions and different library sizes:

EndpointBox ABox B
vergeml/v1/tree77
organize-step33
organize-run11
organize-cancel22
librarian-schemes11
librarian-batches11

Different hardware, different OS, different PHP, different row counts, same integers. That is what "a property of the algorithm" means, and it is why a disagreement between environments is read as a bug rather than a hardware difference.

(These counts were taken with a different probe and dataset from the folder-scaling table above, which is why the /tree figure differs between the two. Each number is reported as it was measured rather than reconciled after the fact.)

Search by meaning #

A 20,000-image meaning search returns in under half a second. That is possible because a compact projection of each vector is stored beside the full one, so the comparison reads a small column rather than unpacking 20,000 full-precision vectors.

The search also runs under a time budget — 2 seconds by default — and returns what it has rather than stalling the screen.

Method #

  • Probe: an mu-plugin that separates handler time from WordPress boot for REST, and logs queries, milliseconds and memory for admin pages.
  • Page figures: four requests, the last three averaged, the first discarded as cold.
  • Plugins toggled with wp plugin activate/deactivate between runs on the same install, so the data never changes underneath the comparison.

Query count is the number that transfers. It is a property of the algorithm, so it is identical in a WASM sandbox and on real MariaDB — verified. Wall-clock in a WASM sandbox is meaningless: PHP-wasm spends about 2.4 seconds booting WordPress on every request, so core's own endpoints time the same as ours there.

Provenance #

The head-to-head figures were taken on a 20,000-attachment dataset that no longer exists — the library was swept and reseeded while chasing test debris. The numbers stand as recorded, and nothing has been re-measured to replace them, deliberately: a smaller library gives different wall-clock, and quietly swapping one in would turn a real comparison into an incomparable one.

Sizing the AI work #

WorkChunk size
Describing, on screen24 files per step, 8 in flight
Describing, background20 seconds of work per cron tick, chunks of 8
Reference scan200 posts per chunk
Duplicate hashing25 per batch, 200 per request
Folder-sorting apply25 files per step, 3.5 s budget
Clustering200 files per batch, 2.5 s budget

Every one of those is a chunk size chosen to sit inside the 30-second max_execution_time that shared hosting still ships, with room for the last call to finish. Nothing here can time out on a small host, and every one of them resumes where it stopped.

Written from version 3.15.0 of the plugin. Last updated 2026-10-02. All documentation · Legal · Support