-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolve AOT compilation issues (#1737)
- Fix #1732 using `RuntimeFeature.IsDynamicCodeSupported` to guard against the allocation regression that avoiding the infinite generic recursion causes through boxing. - Add a console project that validates whether the new-in-v8 Polly assemblies are AoT compatible. - Mark the new Polly v8 assemblies as AoT compatible. - Add a micro-benchmark for `DelegatingComponent` code path for non-AoT and AoT.
- Loading branch information
1 parent
61fdcbd
commit e404873
Showing
15 changed files
with
247 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
...cts/results/Polly.Core.Benchmarks.DelegatingComponentBenchmark-report-github.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
``` | ||
BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, Windows 11 (10.0.22621.2428/22H2/2022Update/SunValley2) | ||
12th Gen Intel Core i7-1270P, 1 CPU, 16 logical and 12 physical cores | ||
.NET SDK 7.0.403 | ||
[Host] : .NET 7.0.13 (7.0.1323.51816), X64 RyuJIT AVX2 | ||
Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15 | ||
LaunchCount=2 WarmupCount=10 | ||
``` | ||
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | | ||
|------------------------------------ |---------:|---------:|---------:|------:|--------:|-------:|----------:|------------:| | ||
| DelegatingComponent_ExecuteCore_Jit | 29.40 ns | 0.699 ns | 1.025 ns | 1.00 | 0.00 | - | - | NA | | ||
| DelegatingComponent_ExecuteCore_Aot | 34.65 ns | 0.627 ns | 0.919 ns | 1.18 | 0.05 | 0.0025 | 24 B | NA | |
38 changes: 38 additions & 0 deletions
38
bench/Polly.Core.Benchmarks/DelegatingComponentBenchmark.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using Polly.Utils.Pipeline; | ||
|
||
namespace Polly.Core.Benchmarks; | ||
|
||
public class DelegatingComponentBenchmark : IAsyncDisposable | ||
{ | ||
private ResilienceContext? _context; | ||
private DelegatingComponent? _component; | ||
|
||
[GlobalSetup] | ||
public void Setup() | ||
{ | ||
var first = PipelineComponent.Empty; | ||
var second = PipelineComponent.Empty; | ||
|
||
_component = new DelegatingComponent(first) { Next = second }; | ||
_context = ResilienceContextPool.Shared.Get(); | ||
} | ||
|
||
public async ValueTask DisposeAsync() | ||
{ | ||
if (_component is not null) | ||
{ | ||
await _component.DisposeAsync().ConfigureAwait(false); | ||
_component = null; | ||
} | ||
|
||
GC.SuppressFinalize(this); | ||
} | ||
|
||
[Benchmark(Baseline = true)] | ||
public ValueTask<Outcome<int>> DelegatingComponent_ExecuteCore_Jit() | ||
=> _component!.ExecuteComponent((_, state) => Outcome.FromResultAsValueTask(state), _context!, 42); | ||
|
||
[Benchmark] | ||
public ValueTask<Outcome<int>> DelegatingComponent_ExecuteCore_Aot() | ||
=> _component!.ExecuteComponentAot((_, state) => Outcome.FromResultAsValueTask(state), _context!, 42); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Polly.Utils.Pipeline; | ||
|
||
/// <summary> | ||
/// A component that delegates the execution to the next component in the chain. | ||
/// </summary> | ||
internal sealed class DelegatingComponent : PipelineComponent | ||
{ | ||
private readonly PipelineComponent _component; | ||
|
||
public DelegatingComponent(PipelineComponent component) => _component = component; | ||
|
||
public PipelineComponent? Next { get; set; } | ||
|
||
public override ValueTask DisposeAsync() => default; | ||
|
||
[ExcludeFromCodeCoverage] | ||
internal override ValueTask<Outcome<TResult>> ExecuteCore<TResult, TState>( | ||
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback, | ||
ResilienceContext context, | ||
TState state) | ||
{ | ||
#if NET6_0_OR_GREATER | ||
return RuntimeFeature.IsDynamicCodeSupported ? ExecuteComponent(callback, context, state) : ExecuteComponentAot(callback, context, state); | ||
#else | ||
return ExecuteComponent(callback, context, state); | ||
#endif | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static ValueTask<Outcome<TResult>> ExecuteNext<TResult, TState>( | ||
PipelineComponent next, | ||
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback, | ||
ResilienceContext context, | ||
TState state) | ||
{ | ||
if (context.CancellationToken.IsCancellationRequested) | ||
{ | ||
return Outcome.FromExceptionAsValueTask<TResult>(new OperationCanceledException(context.CancellationToken).TrySetStackTrace()); | ||
} | ||
|
||
return next.ExecuteCore(callback, context, state); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
internal ValueTask<Outcome<TResult>> ExecuteComponent<TResult, TState>( | ||
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback, | ||
ResilienceContext context, | ||
TState state) | ||
{ | ||
return _component.ExecuteCore( | ||
static (context, state) => ExecuteNext(state.Next!, state.callback, context, state.state), | ||
context, | ||
(Next, callback, state)); | ||
} | ||
|
||
#if NET6_0_OR_GREATER | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
internal ValueTask<Outcome<TResult>> ExecuteComponentAot<TResult, TState>( | ||
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback, | ||
ResilienceContext context, | ||
TState state) | ||
{ | ||
// Custom state object is used to cast the callback and state to prevent infinite | ||
// generic type recursion warning IL3054 when referenced in a native AoT application. | ||
// See https://github.com/App-vNext/Polly/issues/1732 for further context. | ||
return _component.ExecuteCore( | ||
static (context, wrapper) => | ||
{ | ||
var callback = (Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>>)wrapper.Callback; | ||
var state = (TState)wrapper.State; | ||
return ExecuteNext(wrapper.Next, callback, context, state); | ||
}, | ||
context, | ||
new StateWrapper(Next!, callback, state!)); | ||
} | ||
|
||
private readonly record struct StateWrapper(PipelineComponent Next, object Callback, object State); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<Nullable>enable</Nullable> | ||
<OutputType>Exe</OutputType> | ||
<PublishAot>true</PublishAot> | ||
<SKIP_POLLY_ANALYZERS>true</SKIP_POLLY_ANALYZERS> | ||
<TargetFramework>net7.0</TargetFramework> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Polly.Core\Polly.Core.csproj" /> | ||
<ProjectReference Include="..\..\src\Polly.Extensions\Polly.Extensions.csproj" /> | ||
<ProjectReference Include="..\..\src\Polly.RateLimiting\Polly.RateLimiting.csproj" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<TrimmerRootAssembly Include="Polly.Core" /> | ||
<TrimmerRootAssembly Include="Polly.Extensions" /> | ||
<TrimmerRootAssembly Include="Polly.RateLimiting" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Console.WriteLine("Hello Polly!"); |
Oops, something went wrong.