Skip to content

Commit 8a40692

Browse files
feat: add startup and performance benchmarks (#1731)
* chore: use file scoped namespaces * feat: add performance and startup benchmarks * feat: add first call benchmarks
1 parent b75734a commit 8a40692

File tree

8 files changed

+480
-344
lines changed

8 files changed

+480
-344
lines changed

Refit.Benchmarks/EndToEndBenchmark.cs

Lines changed: 239 additions & 240 deletions
Large diffs are not rendered by default.

Refit.Benchmarks/IGitHubService.cs

Lines changed: 74 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,76 @@
1-
namespace Refit.Benchmarks
1+
namespace Refit.Benchmarks;
2+
3+
public interface IGitHubService
24
{
3-
public interface IGitHubService
4-
{
5-
//Task - throws
6-
[Get("/users")]
7-
public Task GetUsersTaskAsync();
8-
9-
[Post("/users")]
10-
public Task PostUsersTaskAsync([Body] IEnumerable<User> users);
11-
12-
//Task<string> - throws
13-
[Get("/users")]
14-
public Task<string> GetUsersTaskStringAsync();
15-
16-
[Post("/users")]
17-
public Task<string> PostUsersTaskStringAsync([Body] IEnumerable<User> users);
18-
19-
//Task<Stream> - throws
20-
[Get("/users")]
21-
public Task<Stream> GetUsersTaskStreamAsync();
22-
23-
[Post("/users")]
24-
public Task<Stream> PostUsersTaskStreamAsync([Body] IEnumerable<User> users);
25-
26-
//Task<HttpContent> - throws
27-
[Get("/users")]
28-
public Task<HttpContent> GetUsersTaskHttpContentAsync();
29-
30-
[Post("/users")]
31-
public Task<HttpContent> PostUsersTaskHttpContentAsync([Body] IEnumerable<User> users);
32-
33-
//Task<HttpResponseMessage>
34-
[Get("/users")]
35-
public Task<HttpResponseMessage> GetUsersTaskHttpResponseMessageAsync();
36-
37-
[Post("/users")]
38-
public Task<HttpResponseMessage> PostUsersTaskHttpResponseMessageAsync(
39-
[Body] IEnumerable<User> users
40-
);
41-
42-
//IObservable<HttpResponseMessage>
43-
[Get("/users")]
44-
public IObservable<HttpResponseMessage> GetUsersObservableHttpResponseMessage();
45-
46-
[Post("/users")]
47-
public IObservable<HttpResponseMessage> PostUsersObservableHttpResponseMessage(
48-
[Body] IEnumerable<User> users
49-
);
50-
51-
//Task<<T>> - throws
52-
[Get("/users")]
53-
public Task<List<User>> GetUsersTaskTAsync();
54-
55-
[Post("/users")]
56-
public Task<List<User>> PostUsersTaskTAsync([Body] IEnumerable<User> users);
57-
58-
//Task<ApiResponse<T>>
59-
[Get("/users")]
60-
public Task<ApiResponse<List<User>>> GetUsersTaskApiResponseTAsync();
61-
62-
[Post("/users")]
63-
public Task<ApiResponse<List<User>>> PostUsersTaskApiResponseTAsync(
64-
[Body] IEnumerable<User> users
65-
);
66-
}
67-
68-
public class User
69-
{
70-
public int Id { get; set; }
71-
public string Name { get; set; }
72-
public string Bio { get; set; }
73-
public int Followers { get; set; }
74-
public int Following { get; set; }
75-
public string Url { get; set; }
76-
}
5+
//Task - throws
6+
[Get("/users")]
7+
public Task GetUsersTaskAsync();
8+
9+
[Post("/users")]
10+
public Task PostUsersTaskAsync([Body] IEnumerable<User> users);
11+
12+
//Task<string> - throws
13+
[Get("/users")]
14+
public Task<string> GetUsersTaskStringAsync();
15+
16+
[Post("/users")]
17+
public Task<string> PostUsersTaskStringAsync([Body] IEnumerable<User> users);
18+
19+
//Task<Stream> - throws
20+
[Get("/users")]
21+
public Task<Stream> GetUsersTaskStreamAsync();
22+
23+
[Post("/users")]
24+
public Task<Stream> PostUsersTaskStreamAsync([Body] IEnumerable<User> users);
25+
26+
//Task<HttpContent> - throws
27+
[Get("/users")]
28+
public Task<HttpContent> GetUsersTaskHttpContentAsync();
29+
30+
[Post("/users")]
31+
public Task<HttpContent> PostUsersTaskHttpContentAsync([Body] IEnumerable<User> users);
32+
33+
//Task<HttpResponseMessage>
34+
[Get("/users")]
35+
public Task<HttpResponseMessage> GetUsersTaskHttpResponseMessageAsync();
36+
37+
[Post("/users")]
38+
public Task<HttpResponseMessage> PostUsersTaskHttpResponseMessageAsync(
39+
[Body] IEnumerable<User> users
40+
);
41+
42+
//IObservable<HttpResponseMessage>
43+
[Get("/users")]
44+
public IObservable<HttpResponseMessage> GetUsersObservableHttpResponseMessage();
45+
46+
[Post("/users")]
47+
public IObservable<HttpResponseMessage> PostUsersObservableHttpResponseMessage(
48+
[Body] IEnumerable<User> users
49+
);
50+
51+
//Task<<T>> - throws
52+
[Get("/users")]
53+
public Task<List<User>> GetUsersTaskTAsync();
54+
55+
[Post("/users")]
56+
public Task<List<User>> PostUsersTaskTAsync([Body] IEnumerable<User> users);
57+
58+
//Task<ApiResponse<T>>
59+
[Get("/users")]
60+
public Task<ApiResponse<List<User>>> GetUsersTaskApiResponseTAsync();
61+
62+
[Post("/users")]
63+
public Task<ApiResponse<List<User>>> PostUsersTaskApiResponseTAsync(
64+
[Body] IEnumerable<User> users
65+
);
7766
}
67+
68+
public class User
69+
{
70+
public int Id { get; set; }
71+
public string Name { get; set; }
72+
public string Bio { get; set; }
73+
public int Followers { get; set; }
74+
public int Following { get; set; }
75+
public string Url { get; set; }
76+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace Refit.Benchmarks;
2+
3+
public interface IPerformanceService
4+
{
5+
[Get("/users")]
6+
public Task<string> ConstantRoute();
7+
8+
[Get("/users/{id}")]
9+
public Task<string> DynamicRoute(int id);
10+
11+
[Get("/users/{id}/{user}/{status}")]
12+
public Task<string> ComplexDynamicRoute(int id, string user, string status);
13+
14+
[Get("/users/{request.someProperty}")]
15+
public Task<string> ObjectRequest(PathBoundObject request);
16+
17+
[Post("/users/{id}/{request.someProperty}")]
18+
[Headers("User-Agent: Awesome Octocat App", "X-Emoji: :smile_cat:")]
19+
public Task<string> ComplexRequest(int id, PathBoundObject request, [Query(CollectionFormat.Multi)]int[] queries);
20+
}
21+
22+
public class PathBoundObject
23+
{
24+
public string SomeProperty { get; set; }
25+
26+
[Query]
27+
public string SomeQuery { get; set; }
28+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System.Net;
2+
using BenchmarkDotNet.Attributes;
3+
4+
namespace Refit.Benchmarks;
5+
6+
[MemoryDiagnoser]
7+
public class PerformanceBenchmark
8+
{
9+
private IPerformanceService? service;
10+
11+
private const string Host = "https://github.com";
12+
private SystemTextJsonContentSerializer systemTextJsonContentSerializer;
13+
14+
[GlobalSetup]
15+
public Task SetupAsync()
16+
{
17+
systemTextJsonContentSerializer = new SystemTextJsonContentSerializer();
18+
service =
19+
RestService.For<IPerformanceService>(
20+
Host,
21+
new RefitSettings(systemTextJsonContentSerializer)
22+
{
23+
HttpMessageHandlerFactory = () =>
24+
new StaticValueHttpResponseHandler(
25+
"Ok",
26+
HttpStatusCode.OK
27+
)
28+
}
29+
);
30+
31+
return Task.CompletedTask;
32+
}
33+
34+
[Benchmark]
35+
public async Task<string> ConstantRouteAsync() => await service.ConstantRoute();
36+
37+
[Benchmark]
38+
public async Task<string> DynamicRouteAsync() => await service.DynamicRoute(101);
39+
40+
[Benchmark]
41+
public async Task<string> ComplexDynamicRouteAsync() => await service.ComplexDynamicRoute(101, "tom", "yCxv");
42+
43+
[Benchmark]
44+
public async Task<string> ObjectRequestAsync() => await service.ObjectRequest(new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"});
45+
46+
[Benchmark]
47+
public async Task<string> ComplexRequestAsync() => await service.ComplexRequest(101, new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"}, [1,2,3,4,5,6]);
48+
}

Refit.Benchmarks/Program.cs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
using BenchmarkDotNet.Running;
2+
using Refit.Benchmarks;
23

3-
namespace Refit.Benchmarks
4+
if (args is { Length: > 0 })
45
{
5-
class Program
6-
{
7-
static void Main(string[] args)
8-
{
9-
if (args != null && args.Length > 0)
10-
{
11-
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
12-
}
13-
else
14-
{
15-
BenchmarkRunner.Run<EndToEndBenchmark>();
16-
}
17-
}
18-
}
6+
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
7+
}
8+
else
9+
{
10+
BenchmarkRunner.Run<EndToEndBenchmark>();
11+
// BenchmarkRunner.Run<StartupBenchmark>();
12+
// BenchmarkRunner.Run<PerformanceBenchmarks>();
1913
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System.Net;
2+
using BenchmarkDotNet.Attributes;
3+
4+
namespace Refit.Benchmarks;
5+
6+
[MemoryDiagnoser]
7+
public class StartupBenchmark
8+
{
9+
private IPerformanceService initialisedService;
10+
private const string Host = "https://github.com";
11+
private readonly RefitSettings settings = new RefitSettings()
12+
{
13+
HttpMessageHandlerFactory = () =>
14+
new StaticValueHttpResponseHandler(
15+
"Ok",
16+
HttpStatusCode.OK
17+
)
18+
};
19+
20+
21+
[IterationSetup(Targets = [nameof(FirstCallConstantRouteAsync), nameof(FirstCallComplexRequestAsync)])]
22+
public void Setup()
23+
{
24+
initialisedService = RestService.For<IPerformanceService>(Host, settings);
25+
}
26+
27+
[Benchmark]
28+
public IPerformanceService CreateService() => RestService.For<IPerformanceService>(Host, settings);
29+
30+
[Benchmark]
31+
public async Task<string> FirstCallConstantRouteAsync() => await initialisedService.ConstantRoute();
32+
33+
[Benchmark]
34+
public async Task<string> ConstantRouteAsync()
35+
{
36+
var service = RestService.For<IPerformanceService>(Host, settings);
37+
return await service.ConstantRoute();
38+
}
39+
40+
[Benchmark]
41+
public async Task<string> FirstCallComplexRequestAsync() => await initialisedService.ObjectRequest(new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"});
42+
43+
[Benchmark]
44+
public async Task<string> ComplexRequestAsync()
45+
{
46+
var service = RestService.For<IPerformanceService>(Host, settings);
47+
return await service.ObjectRequest(new PathBoundObject(){SomeProperty = "myProperty", SomeQuery = "myQuery"});
48+
}
49+
}
Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
using System.Net;
22

3-
namespace Refit.Benchmarks
3+
namespace Refit.Benchmarks;
4+
5+
public class StaticFileHttpResponseHandler : HttpMessageHandler
46
{
5-
public class StaticFileHttpResponseHandler : HttpMessageHandler
6-
{
7-
private readonly HttpStatusCode responseCode;
8-
private readonly string responsePayload;
7+
private readonly HttpStatusCode responseCode;
8+
private readonly string responsePayload;
99

10-
public StaticFileHttpResponseHandler(string fileName, HttpStatusCode responseCode)
11-
{
10+
public StaticFileHttpResponseHandler(string fileName, HttpStatusCode responseCode)
11+
{
1212
if (string.IsNullOrEmpty(fileName))
1313
throw new ArgumentNullException(nameof(fileName));
1414

@@ -17,11 +17,11 @@ public StaticFileHttpResponseHandler(string fileName, HttpStatusCode responseCod
1717
this.responseCode = responseCode;
1818
}
1919

20-
protected override Task<HttpResponseMessage> SendAsync(
21-
HttpRequestMessage request,
22-
CancellationToken cancellationToken
23-
)
24-
{
20+
protected override Task<HttpResponseMessage> SendAsync(
21+
HttpRequestMessage request,
22+
CancellationToken cancellationToken
23+
)
24+
{
2525
return Task.FromResult(
2626
new HttpResponseMessage(responseCode)
2727
{
@@ -30,5 +30,4 @@ CancellationToken cancellationToken
3030
}
3131
);
3232
}
33-
}
34-
}
33+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Net;
2+
3+
namespace Refit.Benchmarks;
4+
5+
public class StaticValueHttpResponseHandler (string response, HttpStatusCode code) : HttpMessageHandler
6+
{
7+
protected override Task<HttpResponseMessage> SendAsync(
8+
HttpRequestMessage request,
9+
CancellationToken cancellationToken
10+
)
11+
{
12+
return Task.FromResult(
13+
new HttpResponseMessage(code)
14+
{
15+
RequestMessage = request,
16+
Content = new StringContent(response)
17+
}
18+
);
19+
}
20+
}

0 commit comments

Comments
 (0)