Share the empty-array singleton for zero-length Array results - #20388
Draft
T-Gro wants to merge 4 commits into
Draft
Share the empty-array singleton for zero-length Array results#20388T-Gro wants to merge 4 commits into
T-Gro wants to merge 4 commits into
Conversation
Add a central Basics.Array.zeroCreateUncheckedOrEmpty primitive that returns the shared System.Array.Empty<_>() singleton (via the [||] literal) when count = 0, and route the reachable-zero allocation sites through it: subUnchecked (all slicing), concatArrays (concat + collect output), collect/map/mapi/indexed and their Array.Parallel counterparts. Existing 'if length = 0' guards now return [||] too. This removes the redundant zero-length array allocations reported in Array.collect (intermediate 'U[][] plus concat output) and Array.map with empty inputs, without adding per-function length checks (the single check lives in the primitive; fill loops naturally no-op at length 0). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Route the remaining reachable-zero allocation sites through zeroCreateUncheckedOrEmpty: append, distinct, map2/mapi2/map3, partition, partitionWith (+ scatterPartitioned), zip/zip3, allPairs, unzip/unzip3, rev, removeAt/removeManyAt, transpose, and the Array.Parallel choose/mapi/zip/filter/ partition/partitionWith/groupBy builders. Count-based constructors (zeroCreate/ create/init/replicate/random*), the sort helpers and provably-nonzero sites keep the branch-free zeroCreateUnchecked. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
T-Gro
marked this pull request as draft
August 27, 2026 22:00
Invert the primitive: zeroCreateUnchecked now shares the empty-array singleton when count = 0 (the improve-by-default behavior), and a new zeroCreateUncheckedNonEmpty keeps the branch-free newarr for the ~18 sites where the length is provably > 0 (countBy/groupBy/choose chunks, filter, windowed, chunkBySize, insert/update, reduceBy, scanSubRight, splitInto, ...). This shrinks the diff and makes every reachable-zero site - including the count-based constructors (zeroCreate/create/init/replicate) - share the singleton for free, instead of each caller opting in. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
T-Gro
force-pushed
the
t-gro-array-collect-empty-perf
branch
from
August 27, 2026 23:16
5ce67ee to
a0cb3f5
Compare
Contributor
|
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.