Skip to content

⚡ Bolt: Optimize list literal evaluation - #337

Merged
logbie merged 1 commit into
mainfrom
bolt/optimize-list-evaluation-11608627115757947855
Feb 11, 2026
Merged

logbie merged 1 commit into
mainfrom
bolt/optimize-list-evaluation-11608627115757947855

Conversation

@logbie

@logbie logbie commented Feb 10, 2026

Copy link
Copy Markdown
Collaborator

💡 What: Optimized Literal::List evaluation in the interpreter.
🎯 Why: Creating list literals previously incurred significant overhead due to Box::pin allocation for every element, even if they were simple constants.
📊 Impact: ~2.8x speedup in tight loops creating lists. Benchmark reduced from 2742ms to 968ms.
🔬 Measurement: Verified with a benchmark script creating 1 million lists of 10 integers.


PR created automatically by Jules for task 11608627115757947855 started by @logbie

Summary by CodeRabbit

  • Refactor
    • Enhanced expression evaluation with optimized synchronous paths for simple expressions (literals, variables, and operations).
    • Refined environment parameter passing through the evaluation pipeline.

- Updates `evaluate_literal_direct` to accept `Environment` and recursively evaluate list elements synchronously using `try_evaluate_simple_expr_sync`.
- Avoids `Box::pin` allocation and async overhead for list literals where all elements can be evaluated synchronously (e.g., constants, variables, simple math).
- Fallback to async evaluation is preserved if any element requires it (e.g., async function call).
- Benchmark shows ~2.8x speedup (2742ms -> 968ms) for creating 1 million lists.

Co-authored-by: logbie <1138960+logbie@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 10, 2026 12:47
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 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 @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@claude

claude Bot commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

Claude finished @logbie's task —— View job


Code Review Complete ✅

Reviewing the list literal optimization PR. Here's my comprehensive feedback:

Todo List:

  • Read and analyze the changed file (src/interpreter/mod.rs)
  • Understand the optimization implementation
  • Evaluate code quality and best practices
  • Check for potential bugs or edge cases
  • Assess performance considerations
  • Review security implications
  • Evaluate test coverage needs
  • Provide comprehensive feedback

📋 Code Review Feedback

Overall Assessment: LGTM with Minor Suggestions

This is a well-implemented optimization that addresses a real performance bottleneck. The approach is sound and the implementation follows good patterns.

🎯 Code Quality & Best Practices

Positive aspects:

  • Clear optimization strategy: The fallback pattern (Ok(None) → async path) is elegant and safe
  • Recursive design: Properly handles nested lists by recursively calling try_evaluate_simple_expr_sync
  • Memory efficiency: Uses Vec::with_capacity(elements.len()) for pre-allocation
  • Consistent error handling: Maintains the same error propagation patterns as existing code

Minor suggestion:

  • The comment "This works for nested lists as well" could be more specific about the optimization benefits for nested structures

🐛 Potential Issues & Edge Cases

No critical bugs found, but consider these edge cases:

  1. Mixed synchronous/asynchronous elements: The optimization correctly falls back to async evaluation when any element requires it - this is the right approach
  2. Large lists: The pre-allocation with Vec::with_capacity(elements.len()) is good, but extremely large lists might benefit from chunked processing in the future
  3. Memory pressure: No immediate concerns, but the optimization creates all list values in memory at once vs. the previous lazy evaluation approach

Performance Considerations

Excellent optimization approach:

  • Synchronous path optimization: Eliminates Box::pin allocation overhead for simple list literals
  • Early bailout: Smart design that falls back to async immediately when encountering complex expressions
  • Pre-allocation: Using Vec::with_capacity prevents repeated allocations during list building

Performance impact assessment:

  • Positive: ~2.8x speedup for simple list literals (as claimed in PR description)
  • Neutral: No performance regression for complex lists (falls back to original async path)
  • Scalable: Recursive optimization works for nested list structures

🔒 Security Concerns

No security issues identified:

  • No new attack vectors introduced
  • Maintains existing error handling and bounds checking
  • No unsafe code or external dependencies added
  • Preserves the same evaluation semantics

