Repository navigation
Share the empty-array singleton for zero-length Array results - #20388
Conversation
|
5ce67ee to
a0cb3f5
Compare
|
Shouldn't the same optimization be done for empty list as well? |
|
I think there's a few other places that could possibly do the same. e.g. Seq.toArray appears to be already special casing an empty array in the general fallback path, but not in the |
677538e to
12c7027
Compare
9290046 to
3f5f8ce
Compare
Array.collect/Array.map and many sibling builders allocated a fresh zero-length array for empty results (Array.collect on empty input allocated two). Make the internal allocation primitives Basics.Array.zeroCreateUnchecked and Basics.List.arrayZeroCreate return the shared System.Array.Empty<_>() singleton (via the [||] literal) when count = 0, so empty results across Array, Array.Parallel and the toArray conversions (List.toArray, Array.ofList/ofSeq, Seq.toArray, Set/Map.toArray) allocate nothing. Array.copy and Seq.toArray no longer clone an empty source array, and Array.insertManyAt reuses Array.copy. The count = 0 check is a single well-predicted branch; provably-non-empty callers pay only that. Empty F# lists are already the interned FSharpList.Empty singleton, so there is no list-result change. The empty-guard inlines into FSharp.Core's methods, growing the trimmed FSharp.Core.dll by 512 bytes; trimming baselines updated accordingly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
3f5f8ce to
aadb137
Compare
T-Gro
left a comment
There was a problem hiding this comment.
🤖 🕵️ AI review — verify independently.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
All done. |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
@copilot resolve the merge conflicts in this pull request |
…mpty-perf # Conflicts: # tests/AheadOfTime/Trimming/check.ps1 Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
Head branch was pushed to by a user without write access
Merged |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…#20388) * Share the empty-array singleton for zero-length results (dotnet#20382) Array.collect/Array.map and many sibling builders allocated a fresh zero-length array for empty results (Array.collect on empty input allocated two). Make the internal allocation primitives Basics.Array.zeroCreateUnchecked and Basics.List.arrayZeroCreate return the shared System.Array.Empty<_>() singleton (via the [||] literal) when count = 0, so empty results across Array, Array.Parallel and the toArray conversions (List.toArray, Array.ofList/ofSeq, Seq.toArray, Set/Map.toArray) allocate nothing. Array.copy and Seq.toArray no longer clone an empty source array, and Array.insertManyAt reuses Array.copy. The count = 0 check is a single well-predicted branch; provably-non-empty callers pay only that. Empty F# lists are already the interned FSharpList.Empty singleton, so there is no list-result change. The empty-guard inlines into FSharp.Core's methods, growing the trimmed FSharp.Core.dll by 512 bytes; trimming baselines updated accordingly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Address review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify empty-array sharing in copy and insertManyAt docs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix CI failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: perf-bundle <perf@local> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <copilot@github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Fixes #20382
Array.collect/Array.mapand their kin allocated a fresh zero-length array for empty results —Array.collecton empty input allocated two (the intermediate'U[][]and the concat output). Now the internal allocation primitivezeroCreateUncheckedhands back the sharedSystem.Array.Empty<_>()singleton when the length is 0, so empty results acrossArrayandArray.Parallel— and evenzeroCreate/create/initat length 0 — allocate nothing.Empty-sharing is the default: the handful of sites where the length is provably
> 0opt out viazeroCreateUncheckedNonEmptyto skip the branch. Non-empty results are unaffected.