Repository navigation
Conversation
Matching each patch against every node spent most of its time allocating, and the allocations stalled the patching thread in Boehm GC waits. In a profiled RP-1/RO repatch, NodeMatcher.IsMatch was 55% of the patching thread, WildcardMatch 36%, and GC waits 22%. - WildcardMatch compares strings directly when the wildcard has no * or ?, keeping the regex's match before a final newline, and caches its regex by the wildcard instead of building the pattern on every call. - CheckConstraints parses each constraint string once and caches it. - CheckConstraints visits the matching subnodes in one pass instead of calling FindConfigNodeIn with each index in turn. - FindConfigNodeIn no longer builds a list of the matching subnodes. - KeyValueCache.FetchWithKey takes a delegate that need not capture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every edit patch tested every root node, and NodeMatcher.IsMatch rejects most of them by type alone. PatchApplier now keeps a PatchTypeIndex of the database's list nodes, grouped by node type in list order, and gives it to edit patches, which visit only the nodes of their matcher's type. Edits replace values in place and cannot rename a root node, so they leave the index valid; it is invalidated if a type changes anyway. Copy and delete patches mark their matcher's type stale and insert patches their node type, and a stale group is rebuilt when next needed. IPatch.Apply is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NodeMatcher.IsMatch looked up a node's name value for every patch with a name pattern, once per node of the patch's type. With the previous two commits applied, in a profiled RP-1/RO repatch, that lookup was about 15% of the patching thread. ProtoUrlConfig already read the name value for FullUrl; it now keeps it as NameValue. NodeMatcher.IsMatch(IProtoUrlConfig) matches against it, and edit, copy, and delete patches call that through an INodeMatcher extension that falls back to IsMatch(ConfigNode) for other matchers. This assumes nothing changes a root node's name value in place during patching: edits and copies modify a copy and wrap it in a new ProtoUrlConfig, so renames by patches are seen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Nice. Those numbers looks great. I will let you test it. |
|
One correctness issue in the third commit ( The Special On master, Possible fixes: have the Special branch refresh the affected Found by reading the code; I haven't run this scenario. |
Reduces an uncached launch of a large RP-1 install by about 55% to the main menu, 76% off ModuleManager's patching time. The patched output is identical. Launches with an not-invalidated cache are unaffected.
Commits
1. Speed up patch node matching (bb61d3a).
This commit can be broken up into smaller commits if requested.
WildcardMatchbuilds a regex pattern string and a closure on every call before checking its cache, and runs a regex for patterns with no*or?. This PR compares such names directly and caches regexes by pattern.CheckConstraintsre-parses its constraint string for every node. This PR introduces a new parsed constraint class and caches after the initial parse of each constraint.FindConfigNodeInfor each index.FindConfigNodeInno longer builds a list.2. Index root nodes by type for edit patches (904fc4b). Currently, every edit patch tests every root node, although a matcher only accepts one exact node type. This PR indexes nodes grouped by type, in list order, and edit patches visit only their own type's group. Edits replace values in place and cannot rename a root node, so they leave the index valid. Copy, delete, and insert patches mark the affected type stale, and it is rebuilt when next needed.
IPatch.Applyis unchanged.3. Match patches against each config's stored name value (ccec578). Currently,
NodeMatchercallsGetValue("name")on every node of the patch's type, for every patch with a name pattern. This PR caches the name value whenProtoUrlConfigreads it. Renames already trigger anotherProtoUrlConfigso that's automatically handled. This relies on nothing changing a root node'snamevalue in place during patching, and I couldn't find anything that does.Benchmarks
Time for a forced full repatch. "Patching" is ModuleManager's logged
Ran intime. "Total" runs from launch to the main menu scene change.Verification
ModuleManager.ConfigCache,.TechTree,.Physics, and.ConfigSHAwere identical to the unpatched build's in all six configurations.Behavior differences
CheckConstraintsstops at the first matching subnode. A later subnode with a null name would previously have thrown, and now is not reached. Null node names are not expected in practice.AI Disclosure
Claude Opus 5.5 produced 99% of the code, 100% of the commit messages, and 50% of this PR description. I have lightly reviewed the code and am proceeding with a more thorough review. I will un-draft this PR when I am confident in its implementation. I won't be offended at all if this is considered an issue with POC code instead of a PR and someone reimplements it by hand instead.