🧪 Test Coverage Assessment

Current test coverage:

  • ✅ Existing WFL test files cover basic list functionality (TestPrograms/docs_examples/basic_syntax/lists_01.wfl, etc.)
  • ✅ List creation patterns are tested in multiple files

Recommendations for additional tests:

// Consider adding a performance regression test
#[test]
fn test_list_literal_performance() {
    // Benchmark simple list creation to catch future regressions
    let start = std::time::Instant::now();
    for _ in 0..10000 {
        // Create lists with simple literals
    }
    let duration = start.elapsed();
    assert!(duration < Duration::from_millis(100)); // Adjust threshold as needed
}

#[test] 
fn test_nested_list_optimization() {
    // Test that nested lists of literals are optimized
    // [[1, 2], [3, 4]] should be evaluated synchronously
}

#[test]
fn test_mixed_sync_async_fallback() {
    // Test lists containing both simple literals and function calls
    // Should properly fall back to async evaluation
}

📍 Specific Code References

The optimization is implemented in:

  • src/interpreter/mod.rs:5749: Modified evaluate_literal_direct to handle Literal::List
  • src/interpreter/mod.rs:5765-5779: Core optimization logic with proper fallback mechanism
  • src/interpreter/mod.rs:5823: Updated function signature to include env parameter

🚀 Recommendations

  1. Consider adding benchmark tests to catch future performance regressions
  2. Document the optimization in code comments explaining when sync vs async paths are used
  3. Monitor memory usage in production for very large list operations

Summary

This is a solid performance optimization that:

  • Significantly improves list literal evaluation performance
  • Maintains backward compatibility and correctness
  • Uses safe fallback patterns for complex expressions
  • Follows existing codebase patterns and conventions

Recommendation: Approve and merge 🎉


@coderabbitai

coderabbitai Bot commented Feb 10, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Modified the interpreter's evaluation logic to support synchronous fast-path evaluation of literals and simple expressions. Added environment parameter to evaluate_literal_direct and introduced helper functions (try_evaluate_simple_expr_sync, try_evaluate_variable_sync, handle_variable_auto_call) to enable synchronous evaluation paths for list elements and simple expressions.

Changes

Cohort / File(s) Summary
Interpreter Synchronous Evaluation Scaffolding
src/interpreter/mod.rs
Added env parameter to evaluate_literal_direct signature; refactored Literal::List handling to evaluate elements synchronously via new helper try_evaluate_simple_expr_sync, returning Ok(None) if async evaluation required; introduced new sync utilities: try_evaluate_variable_sync for variable lookup with auto-call handling, and handle_variable_auto_call for zero-arity function invocation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Hops with delight at the speedy path ahead,
Sync evaluation glimmers through interpreter's thread,
Literals and variables, now swift in their way,
Async steps back when sync saves the day!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '⚡ Bolt: Optimize list literal evaluation' clearly summarizes the main change: optimizing list literal evaluation for performance. It directly relates to the core objective of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bolt/optimize-list-evaluation-11608627115757947855

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

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.

Pull request overview

Optimizes list literal (Literal::List) evaluation by attempting synchronous evaluation of list elements to avoid per-element Box::pin overhead.

Changes:

  • Extends evaluate_literal_direct to accept env so list elements can be evaluated synchronously.
  • Adds a fast path for Literal::List that builds the list directly when all elements are sync-evaluable.
  • Updates the call site in try_evaluate_simple_expr_sync to pass env through.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/interpreter/mod.rs
Comment on lines +5766 to +5776
let mut list_values = Vec::with_capacity(elements.len());
for element in elements {
// Recursively try to evaluate elements synchronously.
// This works for nested lists as well.
if let Some(value) = self.try_evaluate_simple_expr_sync(element, env)? {
list_values.push(value);
} else {
// Element requires async evaluation, abort sync optimization for the whole list
return Ok(None);
}
}

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

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

If a later element requires async evaluation, this returns Ok(None) after already computing earlier elements, which will be recomputed again by the fallback (boxed/async) list path. Consider a two-pass approach (first pass checks whether all elements are sync-evaluable without producing Values; second pass evaluates), or refactor to preserve and reuse the already-evaluated prefix when falling back.

