Skip to content
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

Expose ExecuteOutcomeAsync and use it in benchmarks #1209

Merged
merged 3 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=2 WarmupCount=10

```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|------------------------- |---------:|--------:|--------:|------:|--------:|-------:|----------:|------------:|
| ExecuteCircuitBreaker_V7 | 310.9 ns | 2.48 ns | 3.48 ns | 1.00 | 0.00 | 0.0200 | 504 B | 1.00 |
| ExecuteCircuitBreaker_V8 | 545.0 ns | 2.72 ns | 3.99 ns | 1.75 | 0.02 | 0.0010 | 32 B | 0.06 |
| Method | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio |
|------------------------- |---------:|--------:|--------:|------:|-------:|----------:|------------:|
| ExecuteCircuitBreaker_V7 | 309.0 ns | 3.53 ns | 5.18 ns | 1.00 | 0.0200 | 504 B | 1.00 |
| ExecuteCircuitBreaker_V8 | 369.5 ns | 3.27 ns | 4.68 ns | 1.19 | 0.0010 | 32 B | 0.06 |
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ LaunchCount=2 WarmupCount=10
```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|-------------------------- |------------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| ExecuteAsync_Exception_V7 | 30,264.7 ns | 282.61 ns | 386.83 ns | 53.54 | 0.93 | 0.0916 | 2888 B | 12.89 |
| ExecuteAsync_Exception_V8 | 20,805.9 ns | 123.75 ns | 169.39 ns | 36.81 | 0.76 | 0.0610 | 1848 B | 8.25 |
| ExecuteAsync_Outcome_V8 | 565.4 ns | 7.41 ns | 10.15 ns | 1.00 | 0.00 | 0.0086 | 224 B | 1.00 |
| ExecuteAsync_Exception_V7 | 30,077.7 ns | 130.12 ns | 190.73 ns | 51.40 | 0.53 | 0.0610 | 2888 B | 12.89 |
| ExecuteAsync_Exception_V8 | 20,036.3 ns | 52.50 ns | 75.30 ns | 34.24 | 0.29 | 0.0610 | 1848 B | 8.25 |
| ExecuteAsync_Outcome_V8 | 585.3 ns | 3.50 ns | 4.91 ns | 1.00 | 0.00 | 0.0086 | 224 B | 1.00 |
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=2 WarmupCount=10

```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio |
|------------------------ |---------:|---------:|---------:|------:|--------:|----------:|------------:|
| ExecuteAsync_Generic | 29.98 ns | 0.110 ns | 0.158 ns | 1.00 | 0.00 | - | NA |
| ExecuteAsync_NonGeneric | 33.06 ns | 0.282 ns | 0.404 ns | 1.10 | 0.02 | - | NA |
| Method | Mean | Error | StdDev | Ratio | Allocated | Alloc Ratio |
|------------------------ |---------:|---------:|---------:|------:|----------:|------------:|
| ExecuteAsync_Generic | 29.91 ns | 0.117 ns | 0.168 ns | 1.00 | - | NA |
| ExecuteAsync_NonGeneric | 32.02 ns | 0.084 ns | 0.121 ns | 1.07 | - | NA |
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LaunchCount=2 WarmupCount=10
```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|---------------------------- |---------:|----------:|----------:|------:|--------:|-------:|-------:|----------:|------------:|
| Hedging_Primary | 1.087 μs | 0.0101 μs | 0.0141 μs | 1.00 | 0.00 | 0.0019 | - | 80 B | 1.00 |
| Hedging_Secondary | 1.910 μs | 0.0180 μs | 0.0269 μs | 1.76 | 0.04 | 0.0095 | - | 280 B | 3.50 |
| Hedging_Primary_AsyncWork | 5.156 μs | 0.1238 μs | 0.1814 μs | 4.74 | 0.15 | 0.0534 | 0.0229 | 1435 B | 17.94 |
| Hedging_Secondary_AsyncWork | 7.947 μs | 0.2024 μs | 0.2966 μs | 7.33 | 0.34 | 0.0763 | 0.0381 | 1951 B | 24.39 |
| Hedging_Primary | 1.209 μs | 0.0074 μs | 0.0110 μs | 1.00 | 0.00 | 0.0019 | - | 80 B | 1.00 |
| Hedging_Secondary | 2.152 μs | 0.0666 μs | 0.0933 μs | 1.78 | 0.08 | 0.0076 | - | 280 B | 3.50 |
| Hedging_Primary_AsyncWork | 5.962 μs | 0.5393 μs | 0.7560 μs | 4.93 | 0.64 | 0.0534 | 0.0229 | 1443 B | 18.04 |
| Hedging_Secondary_AsyncWork | 9.242 μs | 1.0615 μs | 1.5889 μs | 7.64 | 1.32 | 0.0610 | 0.0458 | 1815 B | 22.69 |
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=2 WarmupCount=10

