Skip to content

[Repo Assist] Perf: eliminate seq/ref overhead in Stats moving and expanding window helpers#683

Merged
dsyme merged 4 commits intomasterfrom
repo-assist/perf-stats-avoidseq-20260321-977c0ec381d85cc5
Mar 22, 2026
Merged

[Repo Assist] Perf: eliminate seq/ref overhead in Stats moving and expanding window helpers#683
dsyme merged 4 commits intomasterfrom
repo-assist/perf-stats-avoidseq-20260321-977c0ec381d85cc5

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

🤖 This PR was created by Repo Assist, an automated AI assistant.

Summary

Two internal helpers in StatsInternal (movingWindowFn and expandingWindowFn) had unnecessary overhead from F# sequence expressions and ref cells. This PR replaces them with direct imperative loops that build into a ResizeArray(float) and return float[] directly.

Changes

movingWindowFn (moving-window statistics: movingMean, movingStdDev, movingVar, etc.)

Before: Used seq { ... yield ... } with four heap-allocated ref cells (r, i, isInit, state). Each caller also called >> Array.ofSeq to materialise the lazy sequence.

After: Uses a plain while loop with mutable stack-level locals, accumulating directly into a ResizeArray(float) and returning .ToArray(). No ref allocations; no seq state machine; no Array.ofSeq at the call site.

expandingWindowFn (expanding-window statistics: expandingMean, expandingStdDev, etc.)

Before: Seq.scan fupdate initState |> Seq.skip 1 |> Seq.map ftransf — three chained lazy-sequence state machines, plus >> Array.ofSeq at the caller.

After: A single for x in source do loop with a mutable state, accumulating into ResizeArray(float). No lazy sequences.

Dead code removal

expandingMinMaxHelper was defined internally but never called anywhere — the actual expandingMin/expandingMax implementations are self-contained. It has been removed.

Why this matters

These helpers are in the hot path of every Stats.moving* and Stats.expanding* call. For a series with N elements and window size W:

  • The old movingWindowFn created ≥ 4 heap objects per call plus one seq state machine and one IEnumerator wrapper; the new version has zero extra heap objects (the ResizeArray is the only allocation, as before with Array.ofSeq).
  • The old expandingWindowFn created 3 chained lazy-sequence state machines; the new version has none.

The change is particularly valuable for long series or when multiple moving statistics are composed in a pipeline.

Test status

Passed!  - Failed: 0, Passed: 685, Skipped: 0, Total: 685

All existing tests pass without modification. The semantics are unchanged — the output arrays are identical to the previous implementation.

Generated by Repo Assist ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@30f2254f2a7a944da1224df45d181a3f8faefd0d

Replace seq-expression + ref-cell implementation of movingWindowFn with a
direct imperative loop that builds into a ResizeArray<float> and returns
float[]. Likewise convert expandingWindowFn from the Seq.scan |> Seq.skip 1
|> Seq.map chain to an equivalent imperative loop.

Benefits:
- movingWindowFn: eliminates the IEnumerator seq-expression state machine,
  4 heap-allocated ref cells (r, i, isInit, state), and the Array.ofSeq
  materialisation call at each use site.
- expandingWindowFn: eliminates 3 chained lazy-sequence state machines
  (Scan, Skip 1, Map) and the Array.ofSeq call at each use site.
- Remove unused internal function expandingMinMaxHelper (dead code; actual
  expanding-min/max implementations are inline in Stats directly).

All 685 existing tests pass without modification.

Co-authored-by: Copilot <[email protected]>
@dsyme dsyme marked this pull request as ready for review March 22, 2026 21:57
@dsyme dsyme merged commit 8d01dd5 into master Mar 22, 2026
2 checks passed
@dsyme dsyme deleted the repo-assist/perf-stats-avoidseq-20260321-977c0ec381d85cc5 branch March 22, 2026 21:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant