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

Add Benchmarks project #864

Merged
merged 13 commits into from
Aug 25, 2021
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ UpgradeLog*.XML

artifacts
build
BenchmarkDotNet.Artifacts
tools

*.lock.json
Expand Down
49 changes: 49 additions & 0 deletions src/Polly.Benchmarks/Bulkhead.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class Bulkhead
{
private static readonly Policy SyncPolicy = Policy.Bulkhead(2);
private static readonly AsyncPolicy AsyncPolicy = Policy.BulkheadAsync(2);

[Benchmark]
public void Bulkhead_Synchronous()
{
SyncPolicy.Execute(() => Workloads.Action());
}

[Benchmark]
public async Task Bulkhead_Asynchronous()
{
await AsyncPolicy.ExecuteAsync(() => Workloads.ActionAsync());
}

[Benchmark]
public async Task Bulkhead_Asynchronous_With_CancellationToken()
{
await AsyncPolicy.ExecuteAsync((token) => Workloads.ActionAsync(token), CancellationToken.None);
}

[Benchmark]
public int Bulkhead_Synchronous_With_Result()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> Bulkhead_Asynchronous_With_Result()
{
return await AsyncPolicy.ExecuteAsync(() => Workloads.FuncAsync<int>());
}

[Benchmark]
public async Task<int> Bulkhead_Asynchronous_With_Result_With_CancellationToken()
{
return await AsyncPolicy.ExecuteAsync((token) => Workloads.FuncAsync<int>(token), CancellationToken.None);
}
}
}
111 changes: 111 additions & 0 deletions src/Polly.Benchmarks/Cache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.Caching.Memory;
using Polly.Caching;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class Cache
{
private static readonly MemoryCache MemoryCache = new MemoryCache(new MemoryCacheOptions());
private static readonly MemoryCacheProvider CacheProvider = new MemoryCacheProvider(MemoryCache);

private static readonly Policy SyncPolicyMiss = Policy.Cache(CacheProvider, TimeSpan.Zero);
private static readonly AsyncPolicy AsyncPolicyMiss = Policy.CacheAsync(CacheProvider, TimeSpan.Zero);

private static readonly Policy SyncPolicyHit = Policy.Cache(CacheProvider, TimeSpan.MaxValue);
private static readonly AsyncPolicy AsyncPolicyHit = Policy.CacheAsync(CacheProvider, TimeSpan.MaxValue);

private static readonly Context HitContext = new Context(nameof(HitContext));
private static readonly Context MissContext = new Context(nameof(MissContext));

[GlobalSetup]
public async Task GlobalSetup()
{
SyncPolicyHit.Execute((context) => GetObject(), HitContext);
await AsyncPolicyHit.ExecuteAsync((context, token) => GetObjectAsync(token), HitContext, CancellationToken.None);
}

[Benchmark]
public object Cache_Synchronous_Hit()
{
return SyncPolicyHit.Execute((context) => GetObject(), HitContext);
}

[Benchmark]
public async Task<object> Cache_Asynchronous_Hit()
{
return await AsyncPolicyHit.ExecuteAsync((context, token) => GetObjectAsync(token), HitContext, CancellationToken.None);
}

[Benchmark]
public object Cache_Synchronous_Miss()
{
return SyncPolicyMiss.Execute((context) => GetObject(), MissContext);
}

[Benchmark]
public async Task<object> Cache_Asynchronous_Miss()
{
return await AsyncPolicyMiss.ExecuteAsync((context, token) => GetObjectAsync(token), MissContext, CancellationToken.None);
}

private static object GetObject() => new object();

private static Task<object> GetObjectAsync(CancellationToken cancellationToken) => Task.FromResult(new object());

private sealed class MemoryCacheProvider : ISyncCacheProvider, IAsyncCacheProvider
{
private readonly IMemoryCache _cache;

public MemoryCacheProvider(IMemoryCache memoryCache)
{
_cache = memoryCache;
}

public (bool, object) TryGet(string key)
{
bool cacheHit = _cache.TryGetValue(key, out var value);
return (cacheHit, value);
}

public void Put(string key, object value, Ttl ttl)
{
TimeSpan remaining = DateTimeOffset.MaxValue - DateTimeOffset.UtcNow;
var options = new MemoryCacheEntryOptions();

if (ttl.SlidingExpiration)
{
options.SlidingExpiration = ttl.Timespan < remaining ? ttl.Timespan : remaining;
}
else
{
if (ttl.Timespan == TimeSpan.MaxValue)
{
options.AbsoluteExpiration = DateTimeOffset.MaxValue;
}
else
{
options.AbsoluteExpirationRelativeToNow = ttl.Timespan < remaining ? ttl.Timespan : remaining;
}
}

_cache.Set(key, value, options);
}

public Task<(bool, object)> TryGetAsync(string key, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
return Task.FromResult(TryGet(key));
}

public Task PutAsync(string key, object value, Ttl ttl, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
Put(key, value, ttl);
return Task.CompletedTask;
}
}
}
}
38 changes: 38 additions & 0 deletions src/Polly.Benchmarks/CircuitBreaker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class CircuitBreaker
{
private static readonly Policy SyncPolicy = Policy.Handle<InvalidOperationException>().CircuitBreaker(2, TimeSpan.FromMinutes(1));
private static readonly AsyncPolicy AsyncPolicy = Policy.Handle<InvalidOperationException>().CircuitBreakerAsync(2, TimeSpan.FromMinutes(1));

[Benchmark]
public void CircuitBreaker_Synchronous_Succeeds()
{
SyncPolicy.Execute(() => Workloads.Action());
}

[Benchmark]
public async Task CircuitBreaker_Asynchronous_Succeeds()
{
await AsyncPolicy.ExecuteAsync((token) => Workloads.ActionAsync(token), CancellationToken.None);
}

[Benchmark]
public int CircuitBreaker_Synchronous_With_Result_Succeeds()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> CircuitBreaker_Asynchronous_With_Result_Succeeds()
{
return await AsyncPolicy.ExecuteAsync((token) => Workloads.FuncAsync<int>(token), CancellationToken.None);
}
}
}
37 changes: 37 additions & 0 deletions src/Polly.Benchmarks/Fallback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class Fallback
{
private static readonly Policy<int> SyncPolicy = Policy<int>.Handle<InvalidOperationException>().Fallback(0);
private static readonly AsyncPolicy<int> AsyncPolicy = Policy<int>.Handle<InvalidOperationException>().FallbackAsync(0);

[Benchmark]
public int Fallback_Synchronous_Succeeds()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> Fallback_Asynchronous_Succeeds()
{
return await AsyncPolicy.ExecuteAsync(() => Workloads.FuncAsync<int>());
}

[Benchmark]
public int Fallback_Synchronous_Throws()
{
return SyncPolicy.Execute(() => Workloads.FuncThrows<int, InvalidOperationException>());
}

[Benchmark]
public async Task<int> Fallback_Asynchronous_Throws()
{
return await AsyncPolicy.ExecuteAsync(() => Workloads.FuncThrowsAsync<int, InvalidOperationException>());
}
}
}
37 changes: 37 additions & 0 deletions src/Polly.Benchmarks/NoOp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class NoOp
{
private static readonly Policy SyncPolicy = Policy.NoOp();
private static readonly AsyncPolicy AsyncPolicy = Policy.NoOpAsync();

[Benchmark]
public void NoOp_Synchronous()
{
SyncPolicy.Execute(() => Workloads.Action());
}

[Benchmark]
public async Task NoOp_Asynchronous()
{
await AsyncPolicy.ExecuteAsync((token) => Workloads.ActionAsync(token), CancellationToken.None);
}

[Benchmark]
public int NoOp_Synchronous_With_Result()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> NoOp_Asynchronous_With_Result()
{
return await AsyncPolicy.ExecuteAsync((token) => Workloads.FuncAsync<int>(token), CancellationToken.None);
}
}
}
47 changes: 47 additions & 0 deletions src/Polly.Benchmarks/PolicyWrap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;

