From e2b61c7e686e7987a6d99d10d8231c3b5eb4c065 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 19 Aug 2025 08:57:35 -0700
Subject: [PATCH 001/179] [release/10.0-rc1] Introduce size-optimized
IListSelect iterator (#118829)
This lets us keep some of the constant-time indexing advantages of the
IList iterator, without the GVM overhead of Select. There is a small
size increase here, but nowhere near the cost of the GVM.
In a pathological generated example for GVMs the cost was:
1. .NET 9: 12 MB
2. .NET 10 w/out this change: 2.2 MB
3. .NET 10 w/ this change: 2.3 MB
In a real-world example (AzureMCP), the size attributed to System.Linq
was:
1. .NET 9: 1.2 MB
2. .NET 10 w/out this change: 340 KB
3. .NET 10 w/ this change: 430 KB
This seems like a good tradeoff. We mostly keep the algorithmic
complexity the same across the size/speed-opt versions, and just
tradeoff on the margins. We could probably continue to improve this in
the future.
---
.../System.Linq/src/System.Linq.csproj | 4 +-
.../System.Linq/src/System/Linq/Count.cs | 4 +-
.../System.Linq/src/System/Linq/ElementAt.cs | 4 +-
.../System.Linq/src/System/Linq/Last.cs | 2 +-
.../src/System/Linq/OfType.SpeedOpt.cs | 8 +-
.../src/System/Linq/Select.SizeOpt.cs | 100 +++++++++++++++
.../System.Linq/src/System/Linq/Select.cs | 9 +-
.../src/System/Linq/Skip.SizeOpt.cs | 20 ---
.../System.Linq/src/System/Linq/Skip.cs | 4 +-
.../src/System/Linq/Take.SizeOpt.cs | 47 --------
.../System.Linq/src/System/Linq/Take.cs | 8 +-
.../src/System/Linq/Where.SizeOpt.cs | 114 ++++++++++++++++++
.../System.Linq/src/System/Linq/Where.cs | 8 +-
src/libraries/System.Linq/tests/CountTests.cs | 15 ++-
.../System.Linq/tests/OrderedSubsetting.cs | 2 +-
src/libraries/System.Linq/tests/RangeTests.cs | 2 +-
.../System.Linq/tests/SelectTests.cs | 26 ++++
src/libraries/System.Linq/tests/TakeTests.cs | 4 +-
src/libraries/tests.proj | 1 +
19 files changed, 285 insertions(+), 97 deletions(-)
create mode 100644 src/libraries/System.Linq/src/System/Linq/Select.SizeOpt.cs
delete mode 100644 src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs
delete mode 100644 src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs
create mode 100644 src/libraries/System.Linq/src/System/Linq/Where.SizeOpt.cs
diff --git a/src/libraries/System.Linq/src/System.Linq.csproj b/src/libraries/System.Linq/src/System.Linq.csproj
index 2f93e584d18658..f032db1a303ee1 100644
--- a/src/libraries/System.Linq/src/System.Linq.csproj
+++ b/src/libraries/System.Linq/src/System.Linq.csproj
@@ -62,6 +62,7 @@
+
@@ -70,12 +71,10 @@
-
-
@@ -83,6 +82,7 @@
+
diff --git a/src/libraries/System.Linq/src/System/Linq/Count.cs b/src/libraries/System.Linq/src/System/Linq/Count.cs
index 6a12a11cbe163d..cd175cf6b9b3cc 100644
--- a/src/libraries/System.Linq/src/System/Linq/Count.cs
+++ b/src/libraries/System.Linq/src/System/Linq/Count.cs
@@ -20,7 +20,7 @@ public static int Count(this IEnumerable source)
return collectionoft.Count;
}
- if (!IsSizeOptimized && source is Iterator iterator)
+ if (source is Iterator iterator)
{
return iterator.GetCount(onlyIfCheap: false);
}
@@ -113,7 +113,7 @@ public static bool TryGetNonEnumeratedCount(this IEnumerable s
return true;
}
- if (!IsSizeOptimized && source is Iterator iterator)
+ if (source is Iterator iterator)
{
int c = iterator.GetCount(onlyIfCheap: true);
if (c >= 0)
diff --git a/src/libraries/System.Linq/src/System/Linq/ElementAt.cs b/src/libraries/System.Linq/src/System/Linq/ElementAt.cs
index 26c69366fa9f3b..f4dc1f23a16c80 100644
--- a/src/libraries/System.Linq/src/System/Linq/ElementAt.cs
+++ b/src/libraries/System.Linq/src/System/Linq/ElementAt.cs
@@ -23,7 +23,7 @@ public static TSource ElementAt(this IEnumerable source, int i
bool found;
TSource? element =
- !IsSizeOptimized && source is Iterator iterator ? iterator.TryGetElementAt(index, out found) :
+ source is Iterator iterator ? iterator.TryGetElementAt(index, out found) :
TryGetElementAtNonIterator(source, index, out found);
if (!found)
@@ -121,7 +121,7 @@ public static TSource ElementAt(this IEnumerable source, Index
}
return
- !IsSizeOptimized && source is Iterator iterator ? iterator.TryGetElementAt(index, out found) :
+ source is Iterator iterator ? iterator.TryGetElementAt(index, out found) :
TryGetElementAtNonIterator(source, index, out found);
}
diff --git a/src/libraries/System.Linq/src/System/Linq/Last.cs b/src/libraries/System.Linq/src/System/Linq/Last.cs
index ca48475259d8e5..c38604e397592f 100644
--- a/src/libraries/System.Linq/src/System/Linq/Last.cs
+++ b/src/libraries/System.Linq/src/System/Linq/Last.cs
@@ -69,7 +69,7 @@ public static TSource LastOrDefault(this IEnumerable source, F
}
return
- !IsSizeOptimized && source is Iterator iterator ? iterator.TryGetLast(out found) :
+ source is Iterator iterator ? iterator.TryGetLast(out found) :
TryGetLastNonIterator(source, out found);
}
diff --git a/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs
index 4420801ee4abcf..f2649e706c8a0e 100644
--- a/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs
+++ b/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs
@@ -167,7 +167,7 @@ public override IEnumerable Select(Func s
// they're covariant. It's not worthwhile checking for List to use the ListWhereSelectIterator
// because List<> is not covariant.
Func