Repository navigation
Conversation
… per ROM hasEntries(), fillBlanks() and addResource() each walked the full QList<Resource> under cacheMutex for every ROM, so a generate run over a large platform was quadratic in the cache size and serialized across threads. On a synthetic mame- sized cache (15,499 ROMs, 70,480 resources, 20.8 MB db.xml) a cache-mode generate took 76.1 s at -t 1 and 54.2 s at -t 8; with a QHash from cache id to list positions it takes 23.3 s and 5.3 s. The generated gamelist and media tree are byte-identical to the unpatched binary's, and a gather that only skips cached ROMs writes an identical db.xml and quickid.xml. The index is built lazily on first use and invalidated by the bulk mutators (read, edit, purge, vacuum, validate/verifyFiles, merge), which all run before any worker thread exists. addResource() under --flags refresh replaces the resource in place rather than remove-and-append, so positions stay valid; if the replacement's media cannot be written the stale resource is removed as before and the index rebuilt. Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
I was aware of that inefficiency. Thanks for the numbers. I am not sold by the implementation, though: PS: Thanks for flagging your PR as AI assisted. |
Replace the flat QList<Resource> plus lazily rebuilt id index with a QMap<QString, QList<Resource>> filled directly by read(), as suggested in review. Per-rom lookups (hasEntries, fillBlanks, addResource, report, edit mode) go straight to the rom's list, and there is no index to invalidate after purge, vacuum, validate, edit or merge. Vacuum and validate now use set lookups instead of list scans. db.xml is written in cache id order rather than insertion order; the set of resources written is unchanged. On a synthetic 3,000-rom cache, db.xml content, cached media, gamelist, output media and reports match master after generate, edit, purge, vacuum, merge, validate, report and import. On 15,000 roms / 151,500 resources, cache-mode generation takes 23.9 s at -t 1 and 5.2 s at -t 8 (master: 123 s / 95.5 s). Co-Authored-By: Claude Opus 5.5 <[email protected]>
db.xml v2 stores the resources of a rom inside <game id="...">, so the id is written once per game instead of on every resource, and the file maps directly onto the in-memory cache. The root element carries version="2". read() accepts both layouts (a resource without its own id takes the id of the enclosing game), so existing caches load as before and are written as v2 on the next save. On 151,500 resources db.xml shrinks from 24.8 MB to 19.6 MB. Skyscraper 3.21.1 and older cannot read v2, so the old layout stays available as format 'legacy-v1': - config.ini cacheFormat="legacy-v1" ([main] or [<PLATFORM>]) writes it on every save - --cache convert:<FORMAT> rewrites db.xml once, for one platform or, without -p, for all platforms Docs: CACHE.md format section with downgrade warning, CLIHELP.md, CONFIGINI.md, config.ini.example and CHANGELOG.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Good, the side index was a workaround. I've pushed a commit that replaces it: On a synthetic cache (3,000 ROMs, shuffled resources, orphans), master and this branch produce the same
Happy to squash before merge. I've also added the new storage format to this PR, using the naming already in <resources version="2">
<game id="ABC123">
<resource type="title" source="screenscraper" timestamp="...">Foo</resource>
...
</game>
</resources>
Docs ( I assume |
Cache: store resources by cache id, add db.xml format v2
Cache::hasEntries(),Cache::fillBlanks()andCache::addResource()each walked the fullQList<Resource>undercacheMutexfor every ROM. Generating a gamelist for a large platform was therefore quadratic in the cache size, and because every scan held the same mutex, extra threads (-t) barely helped.In memory
resourcesis now aQMap<QString, QList<Resource>>(cache id -> that ROM's resources), filled directly byread(). Per-ROM lookups go straight to that ROM's list, and there is no separate index to keep in sync after purge, vacuum, validate, edit or merge. Vacuum and validate also use set lookups instead of list scans.db.xmlis written in cache id order instead of insertion order; the set of resources is unchanged.On disk: format v2
read()accepts both layouts, so existing caches load unchanged and are written as v2 on the next save.legacy-v1:config.ini:cacheFormat="legacy-v1"in[main]or[<PLATFORM>]writes it on every save.--cache convert:<FORMAT>rewritesdb.xmlonce; without-pit converts all platforms.db.xmlas empty, soCACHE.mdand the changelog tell users to run--cache convert:legacy-v1before going back to an older version.Docs:
CACHE.md,CLIHELP.md,CONFIGINI.md,config.ini.example,CHANGELOG.md(entry under 3.22.0 TBA) and the--cache helptext.Measurements
Synthetic snes cache, 15,000 ROMs / 151,500 resources, cache-mode generation with
--flags pretend:-t 1-t 8Verification
--cache edit(m,t,d,D,edit:new=title),purge:m=thegamesdb,t=cover,vacuum,merge:,validatewith a stray file,report:missing=all, two-s importruns (refresh path), generate again. After each step thedb.xmlresource set and the cached media are identical; both gamelists, the output media, all 19 report files andquickid.xmlare identical.legacy-v1->v2->legacy-v1keeps the resource set identical; master reads and rewrites thelegacy-v1output unchanged;v2->v2is byte-identical.cacheFormatin[main]and in a platform section, invalid values (warning forconfig.ini, exit 2 for--cache convert:), and convert across all platforms.test/suite passes;test_settingscoverscacheFormat.🤖 Generated with Claude Code