mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Rvm optimizations (#620)
* perf(rvm): fix O(n²) comprehension yield by mutating in-place Instead of cloning the entire accumulator collection on every yield iteration, use take_register + Rc::make_mut to get exclusive ownership and mutate in-place. This reduces comprehension yield from O(n²) to O(n) for both run-to-completion and suspendable execution modes. - Add RegoVM::take_register() helper that swaps register with Undefined - Comprehension yield now takes the accumulator, mutates via Rc::make_mut, and writes back — avoiding deep clones when refcount == 1 Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> * perf(rvm): use take_register for ObjectSet, ArrayPush, SetAdd These instructions were cloning the container register (bumping Rc to 2), then calling as_object_mut/as_array_mut/as_set_mut which invokes Rc::make_mut — deep-cloning the entire collection since refcount > 1. Use take_register instead so the Rc refcount stays at 1, making Rc::make_mut a no-op and allowing in-place mutation. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> * perf(rvm): remove unnecessary clones in rule caching - execute_call_rule_common: move final_value into cache instead of cloning, since it is not used afterwards - finalize_rule_frame_data: add comment clarifying the clone is needed because the value is both cached and returned - Remove unnecessary .clone() on result_from_rule when setting register Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> * rvm: avoid RuleInfo clone per rule call Replace RuleInfo.clone() (which heap-allocates name, destructuring_blocks, and potentially function_info) with a cheap Arc<Program> clone (atomic refcount bump) followed by borrowing &RuleInfo from the local Arc. This eliminates per-rule-call heap allocations. Sites changed: - execute_call_rule_common: Arc clone + borrow - execute_call_rule_suspendable: Arc clone + borrow - finalize_rule_frame_data: Arc clone + borrow - handle_rule_break_event: inline Arc clone + borrow (was get_rule_info) - handle_rule_error_event: inline Arc clone + borrow (was get_rule_info) - Removed now-unused get_rule_info method Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> * rvm: replace bincode with postcard for serialization Remove unlinked bincode dependency. Use postcard (already a dep for rvm feature) for all binary serialization/deserialization in program serialization and tests. Also adds rvm_benchmark benchmark. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> * perf(rvm): cache dummy Span/Expr for builtin calls Every builtin call was allocating a Source (via from_contents), a Span, and N Ref<Expr> wrappers just to satisfy the builtin function signature. These dummy values are only used for error reporting context. Cache the dummy Span and Vec<Ref<Expr>> on the RegoVM struct. The Source and Span are created once on first builtin call; dummy Expr entries grow as needed and are reused across calls via mem::take/put-back pattern. This eliminates per-builtin-call heap allocations for Source (Rc + String + Vec<lines>), Span clones, and Rc<Expr> wrappers. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> * perf(rvm): round 2 allocation reduction in builtins, entry points, virtual data - Cache builtin args Vec on RegoVM (mem::take/clear/put-back pattern) - Restructure builtins_cache as two-level map for clone-free lookup - Use IndexMap::get_index() in execute_entry_point_by_index - Use mutable Vec path stack in traverse_rule_tree_subobject (push/pop) - Walk data tree and rule-result paths by reference, clone only leaf - Use mem::replace in resume() instead of cloning ExecutionState Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com> * fix(rvm): address PR review feedback - Restore cached_builtin_args on all error/early-return paths in execute_builtin_call to preserve allocation reuse - Use 1-based line/col and \"<builtin>\" filename in dummy span for clearer diagnostics - Restore result register before returning errors in comprehension mode-mismatch branches (both run-to-completion and suspendable) - Avoid clone in resume() invalid-state error path by formatting debug string before moving state back --------- Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
ee3dff9a3d
commit
50c0215fdb
+14
-25
@@ -4,7 +4,6 @@ use crate::rvm::instructions::Instruction;
|
||||
use crate::rvm::program::Program;
|
||||
use crate::value::Value;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::convert::TryFrom as _;
|
||||
|
||||
use super::dispatch::InstructionOutcome;
|
||||
@@ -24,35 +23,22 @@ impl RegoVM {
|
||||
}
|
||||
|
||||
pub fn execute_entry_point_by_index(&mut self, index: usize) -> Result<Value> {
|
||||
let entry_points: Vec<(String, usize)> = self
|
||||
.program
|
||||
.entry_points
|
||||
.iter()
|
||||
.map(|(name, pc)| (name.clone(), *pc))
|
||||
.collect();
|
||||
|
||||
if index >= entry_points.len() {
|
||||
return Err(VmError::InvalidEntryPointIndex {
|
||||
index,
|
||||
max_index: entry_points.len().saturating_sub(1),
|
||||
pc: self.pc,
|
||||
});
|
||||
}
|
||||
|
||||
let &(ref entry_point_name, entry_point_pc) =
|
||||
entry_points
|
||||
.get(index)
|
||||
.ok_or(VmError::InvalidEntryPointIndex {
|
||||
let (entry_point_name, entry_point_pc) = {
|
||||
let (name, &pc) = self.program.entry_points.get_index(index).ok_or(
|
||||
VmError::InvalidEntryPointIndex {
|
||||
index,
|
||||
max_index: entry_points.len().saturating_sub(1),
|
||||
max_index: self.program.entry_points.len().saturating_sub(1),
|
||||
pc: self.pc,
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
(name.clone(), pc)
|
||||
};
|
||||
|
||||
if entry_point_pc >= self.program.instructions.len() {
|
||||
return Err(VmError::EntryPointPcOutOfBounds {
|
||||
pc: entry_point_pc,
|
||||
instruction_count: self.program.instructions.len(),
|
||||
entry_point: entry_point_name.clone(),
|
||||
entry_point: entry_point_name,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -215,15 +201,18 @@ impl RegoVM {
|
||||
}
|
||||
|
||||
pub fn resume(&mut self, resume_value: Option<Value>) -> Result<Value> {
|
||||
let (reason, mut last_result) = match self.execution_state.clone() {
|
||||
let old_state = core::mem::replace(&mut self.execution_state, ExecutionState::Running);
|
||||
let (reason, mut last_result) = match old_state {
|
||||
ExecutionState::Suspended {
|
||||
reason,
|
||||
last_result,
|
||||
..
|
||||
} => (reason, last_result),
|
||||
current_state => {
|
||||
let desc = alloc::format!("{:?}", current_state);
|
||||
self.execution_state = current_state;
|
||||
return Err(VmError::InvalidResumeState {
|
||||
state: alloc::format!("{:?}", current_state),
|
||||
state: desc,
|
||||
pc: self.pc,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user