Skip to content

perf: improve performance for large workspaces#647

Closed
LK wants to merge 6 commits intomainfrom
lenny/perf-big-ws
Closed

perf: improve performance for large workspaces#647
LK wants to merge 6 commits intomainfrom
lenny/perf-big-ws

Conversation

@LK
Copy link
Copy Markdown
Contributor

@LK LK commented Mar 17, 2026


Open with Devin

Note

Medium Risk
Refactors dependency resolution and evaluation caching paths used by build/resolve, so mistakes could cause incorrect package root/url mapping or stale evaluation state across files. Changes are mostly internal but touch core resolution and build execution paths.

Overview
Speeds up builds in large workspaces by reusing dependency-resolution metadata and evaluation state instead of rebuilding it per file.

resolve_dependencies now takes an optional input_path so the auto-deps scan can be scoped to a specific file/package (and callers are updated accordingly). Dependency resolution also adds cached package_roots and a reverse root → URL map (via OnceLock/Arc) to avoid repeated recomputation and linear scans when formatting/resolving package:// URIs.

Build execution is updated to prepare a shared EvalContextConfig + EvalSession (PreparedBuildEval) and evaluate multiple .zen files via eval_with_config_and_session, resetting only per-root state between runs. PcbToml now treats empty manifest files as default() configs, and tests are updated (plus a new regression test) to ensure branch pinning doesn’t clobber auto-added member deps.

Written by Cursor Bugbot for commit 45f96d4. This will update automatically on new commits. Configure here.

LK added 5 commits March 16, 2026 18:58
Prepare one root eval config per command instead of rebuilding eval startup state for every file, and cache package roots inside ResolutionResult so format_package_uri no longer rebuilds them on hot paths.

On release pcb build /Users/lenny/code/diodeinc/wurth --locked, progress improved from about 6.5k stderr lines in 35s to about 5.5k in 20s. At roughly the 4 minute mark the old build had emitted about 15.0k lines; this version had emitted about 27.3k. Sampling also moved the hotspot from EvalContextConfig::new/build_package_roots churn to Starlark parse/eval.
Keep a shared EvalSession for a build command and reset only per-root module state. This preserves the existing load_cache across root files so repeated stdlib and shared module loads stop being reparsed for every workspace member.

On release pcb build /Users/lenny/code/diodeinc/wurth --locked, the build now finishes in 76.32s. The first 20s progressed from about 5.5k stderr lines on the previous commit to about 11.3k here. Live sampling no longer showed repeated load parsing; the next hotspot is format_package_uri path-prefix matching and symbol-file reads.
Cache a root-to-package map in ResolutionResult and format package URIs by walking parent directories instead of scanning every package root on each call.

On release pcb build /Users/lenny/code/diodeinc/wurth --locked, wall time dropped from 76.32s to 26.10s. The first 20s progressed from about 11.3k stderr lines on the previous commit to about 26.2k here.
@LK LK requested a review from akhilles March 17, 2026 21:48
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 17, 2026

Build Performance

Board Base (median) Head (median) Change
demo/DM0001 195ms ±4 111ms ±3 1.78× ±0.07 faster
demo/DM0002 180ms ±15 93ms ±6 1.97× ±0.21 faster
demo/DM0003 196ms ±11 114ms ±10 1.72× ±0.17 faster
arduino/Nano 127ms ±3 81ms ±7 1.52× ±0.13 faster
arduino/UNOQ 215ms ±5 173ms ±6 1.25× ±0.05 faster

Measured with hyperfine. Times show median ±stddev.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

cursor[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared a fix for 1 of the 2 issues found in the latest run.

  • ✅ Fixed: Removed manifest collection drops version corrections for import-free packages
    • I restored full manifest iteration by including every workspace package pcb.toml, so version corrections now apply even to import-free packages.

Create PR

Or push these changes by commenting:

@cursor push a4eb2ef987
Preview (a4eb2ef987)
diff --git a/crates/pcb-zen/src/auto_deps.rs b/crates/pcb-zen/src/auto_deps.rs
--- a/crates/pcb-zen/src/auto_deps.rs
+++ b/crates/pcb-zen/src/auto_deps.rs
@@ -52,7 +52,15 @@
     let configured_kicad_versions = workspace_info.asset_dep_versions();
 
     let index = CacheIndex::open()?;
-    let mut manifests: Vec<_> = package_imports.keys().cloned().collect();
+    let mut manifest_set: HashSet<PathBuf> = package_imports.keys().cloned().collect();
+    for pkg in packages.values() {
+        let manifest_path = normalize_manifest_path(
+            &pkg.dir(&workspace_info.root).join("pcb.toml"),
+            &workspace_info.root,
+        );
+        manifest_set.insert(manifest_path);
+    }
+    let mut manifests: Vec<_> = manifest_set.into_iter().collect();
     manifests.sort();
 
     for pcb_toml_path in manifests {

if total_normalized > 0 {
workspace_info.reload()?;
if let Some(config) = root_config_update {
workspace_info.config = Some(config);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale in-memory configs after auto-deps phase

Medium Severity

Replacing workspace_info.reload() with per-package updates means packages modified by auto_deps but without branch deps won't have their in-memory pkg.config refreshed. The old reload() refreshed ALL packages from disk when any normalization occurred, catching auto_deps changes for all packages. Now only branch-pinned packages get updated. Since resolution reads pkg.config (in-memory) at line ~586, auto-added dependencies for non-branch-pinned packages are silently dropped from resolution.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bugbot Autofix determined this is a false positive.

This path is already covered because run_auto_deps still calls workspace_info.reload(), refreshing all package configs before dependency collection.

let index = CacheIndex::open()?;
let manifests = collect_manifest_paths(workspace_root, packages, &package_imports);
let mut manifests: Vec<_> = package_imports.keys().cloned().collect();
manifests.sort();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed manifest collection drops version corrections for import-free packages

Low Severity

The deleted collect_manifest_paths function ensured all workspace member manifests were processed, including packages without .zen file imports. The replacement package_imports.keys().cloned().collect() only includes manifests that had scanned imports. Packages with no .zen files (or no imports) will no longer receive the workspace member version corrections performed by mutate_manifest_dependencies.

Fix in Cursor Fix in Web

.read_file(path)
.with_context(|| format!("failed to read {}", path.display()))?;

if content.trim().is_empty() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious why this is needed. I thought blank pcb.toml files should parse fine in the existing code path. is this for performance reasons as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes not a correctness thing just performance, although I suspect this is pretty negligible

@LK LK closed this Mar 31, 2026
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.

2 participants