From 26116207b033dbab106c246cd5f6c906aa4dedb2 Mon Sep 17 00:00:00 2001 From: Timothy Makkison Date: Sun, 23 Jun 2024 12:19:26 +0100 Subject: [PATCH 1/3] chore: use file scoped namespaces --- Refit.Benchmarks/EndToEndBenchmark.cs | 479 +++++++++--------- Refit.Benchmarks/IGitHubService.cs | 149 +++--- .../StaticFileHttpResponseHandler.cs | 27 +- 3 files changed, 326 insertions(+), 329 deletions(-) diff --git a/Refit.Benchmarks/EndToEndBenchmark.cs b/Refit.Benchmarks/EndToEndBenchmark.cs index 792f6912f..15f74415f 100644 --- a/Refit.Benchmarks/EndToEndBenchmark.cs +++ b/Refit.Benchmarks/EndToEndBenchmark.cs @@ -2,317 +2,316 @@ using AutoFixture; using BenchmarkDotNet.Attributes; -namespace Refit.Benchmarks +namespace Refit.Benchmarks; + +[MemoryDiagnoser] +public class EndToEndBenchmark { - [MemoryDiagnoser] - public class EndToEndBenchmark + private readonly Fixture autoFixture = new(); + private const string Host = "https://github.com"; + private SystemTextJsonContentSerializer systemTextJsonContentSerializer; + private NewtonsoftJsonContentSerializer newtonsoftJsonContentSerializer; + private readonly IDictionary> users = + new Dictionary>(); + private readonly IDictionary< + SerializationStrategy, + IDictionary + > refitClient = new Dictionary< + SerializationStrategy, + IDictionary + > { - private readonly Fixture autoFixture = new(); - private const string Host = "https://github.com"; - private SystemTextJsonContentSerializer systemTextJsonContentSerializer; - private NewtonsoftJsonContentSerializer newtonsoftJsonContentSerializer; - private readonly IDictionary> users = - new Dictionary>(); - private readonly IDictionary< - SerializationStrategy, - IDictionary - > refitClient = new Dictionary< - SerializationStrategy, - IDictionary - > { - { - SerializationStrategy.SystemTextJson, - new Dictionary() - }, - { - SerializationStrategy.NewtonsoftJson, - new Dictionary() - } - }; - - private readonly IDictionary httpMethod = new Dictionary< - HttpVerb, - HttpMethod - > + SerializationStrategy.SystemTextJson, + new Dictionary() + }, { - { HttpVerb.Get, HttpMethod.Get }, - { HttpVerb.Post, HttpMethod.Post } - }; + SerializationStrategy.NewtonsoftJson, + new Dictionary() + } + }; - private const int TenUsers = 10; + private readonly IDictionary httpMethod = new Dictionary< + HttpVerb, + HttpMethod + > + { + { HttpVerb.Get, HttpMethod.Get }, + { HttpVerb.Post, HttpMethod.Post } + }; - public enum SerializationStrategy - { - SystemTextJson, - NewtonsoftJson - } + private const int TenUsers = 10; - public enum HttpVerb - { - Get, - Post - } + public enum SerializationStrategy + { + SystemTextJson, + NewtonsoftJson + } - [GlobalSetup] - public Task SetupAsync() - { - systemTextJsonContentSerializer = new SystemTextJsonContentSerializer(); - refitClient[SerializationStrategy.SystemTextJson][HttpStatusCode.OK] = - RestService.For( - Host, - new RefitSettings(systemTextJsonContentSerializer) - { - HttpMessageHandlerFactory = () => - new StaticFileHttpResponseHandler( - "system-text-json-10-users.json", - HttpStatusCode.OK - ) - } - ); - refitClient[SerializationStrategy.SystemTextJson][HttpStatusCode.InternalServerError] = - RestService.For( - Host, - new RefitSettings(systemTextJsonContentSerializer) - { - HttpMessageHandlerFactory = () => - new StaticFileHttpResponseHandler( - "system-text-json-10-users.json", - HttpStatusCode.InternalServerError - ) - } - ); + public enum HttpVerb + { + Get, + Post + } - newtonsoftJsonContentSerializer = new NewtonsoftJsonContentSerializer(); - refitClient[SerializationStrategy.NewtonsoftJson][HttpStatusCode.OK] = - RestService.For( - Host, - new RefitSettings(newtonsoftJsonContentSerializer) - { - HttpMessageHandlerFactory = () => - new StaticFileHttpResponseHandler( - "newtonsoft-json-10-users.json", - System.Net.HttpStatusCode.OK - ) - } - ); - refitClient[SerializationStrategy.NewtonsoftJson][HttpStatusCode.InternalServerError] = - RestService.For( - Host, - new RefitSettings(newtonsoftJsonContentSerializer) - { - HttpMessageHandlerFactory = () => - new StaticFileHttpResponseHandler( - "newtonsoft-json-10-users.json", - System.Net.HttpStatusCode.InternalServerError - ) - } - ); + [GlobalSetup] + public Task SetupAsync() + { + systemTextJsonContentSerializer = new SystemTextJsonContentSerializer(); + refitClient[SerializationStrategy.SystemTextJson][HttpStatusCode.OK] = + RestService.For( + Host, + new RefitSettings(systemTextJsonContentSerializer) + { + HttpMessageHandlerFactory = () => + new StaticFileHttpResponseHandler( + "system-text-json-10-users.json", + HttpStatusCode.OK + ) + } + ); + refitClient[SerializationStrategy.SystemTextJson][HttpStatusCode.InternalServerError] = + RestService.For( + Host, + new RefitSettings(systemTextJsonContentSerializer) + { + HttpMessageHandlerFactory = () => + new StaticFileHttpResponseHandler( + "system-text-json-10-users.json", + HttpStatusCode.InternalServerError + ) + } + ); - users[TenUsers] = autoFixture.CreateMany(TenUsers); + newtonsoftJsonContentSerializer = new NewtonsoftJsonContentSerializer(); + refitClient[SerializationStrategy.NewtonsoftJson][HttpStatusCode.OK] = + RestService.For( + Host, + new RefitSettings(newtonsoftJsonContentSerializer) + { + HttpMessageHandlerFactory = () => + new StaticFileHttpResponseHandler( + "newtonsoft-json-10-users.json", + System.Net.HttpStatusCode.OK + ) + } + ); + refitClient[SerializationStrategy.NewtonsoftJson][HttpStatusCode.InternalServerError] = + RestService.For( + Host, + new RefitSettings(newtonsoftJsonContentSerializer) + { + HttpMessageHandlerFactory = () => + new StaticFileHttpResponseHandler( + "newtonsoft-json-10-users.json", + System.Net.HttpStatusCode.InternalServerError + ) + } + ); - return Task.CompletedTask; - } + users[TenUsers] = autoFixture.CreateMany(TenUsers); + + return Task.CompletedTask; + } - /* - * Each [Benchmark] tests one return type that Refit allows and is parameterized to test different, serializers, and http methods, and status codes - */ + /* + * Each [Benchmark] tests one return type that Refit allows and is parameterized to test different, serializers, and http methods, and status codes + */ - [Params(HttpStatusCode.OK, HttpStatusCode.InternalServerError)] - public HttpStatusCode HttpStatusCode { get; set; } + [Params(HttpStatusCode.OK, HttpStatusCode.InternalServerError)] + public HttpStatusCode HttpStatusCode { get; set; } - [Params(TenUsers)] - public int ModelCount { get; set; } + [Params(TenUsers)] + public int ModelCount { get; set; } - [ParamsAllValues] - public HttpVerb Verb { get; set; } + [ParamsAllValues] + public HttpVerb Verb { get; set; } - [ParamsAllValues] - public SerializationStrategy Serializer { get; set; } + [ParamsAllValues] + public SerializationStrategy Serializer { get; set; } - [Benchmark] - public async Task Task_Async() + [Benchmark] + public async Task Task_Async() + { + try { - try - { - switch (Verb) - { - case HttpVerb.Get: - await refitClient[Serializer][HttpStatusCode].GetUsersTaskAsync(); - break; - case HttpVerb.Post: - await refitClient[Serializer] - [HttpStatusCode] - .PostUsersTaskAsync(users[ModelCount]); - break; - default: - throw new ArgumentOutOfRangeException(nameof(Verb)); - } - } - catch + switch (Verb) { - //swallow + case HttpVerb.Get: + await refitClient[Serializer][HttpStatusCode].GetUsersTaskAsync(); + break; + case HttpVerb.Post: + await refitClient[Serializer] + [HttpStatusCode] + .PostUsersTaskAsync(users[ModelCount]); + break; + default: + throw new ArgumentOutOfRangeException(nameof(Verb)); } } - - [Benchmark] - public async Task TaskString_Async() + catch { - try - { - switch (Verb) - { - case HttpVerb.Get: - return await refitClient[Serializer] - [HttpStatusCode] - .GetUsersTaskStringAsync(); - case HttpVerb.Post: - return await refitClient[Serializer] - [HttpStatusCode] - .PostUsersTaskStringAsync(users[ModelCount]); - default: - throw new ArgumentOutOfRangeException(nameof(Verb)); - } - } - catch - { - //swallow - } - - return default; + //swallow } + } - [Benchmark] - public async Task TaskStream_Async() + [Benchmark] + public async Task TaskString_Async() + { + try { - try - { - switch (Verb) - { - case HttpVerb.Get: - return await refitClient[Serializer] - [HttpStatusCode] - .GetUsersTaskStreamAsync(); - case HttpVerb.Post: - return await refitClient[Serializer] - [HttpStatusCode] - .PostUsersTaskStreamAsync(users[ModelCount]); - default: - throw new ArgumentOutOfRangeException(nameof(Verb)); - } - } - catch + switch (Verb) { - //swallow + case HttpVerb.Get: + return await refitClient[Serializer] + [HttpStatusCode] + .GetUsersTaskStringAsync(); + case HttpVerb.Post: + return await refitClient[Serializer] + [HttpStatusCode] + .PostUsersTaskStringAsync(users[ModelCount]); + default: + throw new ArgumentOutOfRangeException(nameof(Verb)); } - - return default; } - - [Benchmark] - public async Task TaskHttpContent_Async() + catch { - try - { - switch (Verb) - { - case HttpVerb.Get: - return await refitClient[Serializer] - [HttpStatusCode] - .GetUsersTaskHttpContentAsync(); - case HttpVerb.Post: - return await refitClient[Serializer] - [HttpStatusCode] - .PostUsersTaskHttpContentAsync(users[ModelCount]); - default: - throw new ArgumentOutOfRangeException(nameof(Verb)); - } - } - catch - { - //swallow - } - - return default; + //swallow } - [Benchmark] - public async Task TaskHttpResponseMessage_Async() + return default; + } + + [Benchmark] + public async Task TaskStream_Async() + { + try { switch (Verb) { case HttpVerb.Get: return await refitClient[Serializer] [HttpStatusCode] - .GetUsersTaskHttpResponseMessageAsync(); + .GetUsersTaskStreamAsync(); case HttpVerb.Post: return await refitClient[Serializer] [HttpStatusCode] - .PostUsersTaskHttpResponseMessageAsync(users[ModelCount]); + .PostUsersTaskStreamAsync(users[ModelCount]); default: throw new ArgumentOutOfRangeException(nameof(Verb)); } } + catch + { + //swallow + } + + return default; + } - [Benchmark] - public IObservable ObservableHttpResponseMessage() + [Benchmark] + public async Task TaskHttpContent_Async() + { + try { switch (Verb) { case HttpVerb.Get: - return refitClient[Serializer] + return await refitClient[Serializer] [HttpStatusCode] - .GetUsersObservableHttpResponseMessage(); + .GetUsersTaskHttpContentAsync(); case HttpVerb.Post: - return refitClient[Serializer] + return await refitClient[Serializer] [HttpStatusCode] - .PostUsersObservableHttpResponseMessage(users[ModelCount]); + .PostUsersTaskHttpContentAsync(users[ModelCount]); default: throw new ArgumentOutOfRangeException(nameof(Verb)); } } + catch + { + //swallow + } + + return default; + } - [Benchmark] - public async Task> TaskT_Async() + [Benchmark] + public async Task TaskHttpResponseMessage_Async() + { + switch (Verb) { - try - { - switch (Verb) - { - case HttpVerb.Get: - return await refitClient[Serializer][HttpStatusCode].GetUsersTaskTAsync(); - case HttpVerb.Post: - return await refitClient[Serializer] - [HttpStatusCode] - .PostUsersTaskTAsync(users[ModelCount]); - default: - throw new ArgumentOutOfRangeException(nameof(Verb)); - } - } - catch - { - //swallow - } + case HttpVerb.Get: + return await refitClient[Serializer] + [HttpStatusCode] + .GetUsersTaskHttpResponseMessageAsync(); + case HttpVerb.Post: + return await refitClient[Serializer] + [HttpStatusCode] + .PostUsersTaskHttpResponseMessageAsync(users[ModelCount]); + default: + throw new ArgumentOutOfRangeException(nameof(Verb)); + } + } - return default; + [Benchmark] + public IObservable ObservableHttpResponseMessage() + { + switch (Verb) + { + case HttpVerb.Get: + return refitClient[Serializer] + [HttpStatusCode] + .GetUsersObservableHttpResponseMessage(); + case HttpVerb.Post: + return refitClient[Serializer] + [HttpStatusCode] + .PostUsersObservableHttpResponseMessage(users[ModelCount]); + default: + throw new ArgumentOutOfRangeException(nameof(Verb)); } + } - [Benchmark] - public async Task>> TaskApiResponseT_Async() + [Benchmark] + public async Task> TaskT_Async() + { + try { switch (Verb) { case HttpVerb.Get: - return await refitClient[Serializer] - [HttpStatusCode] - .GetUsersTaskApiResponseTAsync(); + return await refitClient[Serializer][HttpStatusCode].GetUsersTaskTAsync(); case HttpVerb.Post: return await refitClient[Serializer] [HttpStatusCode] - .PostUsersTaskApiResponseTAsync(users[ModelCount]); + .PostUsersTaskTAsync(users[ModelCount]); default: throw new ArgumentOutOfRangeException(nameof(Verb)); } } + catch + { + //swallow + } + + return default; + } + + [Benchmark] + public async Task>> TaskApiResponseT_Async() + { + switch (Verb) + { + case HttpVerb.Get: + return await refitClient[Serializer] + [HttpStatusCode] + .GetUsersTaskApiResponseTAsync(); + case HttpVerb.Post: + return await refitClient[Serializer] + [HttpStatusCode] + .PostUsersTaskApiResponseTAsync(users[ModelCount]); + default: + throw new ArgumentOutOfRangeException(nameof(Verb)); + } } } diff --git a/Refit.Benchmarks/IGitHubService.cs b/Refit.Benchmarks/IGitHubService.cs index 50799df78..074074cd8 100644 --- a/Refit.Benchmarks/IGitHubService.cs +++ b/Refit.Benchmarks/IGitHubService.cs @@ -1,77 +1,76 @@ -namespace Refit.Benchmarks +namespace Refit.Benchmarks; + +public interface IGitHubService { - public interface IGitHubService - { - //Task - throws - [Get("/users")] - public Task GetUsersTaskAsync(); - - [Post("/users")] - public Task PostUsersTaskAsync([Body] IEnumerable users); - - //Task - throws - [Get("/users")] - public Task GetUsersTaskStringAsync(); - - [Post("/users")] - public Task PostUsersTaskStringAsync([Body] IEnumerable users); - - //Task - throws - [Get("/users")] - public Task GetUsersTaskStreamAsync(); - - [Post("/users")] - public Task PostUsersTaskStreamAsync([Body] IEnumerable users); - - //Task - throws - [Get("/users")] - public Task GetUsersTaskHttpContentAsync(); - - [Post("/users")] - public Task PostUsersTaskHttpContentAsync([Body] IEnumerable users); - - //Task - [Get("/users")] - public Task GetUsersTaskHttpResponseMessageAsync(); - - [Post("/users")] - public Task PostUsersTaskHttpResponseMessageAsync( - [Body] IEnumerable users - ); - - //IObservable - [Get("/users")] - public IObservable GetUsersObservableHttpResponseMessage(); - - [Post("/users")] - public IObservable PostUsersObservableHttpResponseMessage( - [Body] IEnumerable users - ); - - //Task<> - throws - [Get("/users")] - public Task> GetUsersTaskTAsync(); - - [Post("/users")] - public Task> PostUsersTaskTAsync([Body] IEnumerable users); - - //Task> - [Get("/users")] - public Task>> GetUsersTaskApiResponseTAsync(); - - [Post("/users")] - public Task>> PostUsersTaskApiResponseTAsync( - [Body] IEnumerable users - ); - } - - public class User - { - public int Id { get; set; } - public string Name { get; set; } - public string Bio { get; set; } - public int Followers { get; set; } - public int Following { get; set; } - public string Url { get; set; } - } + //Task - throws + [Get("/users")] + public Task GetUsersTaskAsync(); + + [Post("/users")] + public Task PostUsersTaskAsync([Body] IEnumerable users); + + //Task - throws + [Get("/users")] + public Task GetUsersTaskStringAsync(); + + [Post("/users")] + public Task PostUsersTaskStringAsync([Body] IEnumerable users); + + //Task - throws + [Get("/users")] + public Task GetUsersTaskStreamAsync(); + + [Post("/users")] + public Task PostUsersTaskStreamAsync([Body] IEnumerable users); + + //Task - throws + [Get("/users")] + public Task GetUsersTaskHttpContentAsync(); + + [Post("/users")] + public Task PostUsersTaskHttpContentAsync([Body] IEnumerable users); + + //Task + [Get("/users")] + public Task GetUsersTaskHttpResponseMessageAsync(); + + [Post("/users")] + public Task PostUsersTaskHttpResponseMessageAsync( + [Body] IEnumerable users + ); + + //IObservable + [Get("/users")] + public IObservable GetUsersObservableHttpResponseMessage(); + + [Post("/users")] + public IObservable PostUsersObservableHttpResponseMessage( + [Body] IEnumerable users + ); + + //Task<> - throws + [Get("/users")] + public Task> GetUsersTaskTAsync(); + + [Post("/users")] + public Task> PostUsersTaskTAsync([Body] IEnumerable users); + + //Task> + [Get("/users")] + public Task>> GetUsersTaskApiResponseTAsync(); + + [Post("/users")] + public Task>> PostUsersTaskApiResponseTAsync( + [Body] IEnumerable users + ); } + +public class User +{ + public int Id { get; set; } + public string Name { get; set; } + public string Bio { get; set; } + public int Followers { get; set; } + public int Following { get; set; } + public string Url { get; set; } +} \ No newline at end of file diff --git a/Refit.Benchmarks/StaticFileHttpResponseHandler.cs b/Refit.Benchmarks/StaticFileHttpResponseHandler.cs index b195741fa..bed1ac0fe 100644 --- a/Refit.Benchmarks/StaticFileHttpResponseHandler.cs +++ b/Refit.Benchmarks/StaticFileHttpResponseHandler.cs @@ -1,14 +1,14 @@ using System.Net; -namespace Refit.Benchmarks +namespace Refit.Benchmarks; + +public class StaticFileHttpResponseHandler : HttpMessageHandler { - public class StaticFileHttpResponseHandler : HttpMessageHandler - { - private readonly HttpStatusCode responseCode; - private readonly string responsePayload; + private readonly HttpStatusCode responseCode; + private readonly string responsePayload; - public StaticFileHttpResponseHandler(string fileName, HttpStatusCode responseCode) - { + public StaticFileHttpResponseHandler(string fileName, HttpStatusCode responseCode) + { if (string.IsNullOrEmpty(fileName)) throw new ArgumentNullException(nameof(fileName)); @@ -17,11 +17,11 @@ public StaticFileHttpResponseHandler(string fileName, HttpStatusCode responseCod this.responseCode = responseCode; } - protected override Task SendAsync( - HttpRequestMessage request, - CancellationToken cancellationToken - ) - { + protected override Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken + ) + { return Task.FromResult( new HttpResponseMessage(responseCode) { @@ -30,5 +30,4 @@ CancellationToken cancellationToken } ); } - } -} +} \ No newline at end of file From 8120a95502781bf74db935112c981946fbb55460 Mon Sep 17 00:00:00 2001 From: Timothy Makkison Date: Sun, 23 Jun 2024 12:20:30 +0100 Subject: [PATCH 2/3] feat: add performance and startup benchmarks --- Refit.Benchmarks/IPerformanceApi.cs | 28 +++++++++++ Refit.Benchmarks/PerformanceBenchmark.cs | 48 +++++++++++++++++++ Refit.Benchmarks/Program.cs | 24 ++++------ Refit.Benchmarks/StartupBenchmark.cs | 36 ++++++++++++++ .../StaticValueHttpResponseHandler.cs | 20 ++++++++ 5 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 Refit.Benchmarks/IPerformanceApi.cs create mode 100644 Refit.Benchmarks/PerformanceBenchmark.cs create mode 100644 Refit.Benchmarks/StartupBenchmark.cs create mode 100644 Refit.Benchmarks/StaticValueHttpResponseHandler.cs diff --git a/Refit.Benchmarks/IPerformanceApi.cs b/Refit.Benchmarks/IPerformanceApi.cs new file mode 100644 index 000000000..09ea2266b --- /dev/null +++ b/Refit.Benchmarks/IPerformanceApi.cs @@ -0,0 +1,28 @@ +namespace Refit.Benchmarks; + +public interface IPerformanceApi +{ + [Get("/users")] + public Task ConstantRoute(); + + [Get("/users/{id}")] + public Task DynamicRoute(int id); + + [Get("/users/{id}/{user}/{status}")] + public Task ComplexDynamicRoute(int id, string user, string status); + + [Get("/users/{request.someProperty}")] + public Task ObjectRequest(PathBoundObject request); + + [Post("/users/{id}/{request.someProperty}")] + [Headers("User-Agent: Awesome Octocat App", "X-Emoji: :smile_cat:")] + public Task ComplexRequest(int id, PathBoundObject request, [Query(CollectionFormat.Multi)]int[] queries); +} + +public class PathBoundObject +{ + public string SomeProperty { get; set; } + + [Query] + public string SomeQuery { get; set; } +} diff --git a/Refit.Benchmarks/PerformanceBenchmark.cs b/Refit.Benchmarks/PerformanceBenchmark.cs new file mode 100644 index 000000000..4af89eb07 --- /dev/null +++ b/Refit.Benchmarks/PerformanceBenchmark.cs @@ -0,0 +1,48 @@ +using System.Net; +using BenchmarkDotNet.Attributes; + +namespace Refit.Benchmarks; + +[MemoryDiagnoser] +public class PerformanceBenchmark +{ + private IPerformanceApi? service; + + private const string Host = "https://github.com"; + private SystemTextJsonContentSerializer systemTextJsonContentSerializer; + + [GlobalSetup] + public Task SetupAsync() + { + systemTextJsonContentSerializer = new SystemTextJsonContentSerializer(); + service = + RestService.For( + Host, + new RefitSettings(systemTextJsonContentSerializer) + { + HttpMessageHandlerFactory = () => + new StaticValueHttpResponseHandler( + "Ok", + HttpStatusCode.OK + ) + } + ); + + return Task.CompletedTask; + } + + [Benchmark] + public async Task ConstantRouteAsync() => await service.ConstantRoute(); + + [Benchmark] + public async Task DynamicRouteAsync() => await service.DynamicRoute(101); + + [Benchmark] + public async Task ComplexDynamicRouteAsync() => await service.ComplexDynamicRoute(101, "tom", "yCxv"); + + [Benchmark] + public async Task ObjectRequestAsync() => await service.ObjectRequest(new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"}); + + [Benchmark] + public async Task ComplexRequestAsync() => await service.ComplexRequest(101, new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"}, [1,2,3,4,5,6]); +} diff --git a/Refit.Benchmarks/Program.cs b/Refit.Benchmarks/Program.cs index e34f8661e..1383cae13 100644 --- a/Refit.Benchmarks/Program.cs +++ b/Refit.Benchmarks/Program.cs @@ -1,19 +1,13 @@ using BenchmarkDotNet.Running; +using Refit.Benchmarks; -namespace Refit.Benchmarks +if (args is { Length: > 0 }) { - class Program - { - static void Main(string[] args) - { - if (args != null && args.Length > 0) - { - BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); - } - else - { - BenchmarkRunner.Run(); - } - } - } + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); +} +else +{ + BenchmarkRunner.Run(); + // BenchmarkRunner.Run(); + // BenchmarkRunner.Run(); } diff --git a/Refit.Benchmarks/StartupBenchmark.cs b/Refit.Benchmarks/StartupBenchmark.cs new file mode 100644 index 000000000..12e865234 --- /dev/null +++ b/Refit.Benchmarks/StartupBenchmark.cs @@ -0,0 +1,36 @@ +using System.Net; +using BenchmarkDotNet.Attributes; + +namespace Refit.Benchmarks; + +[MemoryDiagnoser] +public class StartupBenchmark +{ + private const string Host = "https://github.com"; + private readonly RefitSettings settings = new RefitSettings() + { + HttpMessageHandlerFactory = () => + new StaticValueHttpResponseHandler( + "Ok", + HttpStatusCode.OK + ) + }; + + [Benchmark] + public IPerformanceApi CreateService() => RestService.For(Host, settings); + + [Benchmark] + public async Task ConstantRouteAsync() + { + var service = RestService.For(Host, settings); + return await service.ConstantRoute(); + } + + + [Benchmark] + public async Task ComplexRequestAsync() + { + var service = RestService.For(Host, settings); + return await service.ObjectRequest(new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"}); + } +} diff --git a/Refit.Benchmarks/StaticValueHttpResponseHandler.cs b/Refit.Benchmarks/StaticValueHttpResponseHandler.cs new file mode 100644 index 000000000..6b5e9b972 --- /dev/null +++ b/Refit.Benchmarks/StaticValueHttpResponseHandler.cs @@ -0,0 +1,20 @@ +using System.Net; + +namespace Refit.Benchmarks; + +public class StaticValueHttpResponseHandler (string response, HttpStatusCode code) : HttpMessageHandler +{ + protected override Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken + ) + { + return Task.FromResult( + new HttpResponseMessage(code) + { + RequestMessage = request, + Content = new StringContent(response) + } + ); + } +} From a17f0b4f468cdd8363ac2028510bc3634a0275e3 Mon Sep 17 00:00:00 2001 From: Timothy Makkison Date: Sun, 23 Jun 2024 12:54:59 +0100 Subject: [PATCH 3/3] feat: add first call benchmarks --- ...rformanceApi.cs => IPerformanceService.cs} | 2 +- Refit.Benchmarks/PerformanceBenchmark.cs | 4 ++-- Refit.Benchmarks/StartupBenchmark.cs | 19 ++++++++++++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) rename Refit.Benchmarks/{IPerformanceApi.cs => IPerformanceService.cs} (95%) diff --git a/Refit.Benchmarks/IPerformanceApi.cs b/Refit.Benchmarks/IPerformanceService.cs similarity index 95% rename from Refit.Benchmarks/IPerformanceApi.cs rename to Refit.Benchmarks/IPerformanceService.cs index 09ea2266b..36b7036a8 100644 --- a/Refit.Benchmarks/IPerformanceApi.cs +++ b/Refit.Benchmarks/IPerformanceService.cs @@ -1,6 +1,6 @@ namespace Refit.Benchmarks; -public interface IPerformanceApi +public interface IPerformanceService { [Get("/users")] public Task ConstantRoute(); diff --git a/Refit.Benchmarks/PerformanceBenchmark.cs b/Refit.Benchmarks/PerformanceBenchmark.cs index 4af89eb07..d34d912bb 100644 --- a/Refit.Benchmarks/PerformanceBenchmark.cs +++ b/Refit.Benchmarks/PerformanceBenchmark.cs @@ -6,7 +6,7 @@ namespace Refit.Benchmarks; [MemoryDiagnoser] public class PerformanceBenchmark { - private IPerformanceApi? service; + private IPerformanceService? service; private const string Host = "https://github.com"; private SystemTextJsonContentSerializer systemTextJsonContentSerializer; @@ -16,7 +16,7 @@ public Task SetupAsync() { systemTextJsonContentSerializer = new SystemTextJsonContentSerializer(); service = - RestService.For( + RestService.For( Host, new RefitSettings(systemTextJsonContentSerializer) { diff --git a/Refit.Benchmarks/StartupBenchmark.cs b/Refit.Benchmarks/StartupBenchmark.cs index 12e865234..babd94bb2 100644 --- a/Refit.Benchmarks/StartupBenchmark.cs +++ b/Refit.Benchmarks/StartupBenchmark.cs @@ -6,6 +6,7 @@ namespace Refit.Benchmarks; [MemoryDiagnoser] public class StartupBenchmark { + private IPerformanceService initialisedService; private const string Host = "https://github.com"; private readonly RefitSettings settings = new RefitSettings() { @@ -16,21 +17,33 @@ public class StartupBenchmark ) }; + + [IterationSetup(Targets = [nameof(FirstCallConstantRouteAsync), nameof(FirstCallComplexRequestAsync)])] + public void Setup() + { + initialisedService = RestService.For(Host, settings); + } + [Benchmark] - public IPerformanceApi CreateService() => RestService.For(Host, settings); + public IPerformanceService CreateService() => RestService.For(Host, settings); + + [Benchmark] + public async Task FirstCallConstantRouteAsync() => await initialisedService.ConstantRoute(); [Benchmark] public async Task ConstantRouteAsync() { - var service = RestService.For(Host, settings); + var service = RestService.For(Host, settings); return await service.ConstantRoute(); } + [Benchmark] + public async Task FirstCallComplexRequestAsync() => await initialisedService.ObjectRequest(new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"}); [Benchmark] public async Task ComplexRequestAsync() { - var service = RestService.For(Host, settings); + var service = RestService.For(Host, settings); return await service.ObjectRequest(new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"}); } }