Hive Hive
Sign in

perf(cli): use Set for project path lookups in tree-shake mapper

GitHub issue · Closed

Metadata
Source
tuist/tuist #10033
Updated
Jun 24, 2026
Domains
Generated projects
Details

Description

Optimize treeShake(workspace:projects:sourceTargets:) in TreeShakePrunedTargetsGraphMapper by replacing a repeated Array.map().contains() with a pre-built Set lookup.

Before:

let projects = workspace.projects.filter { projects.map(\.path).contains($0) }
projects.map(\.path) creates a new array on every iteration of filter, making the overall complexity O(n²).
After:
let projectPaths = Set(projects.map(\.path))
let projects = workspace.projects.filter { projectPaths.contains($0) }
The Set is built once and each contains call is O(1), reducing the overall complexity to O(n).
How to test locally
This is a pure refactor with no behavioral change. Existing tests for TreeShakePrunedTargetsGraphMapper cover the affected code path.
Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments
L
likaiacorns May 15, 2026

We validated this fix against our production monorepo (~750 targets) and can confirm a significant, measurable improvement.

Local benchmarks (Tuist 4.194.4, M-series Mac, tuist generate against our full iOS project):

Scenario Without fix With fix
Cold generate 232s 181s (-22%)
Warm generate (run 1) 21s 15s (-29%)
Warm generate (run 2) 20s 15s (-25%)

The improvement is consistent across cold and warm runs. The O(n²) from workspace.projects.filter { projects.map(\.path).contains($0) } rebuilding the array on every iteration becomes very visible at our target count.

Would love to see it merged in an upcoming release. Happy to help test a pre-release build if useful.

cc @fortmarek

P
pepicrft May 18, 2026

@inju2403 do you mind solving the git conflicts?

I
inju2403 May 18, 2026

@pepicrft Conflict resolved — kept the Set optimization while adopting main’s new prunedTargets: signature. Thanks!

L
likaiacorns May 18, 2026

Thank you all!

P
pepicrft May 19, 2026

Thanks a lot @inju2403 🙏🏼. Keep those optimizations coming

P
pepicrft May 19, 2026

@all-contributors add @inju2403 for code

A
allcontributors[bot] May 19, 2026

@pepicrft

I’ve put up a pull request to add @inju2403! 🎉

I
inju2403 May 19, 2026

@pepicrft Thanks a lot for the review and merge! 🙏 Glad you’re up for more — turns out a few more small perf PRs from the same review pass are already up:

  • #10034 — SchemeLinter: Array → Set (O(n) → O(1))
  • #10035 — DefaultSettingsProvider: precompute Set outside closure (per-call O(n) → O(1))
  • #10036 — TargetContentHasher: Array → Set on a hot path (O(n) → O(1))
  • #10038 — GraphLoader: sorted().firstmin() (O(n log n) → O(n))
  • #10039 — ContentHasher: string += loop → joined(separator:) (O(n²) → O(n))

All pure refactors, no behavior changes.