Skip to content

Reuse the traversal key path in TestFilter's precomputed filter#1744

Open
inju2403 wants to merge 1 commit into
swiftlang:mainfrom
inju2403:inju2403/reuse-traversal-keypath-in-test-filter
Open

Reuse the traversal key path in TestFilter's precomputed filter#1744
inju2403 wants to merge 1 commit into
swiftlang:mainfrom
inju2403:inju2403/reuse-traversal-keypath-in-test-filter

Conversation

@inju2403

@inju2403 inju2403 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Reuse the key path the graph traversal already provides instead of recomputing it from each test's ID while filtering.

Motivation

Following the key-path work in #1725 and #1730 — which made the Graph traversal thread its key path efficiently — this change makes a consumer of that traversal actually use it.

When a precomputed (ID-based) Configuration.TestFilter is applied, Operation.apply(to:) walks the test graph with mapValues, which hands each closure the node's key path. The .precomputed branches discard it (mapValues { _, item in ... }) and instead call selection.contains(item.test), which reconstructs item.test.id and its keyPathRepresentation for every node — allocating a fresh [String] and formatting the source location into a "file:line:column" string each time.

That work is redundant: the graph is built by inserting each test at test.id.keyPathRepresentation (Runner.Plan._constructStepGraph / Plan.init(steps:)), so a node's key path in the graph is that test's key-path representation. The value being recomputed is, by construction, already in hand.

Every path that applies the .precomputed operation does so against that same graph — or, for tag filters, a key-path preserving mapValues of it into FilterItem — so the invariant holds for ID, tag, and combined filters alike; intermediate nodes with no test value are skipped by the existing guard let item.

Modifications

  • In Configuration.TestFilter.Operation.apply(to:), the .precomputed including/excluding branches now use the keyPath provided by mapValues (selection.contains(keyPath)) instead of selection.contains(item.test).
  • Added a comment explaining why the node's key path equals test.id.keyPathRepresentation, mirroring the existing explanatory comment on the sibling .function case.

The result is identical by construction, so behavior is unchanged. This is a small simplification that also drops a per-node ID reconstruction, [String] allocation, and source-location string format from the filtering path (which runs on every swift test --filter and tools-driven ID selection). No public API changes.

Measurements

Isolated micro-benchmark of the per-node membership check (faithful replica of keyPathRepresentation + the trie walk, built with -O, results stable across runs):

Tests OLD (ms) NEW (ms) Speedup
50 0.153 0.036 4.3×
250 0.787 0.202 3.9×
1,000 3.219 0.798 4.0×
5,000 17.502 4.439 3.9×
10,000 35.141 8.778 4.0×

Unlike #1730, this is a constant-factor win (the speedup stays ~4× rather than growing), since it removes a fixed per-node cost rather than reducing complexity — so absolute savings scale linearly with the number of tests filtered (sub-millisecond for typical suites, ~26 ms once per run at 10k tests). The benchmark is a conservative lower bound: the old path additionally rebuilds test.id, which the replica omits.

Existing PlanTests and TestFilter tests pass — selection/exclusion by ID, by tag (via the .function.precomputed path), and combined filters.

Checklist

  • Code and documentation should follow the style of the Style Guide.
  • If public symbols are renamed or modified, DocC references should be updated. (N/A — no public API changes.)

Comment thread Sources/Testing/Running/Configuration.TestFilter.swift Outdated
TestFilter's precomputed filter receives each node's key path from the graph traversal but discards it and reconstructs the same value via `item.test.id.keyPathRepresentation` — re-allocating a [String] and re-formatting the source location to a string per node. Since the graph is built by inserting each test at `test.id.keyPathRepresentation`, the node's key path *is* that value, so use it directly.

Simpler, and removes a per-node allocation + string format from the filtering path. (~3.7x on the isolated per-node membership check; absolute per-run cost is sub-ms — this is a readability cleanup first.)
@inju2403 inju2403 force-pushed the inju2403/reuse-traversal-keypath-in-test-filter branch from 7fa17ac to c7cf6d2 Compare June 5, 2026 12:59
@harlanhaskins

harlanhaskins commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Do you have a rough idea of what percentage of time this represents during a test plan execution? I would imagine 35ms over 10,000 test runs to be a rounding error compared to the tests themselves. Not saying we shouldn't take this change (it's a good change) but I'm wondering what the real-world, non-micro-benchmark impact of it is.

@inju2403

inju2403 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

@harlanhaskins
Agreed — speed-wise it's a rounding error (it runs once during plan construction, before any test executes), so your intuition is right. Speed wasn't really the main goal, though: the change is mostly about not rebuilding a key path the traversal already hands us, so it's a cleanup that also drops a per-node Test.ID reconstruction + allocation. The ~4× is just supporting evidence, not something a user would feel — happy to trim the benchmark framing if you'd rather it land purely as a simplification.

@grynspan

Copy link
Copy Markdown
Contributor

@inju2403 Was this PR AI-assisted like #1773? (Not a criticism or a blocker, just want to label it correctly if so.)

@grynspan grynspan assigned grynspan and inju2403 and unassigned grynspan Jul 10, 2026
@grynspan grynspan added enhancement New feature or request tools integration 🛠️ Integration of swift-testing into tools/IDEs performance 🏎️ Performance issues command-line experience ⌨️ enhancements to the command line interface labels Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

command-line experience ⌨️ enhancements to the command line interface enhancement New feature or request performance 🏎️ Performance issues tools integration 🛠️ Integration of swift-testing into tools/IDEs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants