-
Notifications
You must be signed in to change notification settings - Fork 5
Optimize DefaultIfNotSingle loop #430
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
Changes from 8 commits
c3f56c5
2993c6c
79a49d1
4909c58
3de2741
128aebf
bf4a05b
9b2502f
ad58295
5f03f64
fc0ed86
1284a4d
f78c22f
7cccbda
972bf0c
0990b09
669f446
65ff0e0
7c6fa95
ae2f96a
994abca
7c97fea
14c44f8
68d8648
6df8c51
b433321
caecc7a
a8eb22b
ec8564d
b197af1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| using System; | ||
|
Check failure on line 1 in tests/Moq.Analyzers.Benchmarks/DefaultIfNotSingleBaseline.cs
|
||
| using System.Collections.Immutable; | ||
| using System.Linq; | ||
|
|
||
| namespace Moq.Analyzers.Benchmarks; | ||
|
|
||
| #pragma warning disable ECS0900 // Minimize boxing and unboxing | ||
| internal static class DefaultIfNotSingleBaseline | ||
|
Check warning on line 8 in tests/Moq.Analyzers.Benchmarks/DefaultIfNotSingleBaseline.cs
|
||
| { | ||
| public static T? DefaultIfNotSingleBaselineMethod<T>(this IEnumerable<T> source, Func<T, bool> predicate) | ||
| { | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ArgumentNullException.ThrowIfNull(source); | ||
| ArgumentNullException.ThrowIfNull(predicate); | ||
| bool found = false; | ||
| T? item = default; | ||
| foreach (T element in source.Where(predicate)) | ||
| { | ||
| if (found) | ||
| { | ||
| return default; | ||
| } | ||
|
|
||
| found = true; | ||
| item = element; | ||
| } | ||
|
|
||
| return item; | ||
| } | ||
|
|
||
| public static T? DefaultIfNotSingleBaselineMethod<T>(this IEnumerable<T> source) | ||
| => source.DefaultIfNotSingleBaselineMethod(static _ => true); | ||
|
|
||
| public static T? DefaultIfNotSingleBaselineMethod<T>(this ImmutableArray<T> source, Func<T, bool> predicate) | ||
| => source.AsEnumerable().DefaultIfNotSingleBaselineMethod(predicate); | ||
| #pragma warning restore ECS0900 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| using System.Linq; | ||
|
Check failure on line 1 in tests/Moq.Analyzers.Benchmarks/DefaultIfNotSingleEnumerableBenchmarks.cs
|
||
| using BenchmarkDotNet.Attributes; | ||
|
|
||
| namespace Moq.Analyzers.Benchmarks; | ||
|
|
||
| [MemoryDiagnoser] | ||
| [InProcess] | ||
| public class DefaultIfNotSingleEnumerableBenchmarks | ||
| { | ||
| private readonly IEnumerable<int> _source = Enumerable.Range(0, 100).ToArray(); | ||
|
|
||
| [Benchmark(Baseline = true)] | ||
| public int? Baseline() => _source.DefaultIfNotSingleBaselineMethod(x => x == 50); | ||
|
|
||
| [Benchmark] | ||
| public int? Optimized() => _source.DefaultIfNotSingleOptimizedMethod(x => x == 50); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| using System.Linq; | ||
|
Check failure on line 1 in tests/Moq.Analyzers.Benchmarks/DefaultIfNotSingleEnumerableNoPredicateBenchmarks.cs
|
||
| using BenchmarkDotNet.Attributes; | ||
|
|
||
| namespace Moq.Analyzers.Benchmarks; | ||
|
|
||
| [MemoryDiagnoser] | ||
| [InProcess] | ||
| public class DefaultIfNotSingleEnumerableNoPredicateBenchmarks | ||
| { | ||
| private readonly IEnumerable<int> _source = new[] { 0 }; | ||
|
|
||
| [Benchmark(Baseline = true)] | ||
| public int? Baseline() => _source.DefaultIfNotSingleBaselineMethod(); | ||
|
|
||
| [Benchmark] | ||
| public int? Optimized() => _source.DefaultIfNotSingleOptimizedMethod(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| using System.Collections.Immutable; | ||
|
Check failure on line 1 in tests/Moq.Analyzers.Benchmarks/DefaultIfNotSingleImmutableArrayBenchmarks.cs
|
||
| using System.Linq; | ||
| using BenchmarkDotNet.Attributes; | ||
|
|
||
| namespace Moq.Analyzers.Benchmarks; | ||
|
|
||
| [MemoryDiagnoser] | ||
| [InProcess] | ||
| public class DefaultIfNotSingleImmutableArrayBenchmarks | ||
| { | ||
| private readonly ImmutableArray<int> _source = ImmutableArray.CreateRange(Enumerable.Range(0, 100)); | ||
|
|
||
| #pragma warning disable ECS0900 // Minimize boxing and unboxing | ||
| [Benchmark(Baseline = true)] | ||
| public int? Baseline() => _source.DefaultIfNotSingleBaselineMethod(x => x == 50); | ||
|
|
||
| [Benchmark] | ||
| public int? Optimized() => _source.DefaultIfNotSingleOptimizedMethod(x => x == 50); | ||
| #pragma warning restore ECS0900 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| using System; | ||
|
Check failure on line 1 in tests/Moq.Analyzers.Benchmarks/DefaultIfNotSingleOptimized.cs
|
||
| using System.Collections.Immutable; | ||
| using System.Linq; | ||
|
|
||
| namespace Moq.Analyzers.Benchmarks; | ||
|
|
||
| #pragma warning disable ECS0900 // Minimize boxing and unboxing | ||
| internal static class DefaultIfNotSingleOptimized | ||
|
Check warning on line 8 in tests/Moq.Analyzers.Benchmarks/DefaultIfNotSingleOptimized.cs
|
||
| { | ||
| public static T? DefaultIfNotSingleOptimizedMethod<T>(this IEnumerable<T> source, Func<T, bool> predicate) | ||
| { | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ArgumentNullException.ThrowIfNull(source); | ||
| ArgumentNullException.ThrowIfNull(predicate); | ||
| bool found = false; | ||
| T? item = default; | ||
| foreach (T element in source) | ||
| { | ||
| if (!predicate(element)) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| if (found) | ||
| { | ||
| return default; | ||
| } | ||
|
|
||
| found = true; | ||
| item = element; | ||
| } | ||
|
|
||
| return item; | ||
| } | ||
|
|
||
| public static T? DefaultIfNotSingleOptimizedMethod<T>(this IEnumerable<T> source) | ||
| => source.DefaultIfNotSingleOptimizedMethod(static _ => true); | ||
|
|
||
| public static T? DefaultIfNotSingleOptimizedMethod<T>(this ImmutableArray<T> source, Func<T, bool> predicate) | ||
| => source.AsEnumerable().DefaultIfNotSingleOptimizedMethod(predicate); | ||
| #pragma warning restore ECS0900 | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,6 @@ | ||
| namespace Moq.Analyzers.Test.Common; | ||
| using System.Collections; | ||
|
Check failure on line 1 in tests/Moq.Analyzers.Test/Common/EnumerableExtensionsTests.cs
|
||
|
|
||
| namespace Moq.Analyzers.Test.Common; | ||
|
|
||
| public class EnumerableExtensionsTests | ||
| { | ||
|
|
@@ -73,4 +75,53 @@ | |
| int result = source.DefaultIfNotSingle(x => x > 5); | ||
| Assert.Equal(0, result); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void DefaultIfNotSingle_StopsEnumeratingAfterSecondMatch() | ||
| { | ||
| CountingEnumerable<int> source = new(new[] { 1, 2, 3, 4 }); | ||
| int result = source.DefaultIfNotSingle(x => x > 1); | ||
|
|
||
| Assert.Equal(0, result); | ||
|
rjmurillo marked this conversation as resolved.
|
||
| Assert.Equal(3, source.Count); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void DefaultIfNotSingle_ThrowsArgumentNullException_WhenSourceIsNull() | ||
| { | ||
| IEnumerable<int> source = null!; | ||
| Assert.Throws<ArgumentNullException>(() => source.DefaultIfNotSingle(x => true)); | ||
|
Check failure on line 93 in tests/Moq.Analyzers.Test/Common/EnumerableExtensionsTests.cs
|
||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| [Fact] | ||
| public void DefaultIfNotSingle_ThrowsArgumentNullException_WhenPredicateIsNull() | ||
| { | ||
| IEnumerable<int> source = new List<int> { 1, 2, 3 }; | ||
| Assert.Throws<ArgumentNullException>(() => source.DefaultIfNotSingle(null!)); | ||
|
Check failure on line 100 in tests/Moq.Analyzers.Test/Common/EnumerableExtensionsTests.cs
|
||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| [Fact] | ||
| public void DefaultIfNotSingle_ImmutableArray_ThrowsArgumentNullException_WhenPredicateIsNull() | ||
| { | ||
| var source = ImmutableArray.Create(1, 2, 3); | ||
| Assert.Throws<ArgumentNullException>(() => source.DefaultIfNotSingle(null!)); | ||
|
Check failure on line 107 in tests/Moq.Analyzers.Test/Common/EnumerableExtensionsTests.cs
|
||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| private sealed class CountingEnumerable<T>(IEnumerable<T> items) : IEnumerable<T> | ||
| { | ||
| private readonly IEnumerable<T> _items = items; | ||
|
|
||
| public int Count { get; private set; } | ||
|
|
||
| public IEnumerator<T> GetEnumerator() | ||
| { | ||
| foreach (T item in _items) | ||
| { | ||
| Count++; | ||
| yield return item; | ||
| } | ||
| } | ||
|
|
||
| IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.