Skip to content

Commit b9619ae

Browse files
martincostellomus65
andcommitted
Add benchmarks project
Add project for running benchmarks. Adapted from mus65@65bfd97. See domaindrivendev#3044. Co-Authored-By: mus65 <[email protected]>
1 parent d358e7b commit b9619ae

File tree

7 files changed

+214
-0
lines changed

7 files changed

+214
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
[Bb]in/
33
.vs/
44
.idea*
5+
BenchmarkDotNet.Artifacts*/
56
coverage
67
coverage.*
78
node_modules/

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project>
22
<ItemGroup>
33
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="4.2.2" />
4+
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
45
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
56
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
67
<PackageVersion Include="IdentityServer4" Version="3.1.4" />

Swashbuckle.AspNetCore.sln

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1010
.editorconfig = .editorconfig
1111
.gitattributes = .gitattributes
1212
.gitignore = .gitignore
13+
benchmark.ps1 = benchmark.ps1
1314
CONTRIBUTING.md = CONTRIBUTING.md
1415
Directory.Build.props = Directory.Build.props
1516
Directory.Build.targets = Directory.Build.targets
@@ -117,6 +118,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalAppWithHostedService
117118
EndProject
118119
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvcWithNullable", "test\WebSites\MvcWithNullable\MvcWithNullable.csproj", "{F88B6070-BE3C-45F9-978C-2ECBA9518C24}"
119120
EndProject
121+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{0C7326F1-F731-4CF9-8A98-80F39541D28F}"
122+
EndProject
123+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Swashbuckle.AspNetCore.Benchmarks", "perf\Swashbuckle.AspNetCore.Benchmarks\Swashbuckle.AspNetCore.Benchmarks.csproj", "{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}"
124+
EndProject
120125
Global
121126
GlobalSection(SolutionConfigurationPlatforms) = preSolution
122127
Debug|Any CPU = Debug|Any CPU
@@ -271,6 +276,10 @@ Global
271276
{F88B6070-BE3C-45F9-978C-2ECBA9518C24}.Debug|Any CPU.Build.0 = Debug|Any CPU
272277
{F88B6070-BE3C-45F9-978C-2ECBA9518C24}.Release|Any CPU.ActiveCfg = Release|Any CPU
273278
{F88B6070-BE3C-45F9-978C-2ECBA9518C24}.Release|Any CPU.Build.0 = Release|Any CPU
279+
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
280+
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}.Debug|Any CPU.Build.0 = Debug|Any CPU
281+
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}.Release|Any CPU.ActiveCfg = Release|Any CPU
282+
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}.Release|Any CPU.Build.0 = Release|Any CPU
274283
EndGlobalSection
275284
GlobalSection(SolutionProperties) = preSolution
276285
HideSolutionNode = FALSE
@@ -316,6 +325,7 @@ Global
316325
{07BB09CF-6C6F-4D00-A459-93586345C921} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB}
317326
{D06A88E8-6F42-4F40-943A-E266C0AE6EC9} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB}
318327
{F88B6070-BE3C-45F9-978C-2ECBA9518C24} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB}
328+
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13} = {0C7326F1-F731-4CF9-8A98-80F39541D28F}
319329
EndGlobalSection
320330
GlobalSection(ExtensibilityGlobals) = postSolution
321331
SolutionGuid = {36FC6A67-247D-4149-8EDD-79FFD1A75F51}

