Lesson 7
Queries and what they cache
What a cached query actually stores, when that cache is updated, and what an uncached query does instead.
A query finds entities that match a list of terms, like Position, Velocity. Because entities with the same type share a table, a query rarely needs to look at individual entities: it can match whole tables. That is the first reason archetype queries are fast.
The second reason is caching. Games create most of their tables early on and then keep reusing them. So instead of searching for matching tables every time, a cached query keeps a list of them and only updates it when a table is created or deleted. Iterating a cached query is just walking that list.
flecs has both kinds:
| Cached | Uncached | |
|---|---|---|
| Iterating | walks a prematched list of tables | searches the component index each time |
| Creating | slower: the cache is filled first | cheap, stateless |
| Memory | a list per query | almost none |
| New tables | every cached query is told, and tests the table once | no cost |
By default, queries with an entity (every system, and named queries) are cached, and ad-hoc queries aren’t. You can choose explicitly with the cache kind (.cached() in C++, or EcsQueryCacheAuto / EcsQueryCacheNone in C).
Watch the caches
Three queries are created before the timeline starts. Movers and AdHoc are the same query, cached and uncached, and Heavy is cached too. The badges on each table card show which caches it is in.
Loading the flecs debugger…
What to look for:
- Creating a table notifies caches. When
ship.set<Mass>creates[Position, Velocity, Mass], the log shows each cached query being told about the new table (OnTableCreate) and testing it once.MoversandHeavyboth add it, whileAdHocisn’t involved at all. - Moving into an existing table costs the caches nothing.
rock.set<Mass>moves Rock into a table that already exists, so no query is notified. - Iteration returns tables, not entities. Iterating
moversyields one result per non-empty table it matches, with all of that table’s matching entities in one go;each()then loops over those rows for you. - Caches keep empty tables. After Ship moves out,
[Position, Velocity]is empty but stays inMovers’ cache. Iteration simply skips it: the cache keeps one list of tables and checks each table’s row count as it goes, so a table that empties and refills never has to be re-matched.