```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|--------------------------- |---------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| ExecuteStrategyPipeline_V7 | 2.291 μs | 0.0265 μs | 0.0388 μs | 1.00 | 0.00 | 0.1106 | 2824 B | 1.00 |
| ExecuteStrategyPipeline_V8 | 2.053 μs | 0.0118 μs | 0.0177 μs | 0.90 | 0.02 | 0.0038 | 136 B | 0.05 |
| Method | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio |
|--------------------------- |---------:|----------:|----------:|------:|-------:|----------:|------------:|
| ExecuteStrategyPipeline_V7 | 2.294 μs | 0.0055 μs | 0.0080 μs | 1.00 | 0.1106 | 2824 B | 1.00 |
| ExecuteStrategyPipeline_V8 | 1.743 μs | 0.0139 μs | 0.0208 μs | 0.76 | 0.0019 | 72 B | 0.03 |
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=2 WarmupCount=10

```
| Method | Components | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|------------------- |----------- |-----------:|---------:|---------:|------:|--------:|-------:|----------:|------------:|
| **ExecutePipeline_V7** | **1** | **104.6 ns** | **0.96 ns** | **1.41 ns** | **1.00** | **0.00** | **0.0120** | **304 B** | **1.00** |
| ExecutePipeline_V8 | 1 | 179.6 ns | 2.04 ns | 2.86 ns | 1.72 | 0.04 | - | - | 0.00 |
| | | | | | | | | | |
| **ExecutePipeline_V7** | **2** | **267.0 ns** | **0.83 ns** | **1.22 ns** | **1.00** | **0.00** | **0.0219** | **552 B** | **1.00** |
| ExecutePipeline_V8 | 2 | 236.3 ns | 1.11 ns | 1.63 ns | 0.89 | 0.01 | - | - | 0.00 |
| | | | | | | | | | |
| **ExecutePipeline_V7** | **5** | **901.0 ns** | **4.79 ns** | **7.02 ns** | **1.00** | **0.00** | **0.0515** | **1296 B** | **1.00** |
| ExecutePipeline_V8 | 5 | 456.0 ns | 4.72 ns | 6.76 ns | 0.51 | 0.01 | - | - | 0.00 |
| | | | | | | | | | |
| **ExecutePipeline_V7** | **10** | **1,942.6 ns** | **35.76 ns** | **50.14 ns** | **1.00** | **0.00** | **0.0992** | **2536 B** | **1.00** |
| ExecutePipeline_V8 | 10 | 699.6 ns | 3.93 ns | 5.63 ns | 0.36 | 0.01 | - | - | 0.00 |
| Method | Components | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|------------------- |----------- |------------:|----------:|----------:|------------:|------:|--------:|-------:|----------:|------------:|
| **ExecutePipeline_V7** | **1** | **110.61 ns** | **4.817 ns** | **7.060 ns** | **107.53 ns** | **1.00** | **0.00** | **0.0120** | **304 B** | **1.00** |
| ExecutePipeline_V8 | 1 | 68.63 ns | 0.583 ns | 0.778 ns | 68.32 ns | 0.62 | 0.04 | - | - | 0.00 |
| | | | | | | | | | | |
| **ExecutePipeline_V7** | **2** | **267.80 ns** | **3.493 ns** | **5.228 ns** | **269.12 ns** | **1.00** | **0.00** | **0.0219** | **552 B** | **1.00** |
| ExecutePipeline_V8 | 2 | 118.35 ns | 5.882 ns | 8.246 ns | 124.97 ns | 0.44 | 0.04 | - | - | 0.00 |
| | | | | | | | | | | |
| **ExecutePipeline_V7** | **5** | **880.79 ns** | **9.145 ns** | **13.405 ns** | **877.94 ns** | **1.00** | **0.00** | **0.0515** | **1296 B** | **1.00** |
| ExecutePipeline_V8 | 5 | 255.76 ns | 1.435 ns | 2.149 ns | 255.36 ns | 0.29 | 0.00 | - | - | 0.00 |
| | | | | | | | | | | |
| **ExecutePipeline_V7** | **10** | **1,882.96 ns** | **15.883 ns** | **23.773 ns** | **1,885.28 ns** | **1.00** | **0.00** | **0.0992** | **2536 B** | **1.00** |
| ExecutePipeline_V8 | 10 | 524.46 ns | 1.388 ns | 2.034 ns | 524.45 ns | 0.28 | 0.00 | - | - | 0.00 |
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ LaunchCount=2 WarmupCount=10
```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|---------------------- |---------:|--------:|--------:|------:|--------:|-------:|----------:|------------:|
| ExecuteRateLimiter_V7 | 229.4 ns | 1.23 ns | 1.72 ns | 1.00 | 0.00 | 0.0148 | 376 B | 1.00 |
| ExecuteRateLimiter_V8 | 342.4 ns | 2.90 ns | 4.26 ns | 1.49 | 0.02 | 0.0014 | 40 B | 0.11 |
| ExecuteRateLimiter_V7 | 237.2 ns | 2.86 ns | 4.19 ns | 1.00 | 0.00 | 0.0148 | 376 B | 1.00 |
| ExecuteRateLimiter_V8 | 235.8 ns | 0.76 ns | 1.06 ns | 0.99 | 0.02 | 0.0014 | 40 B | 0.11 |
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LaunchCount=2 WarmupCount=10
```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio |
|----------------------------------------------- |----------:|---------:|---------:|------:|--------:|----------:|------------:|
| ExecuteOutcomeAsync | 61.80 ns | 0.537 ns | 0.770 ns | 1.00 | 0.00 | - | NA |
| ExecuteAsync_ResilienceContextAndState | 169.88 ns | 1.252 ns | 1.755 ns | 2.75 | 0.05 | - | NA |
| ExecuteAsync_CancellationToken | 178.11 ns | 0.894 ns | 1.253 ns | 2.88 | 0.02 | - | NA |
| ExecuteAsync_GenericStrategy_CancellationToken | 181.22 ns | 0.512 ns | 0.701 ns | 2.93 | 0.04 | - | NA |
| ExecuteOutcomeAsync | 69.47 ns | 0.660 ns | 0.947 ns | 1.00 | 0.00 | - | NA |
| ExecuteAsync_ResilienceContextAndState | 179.56 ns | 4.533 ns | 6.500 ns | 2.59 | 0.12 | - | NA |
| ExecuteAsync_CancellationToken | 190.38 ns | 3.343 ns | 4.900 ns | 2.74 | 0.08 | - | NA |
| ExecuteAsync_GenericStrategy_CancellationToken | 182.27 ns | 1.452 ns | 2.083 ns | 2.62 | 0.05 | - | NA |
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ LaunchCount=2 WarmupCount=10
```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|---------------- |---------:|--------:|--------:|------:|--------:|-------:|----------:|------------:|
| ExecuteRetry_V7 | 236.4 ns | 1.83 ns | 2.68 ns | 1.00 | 0.00 | 0.0219 | 552 B | 1.00 |
| ExecuteRetry_V8 | 283.2 ns | 0.95 ns | 1.39 ns | 1.20 | 0.02 | - | - | 0.00 |
| ExecuteRetry_V7 | 246.9 ns | 2.25 ns | 3.36 ns | 1.00 | 0.00 | 0.0219 | 552 B | 1.00 |
| ExecuteRetry_V8 | 172.9 ns | 4.08 ns | 5.85 ns | 0.70 | 0.03 | - | - | 0.00 |
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=2 WarmupCount=10

```
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|------------------ |---------:|--------:|--------:|------:|--------:|-------:|----------:|------------:|
| ExecuteTimeout_V7 | 383.2 ns | 2.10 ns | 2.94 ns | 1.00 | 0.00 | 0.0286 | 728 B | 1.00 |
| ExecuteTimeout_V8 | 415.4 ns | 4.12 ns | 5.50 ns | 1.08 | 0.02 | 0.0010 | 32 B | 0.04 |
| Method | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio |
|------------------ |---------:|--------:|--------:|------:|-------:|----------:|------------:|
| ExecuteTimeout_V7 | 381.1 ns | 2.96 ns | 4.42 ns | 1.00 | 0.0286 | 728 B | 1.00 |
| ExecuteTimeout_V8 | 262.6 ns | 1.96 ns | 2.88 ns | 0.69 | - | - | 0.00 |
26 changes: 6 additions & 20 deletions src/Polly.Core.Benchmarks/ResilienceStrategyBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,36 @@