benchmark.ps1

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#! /usr/bin/env pwsh
2+
3+
#Requires -PSEdition Core
4+
#Requires -Version 7
5+
6+
param(
7+
[Parameter(Mandatory = $false)][string] $Framework = "net8.0",
8+
[Parameter(Mandatory = $false)][string] $Job = ""
9+
)
10+
11+
$ErrorActionPreference = "Stop"
12+
$ProgressPreference = "SilentlyContinue"
13+
14+
$benchmarks = (Join-Path $PSScriptRoot "perf" "Swashbuckle.AspNetCore.Benchmarks" "Swashbuckle.AspNetCore.Benchmarks.csproj")
15+
16+
$additionalArgs = @()
17+
18+
if (-Not [string]::IsNullOrEmpty($Job)) {
19+
$additionalArgs += "--job"
20+
$additionalArgs += $Job
21+
}
22+
23+
if (-Not [string]::IsNullOrEmpty(${env:GITHUB_SHA})) {
24+
$additionalArgs += "--exporters"
25+
$additionalArgs += "json"
26+
}
27+
28+
dotnet run --project $benchmarks --configuration "Release" --framework $Framework -- $additionalArgs --% --filter *
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
using BenchmarkDotNet.Running;
2+
3+
var switcher = new BenchmarkSwitcher(typeof(Program).Assembly);
4+
switcher.Run(args);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net8.0</TargetFramework>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<ProjectReference Include="..\..\src\Swashbuckle.AspNetCore.Annotations\Swashbuckle.AspNetCore.Annotations.csproj" />
8+
<ProjectReference Include="..\..\src\Swashbuckle.AspNetCore.SwaggerGen\Swashbuckle.AspNetCore.SwaggerGen.csproj" />
9+
<ProjectReference Include="..\..\src\Swashbuckle.AspNetCore.Swagger\Swashbuckle.AspNetCore.Swagger.csproj" />
10+
<ProjectReference Include="..\..\test\Swashbuckle.AspNetCore.SwaggerGen.Test\Swashbuckle.AspNetCore.SwaggerGen.Test.csproj" />
11+
</ItemGroup>
12+
<ItemGroup>
13+
<PackageReference Include="BenchmarkDotNet" />
14+
</ItemGroup>
15+
</Project>
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Reflection;
4+
using System.Xml;
5+
using System.Xml.XPath;
6+
using BenchmarkDotNet.Attributes;
7+
using Microsoft.AspNetCore.Mvc.ApiExplorer;
8+
using Microsoft.AspNetCore.Mvc.Controllers;
9+
using Microsoft.OpenApi.Models;
10+
using Swashbuckle.AspNetCore.SwaggerGen;
11+
using Swashbuckle.AspNetCore.SwaggerGen.Test;
12+
using Swashbuckle.AspNetCore.TestSupport;
13+
14+
namespace Swashbuckle.AspNetCore.Benchmarks;
15+
16+
[MemoryDiagnoser]
17+
public class XmlCommentsBenchmark
18+
{
19+
private XmlCommentsDocumentFilter _documentFilter;
20+
private OpenApiDocument _document;
21+
private DocumentFilterContext _documentFilterContext;
22+
23+
private XmlCommentsOperationFilter _operationFilter;
24+
private OpenApiOperation _operation;
25+
private OperationFilterContext _operationFilterContext;
26+
27+
private XmlCommentsParameterFilter _parameterFilter;
28+
private OpenApiParameter _parameter;
29+
private ParameterFilterContext _parameterFilterContext;
30+
31+
private XmlCommentsRequestBodyFilter _requestBodyFilter;
32+
private OpenApiRequestBody _requestBody;
33+
private RequestBodyFilterContext _requestBodyFilterContext;
34+
35+
private const int AddMemberCount = 10_000;
36+
37+
[GlobalSetup]
38+
public void Setup()
39+
{
40+
// Load XML
41+
XmlDocument xmlDocument;
42+
using (var xmlComments = File.OpenText($"{typeof(FakeControllerWithXmlComments).Assembly.GetName().Name}.xml"))
43+
{
44+
xmlDocument = new XmlDocument();
45+
xmlDocument.Load(xmlComments);
46+
}
47+
48+
// Append dummy members to XML document
49+
XPathNavigator navigator = xmlDocument.CreateNavigator()!;
50+
navigator.MoveToRoot();
51+
navigator.MoveToChild("doc", string.Empty);
52+
navigator.MoveToChild("members", string.Empty);
53+
54+
for (int i = 0; i < AddMemberCount; i++)
55+
{
56+
navigator.PrependChild(@$"<member name=""benchmark_{i}""></member>");
57+
}
58+
59+
using var xmlStream = new MemoryStream();
60+
xmlDocument.Save(xmlStream);
61+
xmlStream.Seek(0, SeekOrigin.Begin);
62+
var xPathDocument = new XPathDocument(xmlStream);
63+
64+
// Document
65+
_document = new OpenApiDocument();
66+
_documentFilterContext = new DocumentFilterContext(
67+
[
68+
new ApiDescription
69+
{
70+
ActionDescriptor = new ControllerActionDescriptor
71+
{
72+
ControllerTypeInfo = typeof(FakeControllerWithXmlComments).GetTypeInfo(),
73+
ControllerName = nameof(FakeControllerWithXmlComments),
74+
},
75+
},
76+
new ApiDescription
77+
{
78+
ActionDescriptor = new ControllerActionDescriptor
79+
{
80+
ControllerTypeInfo = typeof(FakeControllerWithXmlComments).GetTypeInfo(),
81+
ControllerName = nameof(FakeControllerWithXmlComments),
82+
},
83+
},
84+
],
85+
null,
86+
null);
87+
88+
_documentFilter = new XmlCommentsDocumentFilter(xPathDocument);
89+
90+
// Operation
91+
_operation = new OpenApiOperation();
92+
var methodInfo = typeof(FakeConstructedControllerWithXmlComments)
93+
.GetMethod(nameof(FakeConstructedControllerWithXmlComments.ActionWithSummaryAndResponseTags));
94+
95+
var apiDescription = ApiDescriptionFactory.Create(methodInfo: methodInfo, groupName: "v1", httpMethod: "POST", relativePath: "resource");
96+
_operationFilterContext = new OperationFilterContext(apiDescription, null, null, methodInfo);
97+
_operationFilter = new XmlCommentsOperationFilter(xPathDocument);
98+
99+
// Parameter
100+
_parameter = new()
101+
{
102+
Schema = new()
103+
{
104+
Type = "string",
105+
Description = "schema-level description",
106+
},
107+
};
108+
109+
var propertyInfo = typeof(XmlAnnotatedType).GetProperty(nameof(XmlAnnotatedType.StringProperty));
110+
var apiParameterDescription = new ApiParameterDescription();
111+
_parameterFilterContext = new ParameterFilterContext(apiParameterDescription, null, null, propertyInfo: propertyInfo);
112+
_parameterFilter = new XmlCommentsParameterFilter(xPathDocument);
113+
114+
// Request Body
115+
_requestBody = new OpenApiRequestBody
116+
{
117+
Content = new Dictionary<string, OpenApiMediaType>()
118+
{
119+
["application/json"] = new()
120+
{
121+
Schema = new()
122+
{
123+
Type = "string",
124+
},
125+
},
126+
},
127+
};
128+
var parameterInfo = typeof(FakeControllerWithXmlComments)
129+
.GetMethod(nameof(FakeControllerWithXmlComments.ActionWithParamTags))!
130+
.GetParameters()[0];
131+
132+
var bodyParameterDescription = new ApiParameterDescription
133+
{
134+
ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo }
135+
};
136+
_requestBodyFilterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null);
137+
_requestBodyFilter = new XmlCommentsRequestBodyFilter(xPathDocument);
138+
}
139+
140+
[Benchmark]
141+
public void Document()
142+
=> _documentFilter.Apply(_document, _documentFilterContext);
143+
144+
[Benchmark]
145+
public void Operation()
146+
=> _operationFilter.Apply(_operation, _operationFilterContext);
147+
148+
[Benchmark]
149+
public void Parameter()
150+
=> _parameterFilter.Apply(_parameter, _parameterFilterContext);
151+
152+
[Benchmark]
153+
public void RequestBody()
154+
=> _requestBodyFilter.Apply(_requestBody, _requestBodyFilterContext);
155+
}

0 commit comments

Comments
 (0)