namespace Polly.Benchmarks
{
[Config(typeof(PollyConfig))]
public class PolicyWrap
{
private static readonly Policy SyncPolicy = Policy.Wrap(
Policy.Handle<InvalidOperationException>().Retry(),
Policy.Handle<InvalidOperationException>().CircuitBreaker(2, TimeSpan.FromMinutes(1)),
Policy.Timeout(TimeSpan.FromMilliseconds(10)),
Policy.Bulkhead(2));

private static readonly AsyncPolicy AsyncPolicy = Policy.WrapAsync(
Policy.Handle<InvalidOperationException>().RetryAsync(),
Policy.Handle<InvalidOperationException>().CircuitBreakerAsync(2, TimeSpan.FromMinutes(1)),
Policy.TimeoutAsync(TimeSpan.FromMilliseconds(10)),
Policy.BulkheadAsync(2));

[Benchmark]
public void PolicyWrap_Synchronous()
{
SyncPolicy.Execute(() => Workloads.Action());
}

[Benchmark]
public async Task PolicyWrap_Asynchronous()
{
await AsyncPolicy.ExecuteAsync((token) => Workloads.ActionAsync(token), CancellationToken.None);
}

[Benchmark]
public int PolicyWrap_Synchronous_With_Result()
{
return SyncPolicy.Execute(() => Workloads.Func<int>());
}

[Benchmark]
public async Task<int> PolicyWrap_Asynchronous_With_Result()
{
return await AsyncPolicy.ExecuteAsync((token) => Workloads.FuncAsync<int>(token), CancellationToken.None);
}
}
}
14 changes: 14 additions & 0 deletions src/Polly.Benchmarks/Polly.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>latest</LangVersion>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(BenchmarkFromNuGet)' != 'True' ">
<ProjectReference Include="..\Polly\Polly.csproj" />
</ItemGroup>
</Project>
37 changes: 37 additions & 0 deletions src/Polly.Benchmarks/PollyConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;

namespace Polly.Benchmarks
{
internal class PollyConfig : ManualConfig
{
public PollyConfig()
{
var job = Job.Default;

AddDiagnoser(BenchmarkDotNet.Diagnosers.MemoryDiagnoser.Default);

AddJob(PollyJob(job, useNuGet: true).AsBaseline());
AddJob(PollyJob(job, useNuGet: false));
}

private static Job PollyJob(Job job, bool useNuGet)
{
var result = job
.WithId("Polly" + (useNuGet ? string.Empty : "-dev"))
.WithArguments(
new[]
{
new MsBuildArgument("/p:BenchmarkFromNuGet=" + useNuGet),
new MsBuildArgument("/p:SignAssembly=false"),
});

if (useNuGet)
{
result = result.WithNuGet("Polly", "7.2.1");
martincostello marked this conversation as resolved.
Show resolved Hide resolved
}

return result;
}
}
}
4 changes: 4 additions & 0 deletions src/Polly.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using System.Reflection;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run(Assembly.GetCallingAssembly(), args: args);
Loading