namespace Polly.Core.Benchmarks;

#pragma warning disable CA1822 // Mark members as static

public class ResilienceStrategyBenchmark
{
private readonly DummyResilienceStrategy _strategy = new();
private readonly ResilienceStrategy<string> _genericStrategy = NullResilienceStrategy<string>.Instance;

[Benchmark(Baseline = true)]
public async ValueTask ExecuteOutcomeAsync()
{
var context = ResilienceContext.Get();
await _strategy.ExecuteOutcomeAsync((_, _) => new ValueTask<Outcome<string>>(new Outcome<string>("dummy")), context, "state").ConfigureAwait(false);
await NullResilienceStrategy.Instance.ExecuteOutcomeAsync((_, _) => new ValueTask<Outcome<string>>(new Outcome<string>("dummy")), context, "state").ConfigureAwait(false);
ResilienceContext.Return(context);
}

[Benchmark]
public async ValueTask ExecuteAsync_ResilienceContextAndState()
{
var context = ResilienceContext.Get();
await _strategy.ExecuteAsync((_, _) => new ValueTask<string>("dummy"), context, "state").ConfigureAwait(false);
await NullResilienceStrategy.Instance.ExecuteAsync((_, _) => new ValueTask<string>("dummy"), context, "state").ConfigureAwait(false);
ResilienceContext.Return(context);
}

[Benchmark]
public async ValueTask ExecuteAsync_CancellationToken()
{
await _strategy.ExecuteAsync(_ => new ValueTask<string>("dummy"), CancellationToken.None).ConfigureAwait(false);
await NullResilienceStrategy.Instance.ExecuteAsync(_ => new ValueTask<string>("dummy"), CancellationToken.None).ConfigureAwait(false);
}

[Benchmark]
public async ValueTask ExecuteAsync_GenericStrategy_CancellationToken()
{
await _genericStrategy.ExecuteAsync(_ => new ValueTask<string>("dummy"), CancellationToken.None).ConfigureAwait(false);
}

private class DummyResilienceStrategy : ResilienceStrategy
{
public ValueTask<Outcome<TResult>> ExecuteOutcomeAsync<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
TState state) => ExecuteCoreAsync(callback, context, state);

protected override ValueTask<Outcome<TResult>> ExecuteCoreAsync<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
TState state) => callback(context, state);
await NullResilienceStrategy<string>.Instance.ExecuteAsync(_ => new ValueTask<string>("dummy"), CancellationToken.None).ConfigureAwait(false);
}

