From 184b31e8ecb1dd04d9125e34a20fd708c0378c32 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Sat, 18 Apr 2026 20:40:45 +0800 Subject: [PATCH] perf: extend comprehension scope reuse to builtins, select, lookup Motivation: The mutable scope reuse path in comprehension evaluation avoids O(n) ValScope allocations (Arrays.copyOf per iteration), but was gated by isNonCapturingBody which only covered ValidId, BinaryOp, UnaryOp, And, Or, IfElse, and Literal. Common patterns like [std.length(x) for x in arr] or [x.field for x in arr] fell through to the per-iteration scope allocation path. Modification: Extend isNonCapturingBody to cover ApplyBuiltin0-4, Select, and Lookup with recursive argument checks. These produce eager values via visitExpr and don't create closures that capture the comprehension scope. The same expression types are already handled by isInvariantExpr. Result: More comprehension patterns benefit from mutable scope reuse, eliminating per-iteration ValScope allocation + Arrays.copyOf overhead. --- sjsonnet/src/sjsonnet/Evaluator.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sjsonnet/src/sjsonnet/Evaluator.scala b/sjsonnet/src/sjsonnet/Evaluator.scala index 24d60fac..0bc12f3f 100644 --- a/sjsonnet/src/sjsonnet/Evaluator.scala +++ b/sjsonnet/src/sjsonnet/Evaluator.scala @@ -460,6 +460,24 @@ class Evaluator( isNonCapturingBody(ie.cond) && isNonCapturingBody(ie.`then`) && isNonCapturingBody( ie.`else` ) + case ExprTags.ApplyBuiltin0 => true + case ExprTags.ApplyBuiltin1 => + isNonCapturingBody(e.asInstanceOf[ApplyBuiltin1].a1) + case ExprTags.ApplyBuiltin2 => + val ab = e.asInstanceOf[ApplyBuiltin2] + isNonCapturingBody(ab.a1) && isNonCapturingBody(ab.a2) + case ExprTags.ApplyBuiltin3 => + val ab = e.asInstanceOf[ApplyBuiltin3] + isNonCapturingBody(ab.a1) && isNonCapturingBody(ab.a2) && isNonCapturingBody(ab.a3) + case ExprTags.ApplyBuiltin4 => + val ab = e.asInstanceOf[ApplyBuiltin4] + isNonCapturingBody(ab.a1) && isNonCapturingBody(ab.a2) && + isNonCapturingBody(ab.a3) && isNonCapturingBody(ab.a4) + case ExprTags.Select => + isNonCapturingBody(e.asInstanceOf[Select].value) + case ExprTags.Lookup => + val l = e.asInstanceOf[Lookup] + isNonCapturingBody(l.value) && isNonCapturingBody(l.index) case _ => e.isInstanceOf[Val.Literal] }