perf(locator): stop searching once the caller's matches are found - #5
Open
caipira113 wants to merge 1 commit into
Open
perf(locator): stop searching once the caller's matches are found#5caipira113 wants to merge 1 commit into
caipira113 wants to merge 1 commit into
Conversation
Resolution collected every match before discarding all but the ones the caller asked for. On an application that materialises accessibility children on demand this dominates runtime: with a large document open, `snapshot --depth 4` on Word took 139s because the walk continued through the document canvas long after the answer was known. Each step now receives the number of candidates the following steps can actually consume, computed backwards from the caller's request. `nth=N` needs N+1, `first` needs one, and `last`, a negative `nth`, or a following search step still need the full set, so those keep walking. `resolve_one` asks for two: one to act on, one to prove ambiguity. Measured on Word with a 74k-character document open: snapshot --depth 4 139.55s -> 0.14s snapshot splitgroup 94.43s -> 0.11s get role splitgroup >200s -> 1.98s Results are unchanged. The one visible difference is the ambiguity error, which now reports "at least N" rather than a total: proving ambiguity no longer requires counting every match, and paying a full traversal for a number the caller cannot act on is what made the error slower to produce than the successful path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Locator resolution collects every match before discarding all but the ones the caller asked for.
resolve()callsresolve_all()and takes the first element;nth=Nandfirstare applied as selection steps, after the search that produced the candidates has already finished walking the tree.On a small tree that is invisible. On an application that materialises accessibility children on demand it dominates runtime, because the walk keeps asking the app to build elements long after the answer is known. Microsoft Word with a 74k-character document open, where
AXLayoutAreareportskids=0until something descends into it:The same traversal primitives are fine when they stop early — a depth-capped walk of the whole app costs 0.07s — so this is the search continuing past the point of usefulness rather than the AX tree being inherently slow.
It also makes failure slower than success: proving a locator ambiguous walked the entire tree to produce an exact count, so the error path cost more than the path that found an element.
Change
Each pipeline step now receives the number of candidates the steps after it can actually consume, computed backwards from what the caller requested.
nth=N(N ≥ 0)firstlastnth=-NCallers supply the final budget:
resolve_oneasks for two (one to act on, one to prove ambiguity),snapshotwithout--allasks for two (one to print, one to report that more exist), andlocate_allasks forusize::MAX, which reproduces the previous behaviour exactly.Three bounded variants were added alongside the existing functions rather than replacing them —
find_limited,collect_matching_limited,resolve_locator_limited— each equal to its unbounded counterpart atusize::MAX.Verification
Tested on macOS 27.0 (build 26A5388g, arm64), Word 16.111.3.
Word — a large document open, the pathological case
snapshot --depth 4 --simplifysnapshot splitgroup --depth 1get role splitgroupFinder — output equivalence
Ten locators covering each budget rule, comparing the old binary against the new one:
The tenth is the intended difference described below; its selected element and printed tree are identical.
cargo testpasses (62 tests, 7 new) andcargo clippyreports 47 warnings both before and after, so no new ones.One deliberate behaviour change
The ambiguity message now reports a lower bound:
An exact total requires the full traversal this change exists to avoid, and it is a number the caller cannot act on — the suggested fix is
>> nth=Neither way.--allstill reports the true count, because it genuinely collects every match.Not addressed
A locator matching exactly one element is still slow on Word: proving there is no second match requires exhausting the tree. That is a traversal-order problem — DFS descends into the document subtree before reaching siblings such as the menu bar — and changing the order would change which element
nth=Nselects, so it is left out of this PR.