From 126b07e3803d1a268621c317d4c73a3b2aa9ba0e Mon Sep 17 00:00:00 2001 From: He-Pin Date: Sat, 18 Apr 2026 20:30:47 +0800 Subject: [PATCH] perf: single-pass std.sum/std.avg without intermediate array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: std.sum and std.avg perform three passes over the input array: forall (validate) → map (allocate intermediate Array[Double]) → sum. Modification: Replace with a single while-loop that validates types and accumulates in one pass. No intermediate array allocation. Result: Eliminates O(n) Array[Double] allocation and two redundant iterations for every std.sum/std.avg call. --- .../src/sjsonnet/stdlib/ArrayModule.scala | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala b/sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala index 2bf067f5..51780b1b 100644 --- a/sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala +++ b/sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala @@ -736,19 +736,33 @@ object ArrayModule extends AbstractFunctionModule { ) }, builtin("sum", "arr") { (_, _, arr: Val.Arr) => - if (!arr.forall(_.isInstanceOf[Val.Num])) { - Error.fail("Argument must be an array of numbers") + val a = arr.asLazyArray + var sum = 0.0 + var i = 0 + while (i < a.length) { + a(i).value match { + case n: Val.Num => sum += n.asDouble + case x => Error.fail("std.sum expected number, got " + x.prettyName) + } + i += 1 } - arr.asLazyArray.map(_.value.asDouble).sum + sum }, builtin("avg", "arr") { (_, _, arr: Val.Arr) => - if (!arr.forall(_.isInstanceOf[Val.Num])) { - Error.fail("Argument must be an array of numbers") - } - if (arr.length == 0) { + val a = arr.asLazyArray + if (a.length == 0) { Error.fail("Cannot calculate average of an empty array") } - arr.asLazyArray.map(_.value.asDouble).sum / arr.length + var sum = 0.0 + var i = 0 + while (i < a.length) { + a(i).value match { + case n: Val.Num => sum += n.asDouble + case x => Error.fail("std.avg expected number, got " + x.prettyName) + } + i += 1 + } + sum / a.length } ) }