Copilot uses AI. Check for mistakes.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 84a83dbdf0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/interpreter/mod.rs
Comment on lines +5770 to +5774
if let Some(value) = self.try_evaluate_simple_expr_sync(element, env)? {
list_values.push(value);
} else {
// Element requires async evaluation, abort sync optimization for the whole list
return Ok(None);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Prevent double-evaluating list elements on async fallback

The new Literal::List fast path evaluates each element immediately and only returns None when it later encounters a non-sync expression, which causes evaluate_expression to re-run the entire list in _evaluate_expression. In mixed lists (for example [random, some_call(...)] where random is auto-called as a zero-arg native function), earlier elements are executed twice, changing observable behavior (extra RNG/time calls or other side effects) compared to the previous single evaluation path.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/interpreter/mod.rs`:
- Around line 5765-5778: The fast sync path for Literal::List uses
try_evaluate_simple_expr_sync to evaluate elements and returns Ok(None if any
element needs async), which causes already-evaluated earlier elements to be
re-run in the async path and can double-execute side effects; modify the logic
in the Literal::List handling so you first pre-scan elements (calling a
lightweight check like try_evaluate_simple_expr_sync in "probe" mode or adding a
new helper) to detect whether any element requires async without executing
side-effecting evaluations, and if a mix is detected either (1) preserve the
already-evaluated prefix (store values produced so far) and continue evaluation
asynchronously for the remaining elements, or (2) perform a pure pre-scan pass
that only determines async-necessity and then run a single evaluation pass
asynchronously; update try_evaluate_simple_expr_sync or add a new
try_probe_simple_expr_sync helper and ensure the final produced value is wrapped
as Value::List(Rc::new(RefCell::new(...))) as before.

Comment thread src/interpreter/mod.rs
Comment on lines +5765 to +5778
Literal::List(elements) => {
let mut list_values = Vec::with_capacity(elements.len());
for element in elements {
// Recursively try to evaluate elements synchronously.
// This works for nested lists as well.
if let Some(value) = self.try_evaluate_simple_expr_sync(element, env)? {
list_values.push(value);
} else {
// Element requires async evaluation, abort sync optimization for the whole list
return Ok(None);
}
}
Ok(Some(Value::List(Rc::new(RefCell::new(list_values)))))
}

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.

⚠️ Potential issue | 🟠 Major

Avoid double-evaluation when list sync path falls back to async.

If an element later in the list requires async evaluation, the fast-path returns Ok(None) after already evaluating earlier elements. The async fallback then re-evaluates the entire list, which can duplicate side effects (e.g., zero-arg native functions invoked via auto-call). Consider a two-phase approach (pre-scan for async-needed elements without executing), or preserve the already-evaluated prefix and continue asynchronously for the remainder instead of re-running from scratch.

🤖 Prompt for AI Agents
In `@src/interpreter/mod.rs` around lines 5765 - 5778, The fast sync path for
Literal::List uses try_evaluate_simple_expr_sync to evaluate elements and
returns Ok(None if any element needs async), which causes already-evaluated
earlier elements to be re-run in the async path and can double-execute side
effects; modify the logic in the Literal::List handling so you first pre-scan
elements (calling a lightweight check like try_evaluate_simple_expr_sync in
"probe" mode or adding a new helper) to detect whether any element requires
async without executing side-effecting evaluations, and if a mix is detected
either (1) preserve the already-evaluated prefix (store values produced so far)
and continue evaluation asynchronously for the remaining elements, or (2)
perform a pure pre-scan pass that only determines async-necessity and then run a
single evaluation pass asynchronously; update try_evaluate_simple_expr_sync or
add a new try_probe_simple_expr_sync helper and ensure the final produced value
is wrapped as Value::List(Rc::new(RefCell::new(...))) as before.

@logbie
logbie merged commit 300806d into main Feb 11, 2026
19 checks passed
@logbie
logbie deleted the bolt/optimize-list-evaluation-11608627115757947855 branch February 11, 2026 10:09
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