From 2c5c573fa21c4315e7ded5da2e0491e296c48317 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Sun, 10 Nov 2024 01:55:10 -0300 Subject: [PATCH] refactor: implementing flexible solution --- .github/workflows/semantic.yml | 13 +++---- .github/workflows/test.yml | 13 +++---- .../AsyncBranchingBuilder.cs | 21 ++++------- .../BaseBranchingBuilder.cs | 10 ++---- .../BranchResult.cs | 2 +- .../BranchingBuilder.cs | 20 ++++------- .../BranchingExtensions.cs | 8 ++--- .../Internal/BranchContext.cs | 20 +++++++++++ .../Internal/BranchedEnumerable.cs | 9 ----- .../Internal/BranchedEnumerator.cs | 36 ------------------- .../Internal/BranchingHelper.cs | 14 +++++--- .../Internal/LinkedNode.cs | 6 ++-- .../Benchmarks.cs | 32 ++++++++--------- ...rableExtensions.Branching.Benchmark.csproj | 2 +- .../AsyncEnumerableExtensionsTest.cs | 4 +-- ...EnumerableExtensions.Branching.Test.csproj | 2 +- .../EnumerableExtensionsTest.cs | 4 +-- 17 files changed, 88 insertions(+), 128 deletions(-) create mode 100644 src/Codibre.EnumerableExtensions.Branching/Internal/BranchContext.cs delete mode 100644 src/Codibre.EnumerableExtensions.Branching/Internal/BranchedEnumerable.cs delete mode 100644 src/Codibre.EnumerableExtensions.Branching/Internal/BranchedEnumerator.cs diff --git a/.github/workflows/semantic.yml b/.github/workflows/semantic.yml index 2975cd0..382ea8d 100644 --- a/.github/workflows/semantic.yml +++ b/.github/workflows/semantic.yml @@ -37,12 +37,13 @@ jobs: run: npm run build - name: Test - uses: paambaati/codeclimate-action@v9.0.0 - env: - CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} - with: - coverageCommand: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage/lcov.info - coverageLocations: ${{github.workspace}}/test/Codibre.GrpcSqlProxy.Test/coverage/lcov.info:lcov + run: npm run test:coverage + # uses: paambaati/codeclimate-action@v9.0.0 + # env: + # CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} + # with: + # coverageCommand: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage/lcov.info + # coverageLocations: ${{github.workspace}}/test/Codibre.GrpcSqlProxy.Test/coverage/lcov.info:lcov - name: Semantic Release run: npm i -g @semantic-release/changelog @semantic-release/commit-analyzer @semantic-release/git @semantic-release/github @semantic-release/exec @droidsolutions-oss/semantic-release-nuget @semantic-release/release-notes-generator semantic-release @semantic-release/error diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 749a9c3..abc0d39 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,9 +39,10 @@ jobs: # run: npm run docker:run - name: Test - uses: paambaati/codeclimate-action@v9.0.0 - env: - CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} - with: - coverageCommand: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage/lcov.info - coverageLocations: ${{github.workspace}}/test/Codibre.GrpcSqlProxy.Test/coverage/lcov.info:lcov + run: npm run test:coverage + # uses: paambaati/codeclimate-action@v9.0.0 + # env: + # CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} + # with: + # coverageCommand: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage/lcov.info + # coverageLocations: ${{github.workspace}}/test/Codibre.GrpcSqlProxy.Test/coverage/lcov.info:lcov diff --git a/src/Codibre.EnumerableExtensions.Branching/AsyncBranchingBuilder.cs b/src/Codibre.EnumerableExtensions.Branching/AsyncBranchingBuilder.cs index fdfb015..6058896 100644 --- a/src/Codibre.EnumerableExtensions.Branching/AsyncBranchingBuilder.cs +++ b/src/Codibre.EnumerableExtensions.Branching/AsyncBranchingBuilder.cs @@ -1,26 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Codibre.EnumerableExtensions.Branching.Internal; namespace Codibre.EnumerableExtensions.Branching; -public class AsyncBranchingBuilder(IAsyncEnumerable source) : BaseBranchingBuilder +public sealed class AsyncBranchingBuilder(IAsyncEnumerable source) : BaseBranchingBuilder { - internal override async ValueTask<(LinkedNode?, Task)> Iterate() + internal override LinkedNode Iterate(int branchCount) { var enumerator = source.GetAsyncEnumerator(); - var node = await enumerator.MoveLoose() ? new LinkedNode(enumerator.Current) : null; - return (node, node is null ? Task.CompletedTask : Task.Run(async () => - { - try - { - while (await enumerator.MoveLoose()) node = node.Next = new LinkedNode(enumerator.Current); - } - finally - { - node.End = true; - } - })); + return new(enumerator.Current, new( + async (c) => await enumerator.MoveNextAsync() ? new(enumerator.Current, c) : null, + branchCount + )); } } \ No newline at end of file diff --git a/src/Codibre.EnumerableExtensions.Branching/BaseBranchingBuilder.cs b/src/Codibre.EnumerableExtensions.Branching/BaseBranchingBuilder.cs index 2622b87..82caf54 100644 --- a/src/Codibre.EnumerableExtensions.Branching/BaseBranchingBuilder.cs +++ b/src/Codibre.EnumerableExtensions.Branching/BaseBranchingBuilder.cs @@ -18,13 +18,9 @@ internal BaseBranchingBuilder Add(Func, Task> branch) public async Task Run() { - var (node, iterate) = await Iterate().ConfigureAwait(false); - await Task.WhenAll( - _branches - .Select((x, index) => x(node.GetBranchedIterable())) - .Append(iterate) - ).ConfigureAwait(false); + var node = Iterate(_branches.Count); + await Task.WhenAll(_branches.Select(x => x(node.GetBranchedIterable()))); } - internal abstract ValueTask<(LinkedNode?, Task)> Iterate(); + internal abstract LinkedNode Iterate(int branchCount); } \ No newline at end of file diff --git a/src/Codibre.EnumerableExtensions.Branching/BranchResult.cs b/src/Codibre.EnumerableExtensions.Branching/BranchResult.cs index fe4872d..f325254 100644 --- a/src/Codibre.EnumerableExtensions.Branching/BranchResult.cs +++ b/src/Codibre.EnumerableExtensions.Branching/BranchResult.cs @@ -5,7 +5,7 @@ namespace Codibre.EnumerableExtensions.Branching; -public record BranchResult +public sealed record BranchResult { private bool _set = false; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. diff --git a/src/Codibre.EnumerableExtensions.Branching/BranchingBuilder.cs b/src/Codibre.EnumerableExtensions.Branching/BranchingBuilder.cs index 11f2d59..56c2e0f 100644 --- a/src/Codibre.EnumerableExtensions.Branching/BranchingBuilder.cs +++ b/src/Codibre.EnumerableExtensions.Branching/BranchingBuilder.cs @@ -6,22 +6,14 @@ namespace Codibre.EnumerableExtensions.Branching; -public class BranchingBuilder(IEnumerable source) : BaseBranchingBuilder +public sealed class BranchingBuilder(IEnumerable source) : BaseBranchingBuilder { - internal override ValueTask<(LinkedNode?, Task)> Iterate() + internal override LinkedNode Iterate(int branchCount) { var enumerator = source.GetEnumerator(); - var node = enumerator.MoveNext() ? new LinkedNode(enumerator.Current) : null; - return ValueTask.FromResult((node, node is null ? Task.CompletedTask : Task.Run(async () => - { - try - { - while (enumerator.MoveNext()) node = node.Next = new LinkedNode(enumerator.Current); - } - finally - { - node.End = true; - } - }))); + return new LinkedNode(default!, new( + (c) => ValueTask.FromResult?>(enumerator.MoveNext() ? new(enumerator.Current, c) : null), + branchCount + )); } } \ No newline at end of file diff --git a/src/Codibre.EnumerableExtensions.Branching/BranchingExtensions.cs b/src/Codibre.EnumerableExtensions.Branching/BranchingExtensions.cs index 0bc3159..a158bd8 100644 --- a/src/Codibre.EnumerableExtensions.Branching/BranchingExtensions.cs +++ b/src/Codibre.EnumerableExtensions.Branching/BranchingExtensions.cs @@ -11,17 +11,17 @@ public static class BranchingExtensions public static BaseBranchingBuilder Add(this BaseBranchingBuilder builder, Func, ValueTask> branch, out BranchResult result) { var refResult = result = new(); - return builder.Add(async (x) => refResult.Result = await branch(x).ConfigureAwait(false)); + return builder.Add(async (x) => refResult.Result = await branch(x)); } public static BaseBranchingBuilder Add(this BaseBranchingBuilder builder, Func, ValueTask> branch) - => builder.Add(async (x) => await branch(x).AsTask().ConfigureAwait(false)); + => builder.Add(async (x) => await branch(x).AsTask()); public static BaseBranchingBuilder Add(this BaseBranchingBuilder builder, Func, Task> branch, out BranchResult result) { var refResult = result = new(); - return builder.Add(async (x) => refResult.Result = await branch(x).ConfigureAwait(false)); + return builder.Add(async (x) => refResult.Result = await branch(x)); } public static BaseBranchingBuilder Add(this BaseBranchingBuilder builder, Func, Task> branch) - => builder.Add(async (x) => await branch(x).ConfigureAwait(false)); + => builder.Add(async (x) => await branch(x)); } \ No newline at end of file diff --git a/src/Codibre.EnumerableExtensions.Branching/Internal/BranchContext.cs b/src/Codibre.EnumerableExtensions.Branching/Internal/BranchContext.cs new file mode 100644 index 0000000..056ce9c --- /dev/null +++ b/src/Codibre.EnumerableExtensions.Branching/Internal/BranchContext.cs @@ -0,0 +1,20 @@ +using System.Runtime.CompilerServices; +using System.Xml.XPath; + +namespace Codibre.EnumerableExtensions.Branching.Internal; + +internal sealed record BranchContext(Func, ValueTask?>> GetNext, int _branchCount) +{ + private ushort _count = 0; + private readonly ushort _limit = (ushort)int.Min(_branchCount << 14, ushort.MaxValue / 2); + + internal async ValueTask?> FillNext() + { + if (++_count > _limit) + { + _count = 0; + await Task.Yield(); + } + return await GetNext(this); + } +} \ No newline at end of file diff --git a/src/Codibre.EnumerableExtensions.Branching/Internal/BranchedEnumerable.cs b/src/Codibre.EnumerableExtensions.Branching/Internal/BranchedEnumerable.cs deleted file mode 100644 index 6308983..0000000 --- a/src/Codibre.EnumerableExtensions.Branching/Internal/BranchedEnumerable.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections; - -namespace Codibre.EnumerableExtensions.Branching.Internal; - -internal record BranchedEnumerable(LinkedNode node) : IAsyncEnumerable -{ - public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) - => new BranchedEnumerator(node); -} \ No newline at end of file diff --git a/src/Codibre.EnumerableExtensions.Branching/Internal/BranchedEnumerator.cs b/src/Codibre.EnumerableExtensions.Branching/Internal/BranchedEnumerator.cs deleted file mode 100644 index 7877a27..0000000 --- a/src/Codibre.EnumerableExtensions.Branching/Internal/BranchedEnumerator.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections; - -namespace Codibre.EnumerableExtensions.Branching.Internal; - -internal record BranchedEnumerator : IAsyncEnumerator -{ - private LinkedNode _node; - private readonly int _altenateOn; - private int _count; - public BranchedEnumerator(LinkedNode node, int altenateOn = 100) - { - _node = new LinkedNode(default) - { - Next = node, - }; - _altenateOn = altenateOn; - _count = altenateOn; - } - - public T Current => _node.Value; - public ValueTask DisposeAsync() => ValueTask.CompletedTask; - - public async ValueTask MoveNextAsync() - { - while (!_node.End && _node.Next is null) await Task.Yield(); - if (_node.Next is null) return false; - _node = _node.Next; - _count--; - if (_count <= 0) - { - _count = _altenateOn; - await Task.Yield(); - } - return true; - } -} \ No newline at end of file diff --git a/src/Codibre.EnumerableExtensions.Branching/Internal/BranchingHelper.cs b/src/Codibre.EnumerableExtensions.Branching/Internal/BranchingHelper.cs index 8737946..6292c15 100644 --- a/src/Codibre.EnumerableExtensions.Branching/Internal/BranchingHelper.cs +++ b/src/Codibre.EnumerableExtensions.Branching/Internal/BranchingHelper.cs @@ -9,9 +9,13 @@ namespace Codibre.EnumerableExtensions.Branching.Internal; internal static class BranchingHelper { - internal static IAsyncEnumerable GetBranchedIterable(this LinkedNode? node) - => node is null ? Array.Empty().ToAsyncEnumerable() : new BranchedEnumerable(node); - - internal static ConfiguredValueTaskAwaitable MoveLoose(this IAsyncEnumerator enumerator) - => enumerator.MoveNextAsync().ConfigureAwait(false); + internal static async IAsyncEnumerable GetBranchedIterable(this LinkedNode root) + { + var node = await root.Next.Value; + while (node is not null) + { + yield return node.Value; + node = await node.Next.Value; + } + } } \ No newline at end of file diff --git a/src/Codibre.EnumerableExtensions.Branching/Internal/LinkedNode.cs b/src/Codibre.EnumerableExtensions.Branching/Internal/LinkedNode.cs index 35b2b8c..e6245fc 100644 --- a/src/Codibre.EnumerableExtensions.Branching/Internal/LinkedNode.cs +++ b/src/Codibre.EnumerableExtensions.Branching/Internal/LinkedNode.cs @@ -1,8 +1,6 @@ namespace Codibre.EnumerableExtensions.Branching.Internal; -internal record LinkedNode(T value) +internal sealed record LinkedNode(T Value, BranchContext Context) { - public T Value { get; } = value; - public LinkedNode? Next { get; set; } = null; - public bool End { get; set; } = false; + public Lazy?>> Next { get; } = new(Context.FillNext, LazyThreadSafetyMode.ExecutionAndPublication); } \ No newline at end of file diff --git a/test/Codibre.EnumerableExtensions.Branching.Benchmark/Benchmarks.cs b/test/Codibre.EnumerableExtensions.Branching.Benchmark/Benchmarks.cs index 2070413..d770dac 100644 --- a/test/Codibre.EnumerableExtensions.Branching.Benchmark/Benchmarks.cs +++ b/test/Codibre.EnumerableExtensions.Branching.Benchmark/Benchmarks.cs @@ -13,7 +13,7 @@ public static IEnumerable AddOps(this IEnumerable source, int value) public class Benchmarks { - [Params(100)] + [Params(100)] //, 1000, 10000)] public int _size = 100; private IEnumerable GetBaseEnumerable() => Enumerable.Range(0, _size) @@ -21,22 +21,22 @@ private IEnumerable GetBaseEnumerable() .AddOps(3) .AddOps(4); - [Benchmark] - public void Separate() - { - GetBaseEnumerable().Min(); - GetBaseEnumerable().Max(); - GetBaseEnumerable().Average(); - } + // [Benchmark] + // public void Separate() + // { + // GetBaseEnumerable().Min(); + // GetBaseEnumerable().Max(); + // GetBaseEnumerable().Average(); + // } - [Benchmark] - public void ManualBranch() - { - var baseEnum = GetBaseEnumerable(); - baseEnum.Min(); - baseEnum.Max(); - baseEnum.Average(); - } + // [Benchmark] + // public void ManualBranch() + // { + // var baseEnum = GetBaseEnumerable(); + // baseEnum.Min(); + // baseEnum.Max(); + // baseEnum.Average(); + // } [Benchmark] public Task Branching() => GetBaseEnumerable() diff --git a/test/Codibre.EnumerableExtensions.Branching.Benchmark/Codibre.EnumerableExtensions.Branching.Benchmark.csproj b/test/Codibre.EnumerableExtensions.Branching.Benchmark/Codibre.EnumerableExtensions.Branching.Benchmark.csproj index 2dfea2d..484f3d7 100644 --- a/test/Codibre.EnumerableExtensions.Branching.Benchmark/Codibre.EnumerableExtensions.Branching.Benchmark.csproj +++ b/test/Codibre.EnumerableExtensions.Branching.Benchmark/Codibre.EnumerableExtensions.Branching.Benchmark.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 enable enable diff --git a/test/Codibre.EnumerableExtensions.Branching.Test/AsyncEnumerableExtensionsTest.cs b/test/Codibre.EnumerableExtensions.Branching.Test/AsyncEnumerableExtensionsTest.cs index a5dbc70..195117d 100644 --- a/test/Codibre.EnumerableExtensions.Branching.Test/AsyncEnumerableExtensionsTest.cs +++ b/test/Codibre.EnumerableExtensions.Branching.Test/AsyncEnumerableExtensionsTest.cs @@ -3,7 +3,7 @@ namespace Codibre.EnumerableExtensions.Branching.Test; [Collection("Tests")] -public class AsyncBranchingBuilderTest(ITestOutputHelper helper) +public class AsyncBranchingBuilderTest() { private static IAsyncEnumerable Op(IAsyncEnumerable list) => list .Select(x => x * 2) @@ -74,7 +74,7 @@ await enumerable.Branch() public async Task Should_Intercalate_The_Steps_Between_Every_Branch() { // Arrange - var total = 1000000; + var total = 100000; var list = Enumerable.Range(0, total).ToAsyncEnumerable(); List steps = []; var enumerable = Op(list); diff --git a/test/Codibre.EnumerableExtensions.Branching.Test/Codibre.EnumerableExtensions.Branching.Test.csproj b/test/Codibre.EnumerableExtensions.Branching.Test/Codibre.EnumerableExtensions.Branching.Test.csproj index 18b3408..967f2c0 100644 --- a/test/Codibre.EnumerableExtensions.Branching.Test/Codibre.EnumerableExtensions.Branching.Test.csproj +++ b/test/Codibre.EnumerableExtensions.Branching.Test/Codibre.EnumerableExtensions.Branching.Test.csproj @@ -1,7 +1,7 @@  - net7.0;net8.0;net9.0 + net7.0;net8.0 enable enable false diff --git a/test/Codibre.EnumerableExtensions.Branching.Test/EnumerableExtensionsTest.cs b/test/Codibre.EnumerableExtensions.Branching.Test/EnumerableExtensionsTest.cs index a722a78..478bfa4 100644 --- a/test/Codibre.EnumerableExtensions.Branching.Test/EnumerableExtensionsTest.cs +++ b/test/Codibre.EnumerableExtensions.Branching.Test/EnumerableExtensionsTest.cs @@ -3,7 +3,7 @@ namespace Codibre.EnumerableExtensions.Branching.Test; [Collection("Tests")] -public class BranchingBuilderTest(ITestOutputHelper helper) +public class BranchingBuilderTest() { private static IEnumerable Op(IEnumerable list) => list .Select(x => x * 2) @@ -74,7 +74,7 @@ await enumerable.Branch() public async Task Should_Intercalate_The_Steps_Between_Every_Branch() { // Arrange - var total = 1000000; + var total = 100000; var list = Enumerable.Range(0, total); List steps = []; var enumerable = Op(list);