Skip to content

VM: minor call overhead reductions (close_upvalues guard, empty upvalues Vec) #109

@timfennis

Description

@timfennis

Two small wins in the VM dispatch loop

1. Guard close_upvalues with an is_empty() check

Every Return unconditionally calls close_upvalues(frame_pointer), which iterates self.open_upvalues. For functions that capture no variables (like fib), this list is always empty — the loop body never executes but the iteration still occurs on every return.

Fix:

if !self.open_upvalues.is_empty() {
    self.close_upvalues(frame_pointer);
}

2. Avoid empty Vec allocation for upvalues in non-closure frames

dispatch_call_with_memo constructs a ClosureFunction for compiled functions:

Function::Compiled(f) => ClosureFunction {
    prototype: f,
    upvalues: vec![],  // heap allocation even when empty
},

vec![] may allocate (depending on the allocator). For functions with no captured variables this allocation is immediately wasted.

Fix options:

  • Use Vec::new() instead of vec![]Vec::new() is guaranteed not to allocate until the first push.
  • Or introduce a CompiledFrame variant in CallFrame that doesn't have an upvalues field at all, avoiding the field entirely for non-closures.

Neither change is individually dramatic, but both are zero-risk and free given that fib makes ~636K calls.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions