From 80272aec9999b2d1c7f720ad80f91098c8af220f Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Thu, 20 Aug 2026 15:32:40 +0800 Subject: [PATCH] perf(builtin): count rev_foldi's index up instead of deriving it per iteration Completes PR #3784 (mizchi) on current main, where three of its four FixedArray reverse-iteration optimizations have already landed. The remaining delta is rev_foldi, which computed the ascending index as len - i - 1 on every iteration inside the descending range loop. Instead of that PR's C-style three-variable loop, the index becomes a second loop state of the same readable range form: for i in self.length()>..0; index = 0, acc = init { continue index + 1, f(index, acc, self.unsafe_get(i)) } Measured identical to the C-style loop (11.19 vs 11.20 us native) and faster than main on all three backends (rev_foldi, n=100K, sum fold): native 14.88 -> 11.19us (1.33x), js 72.4 -> 59.9us (1.21x), wasm-gc 75.5 -> 65.6us (1.15x). Per-element times (~0.1ns) show the fold is fully inlined and auto-vectorized in this benchmark; the win is the per-lane subtract dependency of len - i - 1 vectorizing worse than a counting index. With an opaque, non-inlinable closure the difference shrinks accordingly. Also adopts #3784's test pinning the reversed index/element pairing, which a miscounted index would break. Codex CLI review (xhigh, first-round sign-off): (index, element) pairing verified as (0,len-1)..(len-1,0) against the documented reverse-range and simultaneous-update semantics (f receives the pre-increment index); the pairing-test trace recomputed to 30120210; raise-polymorphism and the nobreak return intact; benchmark ratios recomputed (1.33x/1.21x/1.15x) and microbenchmark-scoped. Co-Authored-By: Claude Fable 5 Signed-off-by: Codex CLI --- builtin/fixedarray.mbt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/builtin/fixedarray.mbt b/builtin/fixedarray.mbt index 23108f7a8..3fb64272e 100644 --- a/builtin/fixedarray.mbt +++ b/builtin/fixedarray.mbt @@ -774,9 +774,8 @@ pub fn[A, B] FixedArray::rev_foldi( init~ : B, f : (Int, B, A) -> B raise?, ) -> B raise? { - let len = self.length() - for i in len>..0; acc = init { - continue f(len - i - 1, acc, self.unsafe_get(i)) + for i in self.length()>..0; index = 0, acc = init { + continue index + 1, f(index, acc, self.unsafe_get(i)) } nobreak { acc } @@ -797,6 +796,16 @@ test "rev_foldi" { inspect(sum, content="25") } +///| +test "rev_foldi preserves reversed index element pairs" { + let trace = ([10, 20, 30] : FixedArray[_]).rev_foldi(init=0, ( + index, + acc, + elem, + ) => acc * 1000 + index * 100 + elem) + inspect(trace, content="30120210") +} + ///| /// Reverses the array in place by swapping elements from both ends until /// reaching the middle.