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.
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.