Feat: Enable nested destructuring optimization
This commit introduces optimizations for nested destructuring, allowing tuples and records to be flattened and matched directly against function arguments. This significantly improves performance by enabling more constant folding and reducing intermediate allocations. The changes include: - Modifying the `Binder` to correctly count nested parameters. - Enhancing `flatten_tuple` in the `Optimizer` to handle records and NOP nodes. - Updating `map_params_to_args` to recursively destructure nested compound arguments. - Adding integration tests to verify the correctness of tuple-to-tuple and record-to-tuple destructuring optimizations.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
;; Benchmark: 2.1us
|
;; Benchmark: 969ns
|
||||||
;; Benchmark-Repeat: 961
|
;; Benchmark-Repeat: 2078
|
||||||
;; Comprehensive Destructuring Test
|
;; Comprehensive Destructuring Test
|
||||||
;; Covers: Nested tuples, mixed params, dynamic passing
|
;; Covers: Nested tuples, mixed params, dynamic passing
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
;; This test calls a function that destructures a record
|
;; This test calls a function that destructures a record
|
||||||
;; Current implementation is now zero-allocation for destructuring.
|
;; Current implementation is now zero-allocation for destructuring.
|
||||||
;; Output: 3
|
;; Output: 3
|
||||||
;; Benchmark: 698ns
|
;; Benchmark: 129ns
|
||||||
;; Benchmark-Repeat: 2875
|
;; Benchmark-Repeat: 15432
|
||||||
|
|
||||||
(do
|
(do
|
||||||
(def process (fn [[x y]]
|
(def process (fn [[x y]]
|
||||||
|
|||||||
+14
-15
@@ -247,24 +247,23 @@ impl Binder {
|
|||||||
|
|
||||||
let compiled_fn = self.functions.pop().unwrap();
|
let compiled_fn = self.functions.pop().unwrap();
|
||||||
|
|
||||||
// 3. Static optimization: check if parameters are purely positional
|
// 3. Static optimization: count total parameters needed in flat argument list
|
||||||
let positional_count = match ¶ms_bound.kind {
|
fn count_params(node: &BoundNode) -> Option<u32> {
|
||||||
BoundKind::Tuple { elements } => {
|
match &node.kind {
|
||||||
let mut count = 0;
|
BoundKind::Parameter { .. } => Some(1),
|
||||||
let mut all_params = true;
|
BoundKind::Tuple { elements } => {
|
||||||
for e in elements {
|
let mut total = 0;
|
||||||
if matches!(e.kind, BoundKind::Parameter { .. }) {
|
for e in elements {
|
||||||
count += 1;
|
total += count_params(e)?;
|
||||||
} else {
|
|
||||||
all_params = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
Some(total)
|
||||||
}
|
}
|
||||||
if all_params { Some(count) } else { None }
|
BoundKind::Nop => Some(0),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
BoundKind::Parameter { .. } => Some(1),
|
}
|
||||||
_ => None,
|
|
||||||
};
|
let positional_count = count_params(¶ms_bound);
|
||||||
|
|
||||||
Ok(self.make_node(
|
Ok(self.make_node(
|
||||||
identity,
|
identity,
|
||||||
|
|||||||
@@ -695,10 +695,15 @@ impl Optimizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn flatten_tuple(&self, node: AnalyzedNode, into: &mut Vec<AnalyzedNode>) {
|
fn flatten_tuple(&self, node: AnalyzedNode, into: &mut Vec<AnalyzedNode>) {
|
||||||
if let BoundKind::Tuple { elements } = &node.kind {
|
match node.kind {
|
||||||
for el in elements { self.flatten_tuple(el.clone(), into); }
|
BoundKind::Tuple { elements } => {
|
||||||
} else if !matches!(node.kind, BoundKind::Nop) {
|
for el in elements { self.flatten_tuple(el, into); }
|
||||||
into.push(node);
|
}
|
||||||
|
BoundKind::Record { fields } => {
|
||||||
|
for (_, v) in fields { self.flatten_tuple(v, into); }
|
||||||
|
}
|
||||||
|
BoundKind::Nop => {}
|
||||||
|
_ => into.push(node),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,6 +730,30 @@ impl Optimizer {
|
|||||||
*offset += 1;
|
*offset += 1;
|
||||||
}
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
|
// RECURSIVE DESTRUCTURING SUPPORT
|
||||||
|
// Check if the current argument at 'offset' is itself a Tuple or Record literal.
|
||||||
|
if let Some(arg) = args.get(*offset) {
|
||||||
|
let mut sub_args = Vec::new();
|
||||||
|
let is_compound = match &arg.kind {
|
||||||
|
BoundKind::Tuple { .. } | BoundKind::Record { .. } => {
|
||||||
|
self.flatten_tuple(arg.clone(), &mut sub_args);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_compound {
|
||||||
|
// Match inner elements against the flattened compound argument
|
||||||
|
let mut sub_offset = 0;
|
||||||
|
for el in elements {
|
||||||
|
self.map_params_to_args(el, &sub_args, &mut sub_offset, sub);
|
||||||
|
}
|
||||||
|
*offset += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Continue flat matching (original behavior)
|
||||||
for el in elements { self.map_params_to_args(el, args, offset, sub); }
|
for el in elements { self.map_params_to_args(el, args, offset, sub); }
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|||||||
@@ -278,4 +278,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
assert_eq!(format!("{}", result.unwrap()), "60");
|
assert_eq!(format!("{}", result.unwrap()), "60");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nested_destructuring_optimization() {
|
||||||
|
let env = Environment::new();
|
||||||
|
|
||||||
|
// 1. Tuple-to-Tuple
|
||||||
|
let source_tuple = "((fn [[x y]] (+ x y)) [10 20])";
|
||||||
|
assert_eq!(format!("{}", env.run_script(source_tuple).unwrap()), "30");
|
||||||
|
let dump_tuple = env.dump_ast(source_tuple).unwrap();
|
||||||
|
assert!(dump_tuple.contains("Constant: 30"), "Nested tuple should be folded to 30. Dump:\n{}", dump_tuple);
|
||||||
|
|
||||||
|
// 2. Record-to-Tuple
|
||||||
|
let source_record = "((fn [[x y]] (+ x y)) {:a 5 :b 7})";
|
||||||
|
assert_eq!(format!("{}", env.run_script(source_record).unwrap()), "12");
|
||||||
|
let dump_record = env.dump_ast(source_record).unwrap();
|
||||||
|
assert!(dump_record.contains("Constant: 12"), "Record-to-Tuple destructuring should be folded to 12. Dump:\n{}", dump_record);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user