public class NonGenericStrategy
Expand Down
11 changes: 10 additions & 1 deletion src/Polly.Core.Benchmarks/Utils/Helper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma warning disable S4225 // Extension methods should not extend "object"

using Polly.Strategy;

namespace Polly.Core.Benchmarks.Utils;

internal static partial class Helper
Expand All @@ -12,7 +14,14 @@ public static async ValueTask ExecuteAsync(this object obj, PollyVersion version
await ((IAsyncPolicy<string>)obj).ExecuteAsync(static _ => Task.FromResult("dummy"), CancellationToken.None).ConfigureAwait(false);
return;
case PollyVersion.V8:
await ((ResilienceStrategy<string>)obj).ExecuteAsync(static _ => new ValueTask<string>("dummy"), CancellationToken.None).ConfigureAwait(false);
var context = ResilienceContext.Get();

await ((ResilienceStrategy<string>)obj).ExecuteOutcomeAsync(
static (_, _) => new ValueTask<Outcome<string>>(new Outcome<string>("dummy")),
context,
string.Empty).ConfigureAwait(false);

ResilienceContext.Return(context);
return;
}

Expand Down
18 changes: 18 additions & 0 deletions src/Polly.Core.Tests/ResilienceStrategyTests.Async.ValueTaskT.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Polly.Strategy;

