Skip to content

perf: map Array.mapFinIdx/mapIdx in place#14224

Open
kim-em wants to merge 1 commit into
masterfrom
array-mapfinidx-inplace
Open

perf: map Array.mapFinIdx/mapIdx in place#14224
kim-em wants to merge 1 commit into
masterfrom
array-mapfinidx-inplace

Conversation

@kim-em

@kim-em kim-em commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

This PR gives Array.mapFinIdxM an @[implemented_by] unsafe implementation so that mapFinIdx, mapIdxM, and mapIdx map in place, like Array.map.

Array.map (via mapMUnsafe) nulls each slot before calling f, so on a uniquely-owned input every element arrives exclusive and f can mutate it without copying. mapFinIdxM had no such implementation — only its push-based reference body, which reads as[i] as a borrow, leaving each element shared with the still-live input array.

This is a silent performance footgun: reaching for mapFinIdx/mapIdx instead of map just to get the index turns in-place work into copying. For an Array (Array _), a column-style operation that bumps one entry of every row becomes O(rows · cols) instead of O(rows) — each row is fully copied to change a single entry — and the gap widens without bound as the rows get wider (measured 6.5× → 17× → 21× as row width goes 32 → 256 → 2048).

The fix mirrors mapMUnsafe exactly, threading i.toNat and supplying the in-bounds proof via lcProof. The reference body of mapFinIdxM is unchanged, so its lemmas still hold; @[inline] is dropped (and @[expose] kept) because inlining the reference body would bypass @[implemented_by], exactly as mapM is not @[inline].

tests/compile/arrayMapIdxInPlace.lean is a deterministic regression test (no timing) using dbgTraceIfShared: before this change the mapFinIdx/mapIdx cases each printed one row shared trace per row; now they print none, matching Array.map.

Draft: validated against a faithful standalone reproduction on v4.32.0-rc1 (stock prints one trace per row, patched prints none, checksums match); the full-stage build and test suite are left to CI.

🤖 Prepared with Claude Code

`Array.mapFinIdxM` (and hence `mapFinIdx`, `mapIdxM`, `mapIdx`) ran only its
push-based reference implementation: it reads each element with `as[i]`, a
borrow that leaves the element shared with the still-live input array. Any
in-place work the mapping function attempts on an element therefore copies it,
so a column-style operation that touches one entry of each row of an
`Array (Array _)` is O(rows * cols) instead of O(rows), and the gap widens
without bound as the rows get wider.

Give `mapFinIdxM` an `@[implemented_by]` unsafe implementation mirroring
`mapMUnsafe`: it nulls each slot (`uset i default`) before invoking `f`, so on a
uniquely-owned input each element arrives exclusive (refcount 1) and may be
mutated in place. The reference body is unchanged, so every lemma about
`mapFinIdxM` still holds; the `@[inline]` is dropped (kept `@[expose]`) because
inlining the reference body at call sites would bypass `@[implemented_by]`, just
as `mapM` is not `@[inline]`.

tests/compile/arrayMapIdxInPlace.lean is a deterministic regression test using
`dbgTraceIfShared`: before this change the `mapFinIdx`/`mapIdx` cases each
printed one "row shared" trace per row; now they print none, matching
`Array.map`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kim-em kim-em marked this pull request as ready for review June 30, 2026 13:21
@github-actions github-actions Bot added the toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN label Jun 30, 2026
@mathlib-lean-pr-testing

Copy link
Copy Markdown

Mathlib CI status (docs):

  • ❗ Mathlib CI can not be attempted yet, as the nightly-testing-2026-06-30 tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto nightly-with-mathlib, Mathlib CI should run now. You can force Mathlib CI using the force-mathlib-ci label. (2026-06-30 13:29:02)

@leanprover-bot leanprover-bot added the builds-manual CI has verified that the Lean Language Reference builds against this PR label Jun 30, 2026
@leanprover-bot

leanprover-bot commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Reference manual CI status:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builds-manual CI has verified that the Lean Language Reference builds against this PR toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants