Skip to content

feat(core): compile board graph topology on first execution#572

Open
SBALAVIGNESH123 wants to merge 1 commit into
Rheosoph:devfrom
SBALAVIGNESH123:feat/compiled-board-cache
Open

feat(core): compile board graph topology on first execution#572
SBALAVIGNESH123 wants to merge 1 commit into
Rheosoph:devfrom
SBALAVIGNESH123:feat/compiled-board-cache

Conversation

@SBALAVIGNESH123

Copy link
Copy Markdown

Summary

Resolves #71Graphs should be compiled on first execution or version creation to speed-up future executions and loading times.

Problem

Every call to InternalRun::new_with_run_id() performs ~370 lines of O(N²+E) graph construction:

  1. Create InternalPins from board pins — O(N×P)
  2. Wire OnceLock connections — O(edges)
  3. Instantiate nodes from registry — O(N)
  4. Instantiate function layer nodes — O(L×N)
  5. Dependency resolution — O(N²) worst case

All of this is deterministic for a given board version and repeated identically every execution.

Solution

Cache the compiled graph topology (pins, nodes, dependencies) on first execution, keyed by a content hash (HighwayHash) of the board. Subsequent runs reuse the cached topology and only reset mutable runtime state.

Key design insight: InternalPin uses OnceLock for graph wiring (connected_to, depends_on, node), making the topology immutable after construction. Only pin.value: RwLock<Option<Value>> is mutable — and pin.reset() already clears it.

Changes

File Change
compiled.rs NEWCompiledBoard struct with compile(), reset_for_new_run(), compute_board_hash(), is_valid_for()
execution.rs Cache lookup at top of new_with_run_id() + from_compiled() fast path + cache store after slow path
state.rs compiled_boards: DashMap field on FlowLikeState

Fast Path Performance

  • Skips: O(N×P + E + N²) graph construction entirely
  • Reset cost: O(P + N) with zero allocations (atomic stores + RwLock writes)
  • Per-run: Fresh Run, variables, cache for full isolation

Safety

  • Graph topology (OnceLock) is never modified after compilation
  • Each InternalRun gets its own Run, variables, and cache
  • Content hash invalidation: any board change → hash mismatch → recompile
  • Zero changes to node APIs — completely transparent to all catalog nodes
  • Existing new_with_run_id() preserved as fallback (slow path)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a caching layer for board graph topologies to improve execution performance by skipping expensive graph construction phases. A new CompiledBoard struct handles the compilation and hashing of board structures, while InternalRun is updated with a fast-path initialization method. However, the review highlights a critical concurrency issue where mutable runtime state, such as pin values and execution counters, is shared across concurrent runs using the same cached topology. There is also significant code duplication between the new caching logic and existing initialization paths that should be refactored to ensure maintainability. Other feedback includes replacing println! with the tracing crate for diagnostics and removing an unused variable in the compilation logic.

Comment thread packages/core/src/flow/execution/compiled.rs
Comment thread packages/core/src/flow/execution/compiled.rs Outdated
Comment thread packages/core/src/flow/execution.rs
Comment thread packages/core/src/flow/execution/compiled.rs Outdated
Comment thread packages/core/src/flow/execution.rs Outdated
@codacy-production

codacy-production Bot commented Apr 28, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 14 complexity

Metric Results
Complexity 14

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@SBALAVIGNESH123 SBALAVIGNESH123 force-pushed the feat/compiled-board-cache branch from 8513a3d to 0c1917f Compare April 28, 2026 12:33
@SBALAVIGNESH123

Copy link
Copy Markdown
Author

Thanks for the thorough review! All valid points — I've pushed a v2 that addresses everything:

Concurrency bug (critical) — You're absolutely right, sharing Arc across concurrent runs would corrupt pin values. Fixed by restructuring CompiledBoard to only cache the stateless Arc instances (which are Send + Sync by trait contract and take &self in run()). Each execution now creates its own fresh pins, nodes, and wiring — no mutable state is shared between runs.

Code duplication — Removed the standalone compile() method entirely. from_compiled() now follows the same phase structure as new_with_run_id() (pin creation → wiring → node instantiation → dependency resolution), just swapping registry.instantiate() for a cached logic lookup. Happy to extract shared helpers in a follow-up if you'd prefer less duplication, but wanted to keep this PR's diff minimal against the existing code.

Unused variable & println — Removed pin_to_node from the old compile path, replaced println! with tracing::info!.

The main perf win is skipping registry.instantiate() on subsequent runs — especially useful for WASM nodes where that can involve module loading. Graph wiring still runs fresh each time, which is the right tradeoff for correctness.

)

Cache stateless NodeLogic instances from the expensive registry.instantiate()
call so subsequent executions of the same board version skip it entirely.

Design: only Arc<dyn NodeLogic> (stateless, Send+Sync) is cached. Each run
creates its own fresh InternalPins, InternalNodes, variables, and cache to
guarantee full isolation between concurrent executions.

Key changes:
- Add CompiledBoard struct that stores cached NodeLogic instances with
  content-hash-based invalidation via HighwayHash
- Add InternalRun::from_compiled() fast path that creates fresh graph
  topology per run but reuses cached NodeLogic (skips registry lookups)
- Add compiled_boards cache (DashMap) to FlowLikeState
- First execution compiles and caches NodeLogic; subsequent runs hit fast path
- Zero changes to node APIs -- completely transparent to all catalog nodes

Concurrency safety:
- No mutable runtime state (pin values, exec counters) is shared between runs
- Each InternalRun gets its own InternalPin, InternalNode, Run, variables
- NodeLogic::run() takes &self (not &mut self) -- stateless by contract

Closes Rheosoph#71
@SBALAVIGNESH123 SBALAVIGNESH123 force-pushed the feat/compiled-board-cache branch from 0c1917f to f459cda Compare April 28, 2026 13:04
@felix-schultz

Copy link
Copy Markdown
Member

Thank you very much for the PR! I will review it later :)

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.

Compile Graphs

2 participants