-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Iterator<T>.TryGetFirst in Enumerable.Any() #99218
Conversation
Enumerable.Any() currently uses TryGetNonEnumeratedCount, comparing the result to 0, and TryGetNonEnumeratedCount uses `Iterator<T>.GetCount(onlyIfCheap: true)`. But this leaves out iterators for which it's not cheap to compute the count; for these, however, we can now benefit from every `Iterator<T>` providing a `TryGetFirst`; its `found` result can be used as the result of `Any`. This PR inlines TryGetNonEnumeratedCount into Any and then updates its use of `Iterator<T>` to first use GetCount and then fall back to using TryGetFirst if GetCount is unsuccessful.
Tagging subscribers to this area: @dotnet/area-system-linq Issue DetailsEnumerable.Any() currently uses TryGetNonEnumeratedCount, comparing the result to 0, and TryGetNonEnumeratedCount uses
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Tests).Assembly).Run(args);
[MemoryDiagnoser(false)]
[HideColumns("Job", "Error", "StdDev", "Median", "RatioSD")]
public partial class Tests
{
private IEnumerable<int> _data;
[Params(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)]
public int Source { get; set; }
[GlobalSetup]
public void Setup()
{
IEnumerable<int> data = Source >= 10 ? Iterations(100) : Enumerable.Range(0, 100).ToArray();
_data = (Source % 10) switch
{
0 => data,
1 => data.Select(i => i),
2 => data.Where(i => i % 2 == 0).Select(i => i),
3 => data.Order(),
4 => data.OrderBy(i => i).Select(i => i),
5 => data.Skip(20).Take(10),
6 => data.Reverse(),
7 => data.SelectMany(i => new int[] { i }),
8 => data.Distinct(),
9 => data.Concat(data),
_ => throw new NotSupportedException()
};
}
[Benchmark]
public bool Any() => _data.Any();
private static IEnumerable<int> Iterations(int count)
{
for (int i = 0; i < count; i++) yield return i;
}
}
|
Enumerable.Any() currently uses TryGetNonEnumeratedCount, comparing the result to 0, and TryGetNonEnumeratedCount uses
Iterator<T>.GetCount(onlyIfCheap: true)
. But this leaves out iterators for which it's not cheap to compute the count; for these, however, we can now benefit from everyIterator<T>
providing aTryGetFirst
; itsfound
result can be used as the result ofAny
. This PR inlines TryGetNonEnumeratedCount into Any and then updates its use ofIterator<T>
to first use GetCount and then fall back to using TryGetFirst if GetCount is unsuccessful.