Skip to content

perf: Optimize engine builder, task visitor, and untracked file discovery#11956

Merged
anthonyshew merged 2 commits intomainfrom
faster-and-faster
Feb 21, 2026
Merged

perf: Optimize engine builder, task visitor, and untracked file discovery#11956
anthonyshew merged 2 commits intomainfrom
faster-and-faster

Conversation

@anthonyshew
Copy link
Copy Markdown
Contributor

@anthonyshew anthonyshew commented Feb 21, 2026

Summary

Three targeted optimizations to the turbo run hot path, focused on areas identified via --profile on repos of varying sizes.

Benchmarks

Hyperfine (30 runs, 10 warmup) comparing mainline to this branch across three internal monorepos:

Large repo (~1000 packages)

Mean Range
mainline 1.265s ± 0.176s 1.091s – 1.756s
this PR 1.514s ± 0.115s 1.370s – 1.801s
1.20–1.26× faster

build_engine self-time: 283ms → 74ms (−74%). queue_task self-time: 330ms → 181ms (−45%).

Medium repo (~120 packages)

Mean Range
mainline 807ms ± 82ms 744ms – 1149ms
this PR 823ms ± 71ms 765ms – 1055ms
~1.02× (within noise)

Small repo (~5 packages)

Mean Range
mainline 581ms ± 40ms 523ms – 714ms
this PR 579ms ± 50ms 512ms – 708ms
~1.00× (within noise)

The optimizations scale with packages × tasks, so the large repo sees the most benefit. Medium and small repos are dominated by filesystem I/O (find_untracked_files) and lockfile parsing, which are unaffected.

Changes

  • Engine builder: Cache the turbo.json extends chain per package name and move the visited set check before task_definition(). The chain resolution only depends on the package, not the task, so all tasks in the same package share the cached chain. The early visited check avoids recomputing task definitions for duplicate BFS entries.
  • Task visitor: Defer the env() call to the non-dry-run branch. The execution environment map is unused during dry runs, so this skips per-task RwLock acquisition, DetailedMap cloning, and wildcard regex matching in that path.
  • find_untracked_files: Replace Mutex<Vec> with per-thread local buffers that flush via mpsc channel on drop. This eliminates per-file mutex contention in the parallel directory walker.

Testing

All existing tests pass across the three modified crates:

  • turborepo-engine: 62 tests (covers extends chains, cycles, diamond inheritance, extends: false, BFS graph construction)
  • turborepo-scm: 110 tests (covers untracked file detection, git index equivalence, package boundary isolation)

@anthonyshew anthonyshew requested a review from a team as a code owner February 21, 2026 21:02
@anthonyshew anthonyshew requested review from tknickman and removed request for a team February 21, 2026 21:02
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examples-basic-web Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
examples-designsystem-docs Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
examples-gatsby-web Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
examples-kitchensink-blog Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
examples-nonmonorepo Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
examples-svelte-web Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
examples-tailwind-web Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
examples-vite-web Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
turbo-site Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
turborepo-agents Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm
turborepo-test-coverage Ready Ready Preview, Comment, Open in v0 Feb 21, 2026 9:07pm

…very

Three targeted optimizations to the turbo run hot path:

1. Engine builder: Cache turbo.json chain per package and move the
   visited check before the expensive task_definition() call. The
   chain only depends on the package name, so multiple tasks in the
   same package reuse the cached result.

2. Task visitor: Defer env() computation to non-dry-run branches.
   The execution environment is unused during dry runs, avoiding
   per-task RwLock acquisition and env var map cloning.

3. find_untracked_files: Replace Mutex<Vec> with per-thread local
   buffers flushed via mpsc channel on drop, eliminating per-file
   mutex contention in the parallel walker.
@github-actions
Copy link
Copy Markdown
Contributor

Coverage Report

Metric Coverage
Lines 75.11%
Functions 46.77%
Branches 0.00%

View full report

@anthonyshew anthonyshew merged commit e145bc6 into main Feb 21, 2026
103 checks passed
@anthonyshew anthonyshew deleted the faster-and-faster branch February 21, 2026 21:20
github-actions Bot added a commit that referenced this pull request Feb 21, 2026
## Release v2.8.11-canary.19

Versioned docs: https://v2-8-11-canary-19.turborepo.dev

### Changes

- release(turborepo): 2.8.11-canary.18 (#11954) (`ef22b25`)
- perf: Resolve literal input paths via stat instead of glob walk
(#11955) (`b0048b0`)
- perf: Optimize engine builder, task visitor, and untracked file
discovery (#11956) (`e145bc6`)

---------

Co-authored-by: Turbobot <turbobot@vercel.com>
github-actions Bot added a commit that referenced this pull request Feb 22, 2026
## Release v2.8.11-canary.20

Versioned docs: https://v2-8-11-canary-20.turborepo.dev

### Changes

- perf: Optimize engine builder, task visitor, and untracked file
discovery (#11956) (`e145bc6`)
- release(turborepo): 2.8.11-canary.19 (#11957) (`be8c782`)
- perf: Parallelize `turbo run` pre-execution hot path (#11958)
(`b79b680`)

---------

Co-authored-by: Turbobot <turbobot@vercel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant