Skip to content

Commit 9c6debb

Browse files
authored
perf: single-pass std.sum/std.avg without intermediate array (#782)
## Summary - Replace 3-pass implementation (`forall` validate → `.map(_.value.asDouble)` allocate intermediate `Array[Double]` → `.sum` accumulate) with a single while-loop that validates + accumulates in one pass - Eliminates intermediate `Array[Double]` allocation entirely - Same pattern applied to both `std.sum` and `std.avg` ## Changes - `ArrayModule.scala`: Rewrite `std.sum` and `std.avg` to use single-pass while-loop with pattern matching for validation + accumulation ## Test plan - [x] `./mill 'sjsonnet.jvm[3.3.7]'.test` — all tests pass - [x] `./mill 'sjsonnet.jvm[3.3.7]'.compile` — compiles clean - [x] Error messages preserved: `std.sum expected number` / `Cannot calculate average of an empty array`
1 parent 4123ac3 commit 9c6debb

1 file changed

Lines changed: 22 additions & 8 deletions

File tree

sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -736,19 +736,33 @@ object ArrayModule extends AbstractFunctionModule {
736736
)
737737
},
738738
builtin("sum", "arr") { (_, _, arr: Val.Arr) =>
739-
if (!arr.forall(_.isInstanceOf[Val.Num])) {
740-
Error.fail("Argument must be an array of numbers")
739+
val a = arr.asLazyArray
740+
var sum = 0.0
741+
var i = 0
742+
while (i < a.length) {
743+
a(i).value match {
744+
case n: Val.Num => sum += n.asDouble
745+
case x => Error.fail("std.sum expected number, got " + x.prettyName)
746+
}
747+
i += 1
741748
}
742-
arr.asLazyArray.map(_.value.asDouble).sum
749+
sum
743750
},
744751
builtin("avg", "arr") { (_, _, arr: Val.Arr) =>
745-
if (!arr.forall(_.isInstanceOf[Val.Num])) {
746-
Error.fail("Argument must be an array of numbers")
747-
}
748-
if (arr.length == 0) {
752+
val a = arr.asLazyArray
753+
if (a.length == 0) {
749754
Error.fail("Cannot calculate average of an empty array")
750755
}
751-
arr.asLazyArray.map(_.value.asDouble).sum / arr.length
756+
var sum = 0.0
757+
var i = 0
758+
while (i < a.length) {
759+
a(i).value match {
760+
case n: Val.Num => sum += n.asDouble
761+
case x => Error.fail("std.avg expected number, got " + x.prettyName)
762+
}
763+
i += 1
764+
}
765+
sum / a.length
752766
}
753767
)
754768
}

0 commit comments

Comments
 (0)