Skip to content

perf(plan): share the planning env map via Arc instead of cloning per command#436

Merged
wan9chi merged 1 commit into
mainfrom
plan-env-arc
Jun 11, 2026
Merged

perf(plan): share the planning env map via Arc instead of cloning per command#436
wan9chi merged 1 commit into
mainfrom
plan-env-arc

Conversation

@wan9chi

@wan9chi wan9chi commented Jun 11, 2026

Copy link
Copy Markdown
Member

Motivation

The planning context's env map — the full session environment, typically hundreds of entries — was copied at every boundary it crossed during planning:

  • once into PlanContext per plan (plan_query),
  • once per &&-item by PlanContext::duplicate (each command gets a scoped context),
  • once more per &&-item into ScriptCommand::envs for program lookup and plan-request callbacks,
  • and once per spawn into all_envs.

Of these, only the last one does real work — the spawn env is filtered in place by EnvFingerprints::resolve. Every other copy duplicates an almost-always-identical map just to hand it to the next stage, and the cost scales with task count × command count, not with actual env changes.

The immediate trigger is the upcoming runner-aware caching work (#430/#431): it saves each spawn's full env context into the plan (SpawnCommand::full_envs) so getEnv/getEnvs and tracked-env validation resolve against the plan rather than the live process env. Without sharing, that would add yet another full map copy per spawn; with this change it costs a pointer.

Approach

Wrap the map in Arc with copy-on-write semantics:

  • Duplication and hand-off points (duplicate, ScriptCommand::envs, PlanContext::new) share the map — O(1).
  • The two mutation points pay for a copy only when they change something: prepend_path once per task node (package .bin PATH prepend), and add_envs only when a command actually has prefix envs — an empty prefix set is guarded so it doesn't break sharing.
  • The per-spawn all_envs clone remains, with a comment marking it as the one place the map's contents are genuinely copied.

No behavior change; Session already held the map in an Arc, so its call sites are untouched.

… command

The planning context's env map — typically hundreds of entries — was
copied at every boundary it crossed: once into PlanContext per plan, once
per and-item by PlanContext::duplicate, once more per and-item into
ScriptCommand::envs, and once per spawn into all_envs. Only the last copy
is real work (the spawn env is filtered in place); the rest were clones
of an almost-always-identical map.

Wrap the map in Arc with copy-on-write: duplication and hand-off points
share the map (O(1)), and the two mutation points pay for a copy only
when they actually change something — prepend_path once per task node,
add_envs only when a command has prefix envs (guarded so an empty prefix
set doesn't break sharing).

This also prepares for runner-aware caching, which saves each spawn's
full env context into the plan; with Arc sharing that addition costs a
pointer instead of yet another full copy per spawn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

wan9chi commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@wan9chi wan9chi marked this pull request as ready for review June 11, 2026 14:34
@wan9chi wan9chi merged commit 31fea5b into main Jun 11, 2026
18 checks passed
@wan9chi wan9chi deleted the plan-env-arc branch June 11, 2026 14:34
Comment thread crates/vite_task_plan/src/plan.rs
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