⚡ Bolt: [performance improvement] Avoid Vec allocations in tree traversal - #89
Conversation
Bumps edition 2021 -> 2024 across all 11 crates (rust-version 1.93 already met the 1.85 MSRV). Full verification: clippy -D warnings, fmt --check, and cargo test --workspace (94 suites, 3825 tests) all clean on a from-scratch rebuild. Pre-migration: - cargo fix --edition applied mechanical idiom fixes (redundant `ref` bindings removed under 2024's match-ergonomics rules). Drop-order review: - Edition 2024 changes when temporaries with custom Drop impls get dropped inside if-let/while-let/match statements. Audited all 33 unique sites the migration tool flagged: bytes::Bytes refcounts, SecretString/SigningKey zeroing (earlier drop is safer, not riskier), CancellationToken/JoinHandle/tokio-internal timer drops, hashbrown/ moka internal cleanup, sqlx query results, object_store BoxStream. None had cross-statement dependencies or lock-release-order risk; no code changes needed. Mandatory-unsafe fix: - Edition 2024 requires unsafe blocks around std::env::set_var/ remove_var, conflicting with this workspace's unsafe_code = deny policy in 2 test files (orch8-engine, orch8-server; 9 call sites). Added serial_test to workspace.dependencies and tagged the affected tests #[serial(...)] to eliminate the actual env-var race, then added scoped #[allow(unsafe_code)] with SAFETY comments referencing the serialization. Idiom modernization: - Converted ~95 clippy::collapsible_if sites to if-let chains across orch8-engine, orch8-api, orch8-server, orch8-mobile, orch8-cli, orch8-grpc, and orch8. Sites with an else branch or a multi-statement body were left as nested ifs, since a chain can't express those. Also includes a workspace-wide cargo fmt normalization pass (import ordering) after a local rustfmt version drift surfaced on nearly every touched file.
…context field StepContext's doc comment claimed it was "cheaply cloneable," but that was never measured. Added a criterion benchmark (step_context_clone_* in engine_bench.rs) that clones StepContext at realistic ExecutionContext.data sizes: ~1us empty, ~113us at 10KB, ~596us at 50KB, and ~3.2ms at 256KB (the engine's own DEFAULT_MAX_CONTEXT_BYTES ceiling). agent.rs's tool-calling loop clones the context once per LLM call and once per tool call, so this scales with iteration count on top of the per-clone cost. Fix: wrap StepContext.context in Arc<ExecutionContext>. Audited every handler in the crate first to confirm none mutate .context directly (the scheduler is the sole writer, on its own owned copy) — read call sites are unaffected via Deref, and the two test-only sites that flip the dry_run flag now go through Arc::make_mut instead of a direct field assignment. Every construction site that used to clone/move a plain ExecutionContext now wraps it in Arc::new once at construction; every downstream StepContext::clone() (agent.rs, step_block.rs, step_exec.rs, and anywhere else that clones a context to build a "sub" context for a nested dispatch) is now an O(1) refcount bump instead of an O(context size) deep clone. Verified: cargo check/clippy -D warnings/fmt --check clean across the full workspace (StepContext's field is public but every consumer besides the handlers crate itself only reads through it).
Co-authored-by: ovasylenko <3797513+ovasylenko@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
396e1d8 to
de325b7
Compare
💡 What: Replaced uses of
children_of(...).into_iter().any(...)with directtree.iter().any(...)inline checks inis_inside_decided_raceandhas_racing_composite_sibling.🎯 Why:
children_ofallocates a newVecof matching children on the heap every time it's called. For.any()checks (which are designed to short-circuit), eagerly evaluating and allocating a vector of all children is pure overhead on execution hot paths.📊 Impact: Eliminates 1-2 heap allocations per step evaluation when traversing upward to check for running racing siblings or decided races, marginally speeding up node processing and reducing GC/allocator pressure under high concurrency.
🔬 Measurement: Code was verified against the
evaluator::teststest suite to ensure functional exactness.PR created automatically by Jules for task 8074531284134042395 started by @ovasylenko