namespace Polly.Core.Tests;

#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
Expand Down Expand Up @@ -113,4 +115,20 @@ static async ValueTask AssertStackTrace(Func<ResilienceStrategy, ValueTask<strin

static ValueTask<string> MyThrowingMethod() => throw new FormatException();
}

[Fact]
public async Task ExecuteOutcomeAsync_Ok()
{
var result = await NullResilienceStrategy.Instance.ExecuteOutcomeAsync((context, state) =>
{
state.Should().Be("state");
context.IsSynchronous.Should().BeFalse();
context.ResultType.Should().Be(typeof(int));
return new Outcome<int>(12345).AsValueTask();
},
ResilienceContext.Get(),
"state");

result.Result.Should().Be(12345);
}
}
17 changes: 17 additions & 0 deletions src/Polly.Core.Tests/ResilienceStrategyTests.TResult.Async.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Polly.Strategy;
using Xunit;

namespace Polly.Core.Tests;
Expand Down Expand Up @@ -77,4 +78,20 @@ public async Task ExecuteAsync_GenericStrategy_Ok(Func<ResilienceStrategy<string

await execute(strategy);
}

[Fact]
public async Task ExecuteOutcomeAsync_GenericStrategy_Ok()
{
var result = await NullResilienceStrategy<int>.Instance.ExecuteOutcomeAsync((context, state) =>
{
state.Should().Be("state");
context.IsSynchronous.Should().BeFalse();
context.ResultType.Should().Be(typeof(int));
return new Outcome<int>(12345).AsValueTask();
},
ResilienceContext.Get(),
"state");

result.Result.Should().Be(12345);
}
}
27 changes: 27 additions & 0 deletions src/Polly.Core/ResilienceStrategy.Async.ValueTaskT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ namespace Polly;

public abstract partial class ResilienceStrategy
{
/// <summary>
/// Executes the specified outcome-based callback.
/// </summary>
/// <typeparam name="TResult">The type of result returned by the callback.</typeparam>
/// <typeparam name="TState">The type of state associated with the callback.</typeparam>
/// <param name="callback">The user-provided callback.</param>
/// <param name="context">The context associated with the callback.</param>
/// <param name="state">The state associated with the callback.</param>
/// <returns>The instance of <see cref="ValueTask"/> that represents the asynchronous execution.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="callback"/> or <paramref name="context"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method is for advanced and high performance scenarios. The caller must make sure that the <paramref name="callback"/>
/// does not throw any exceptions. Instead, it converts them to <see cref="Outcome{TResult}"/>.
/// </remarks>
public ValueTask<Outcome<TResult>> ExecuteOutcomeAsync<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
TState state)
{
Guard.NotNull(callback);
Guard.NotNull(context);

InitializeAsyncContext<TResult>(context);

return ExecuteCoreAsync(callback, context, state);
}

/// <summary>
/// Executes the specified callback.
/// </summary>
Expand Down
Loading