Skip to content

Commit

Permalink
Add benchmarks project (#3050)
Browse files Browse the repository at this point in the history
Add project for running benchmarks.

Adapted from mus65@65bfd97.

See #3044.

Co-Authored-By: mus65 <[email protected]>
  • Loading branch information
martincostello and mus65 committed Sep 8, 2024
1 parent 46c1bc9 commit 3405724
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
[Bb]in/
.vs/
.idea*
BenchmarkDotNet.Artifacts*/
coverage
coverage.*
node_modules/
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<ItemGroup>
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="4.2.2" />
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
<PackageVersion Include="IdentityServer4" Version="3.1.4" />
Expand Down
10 changes: 10 additions & 0 deletions Swashbuckle.AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
benchmark.ps1 = benchmark.ps1
CONTRIBUTING.md = CONTRIBUTING.md
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
Expand Down Expand Up @@ -117,6 +118,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalAppWithHostedService
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvcWithNullable", "test\WebSites\MvcWithNullable\MvcWithNullable.csproj", "{F88B6070-BE3C-45F9-978C-2ECBA9518C24}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{0C7326F1-F731-4CF9-8A98-80F39541D28F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Swashbuckle.AspNetCore.Benchmarks", "perf\Swashbuckle.AspNetCore.Benchmarks\Swashbuckle.AspNetCore.Benchmarks.csproj", "{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -271,6 +276,10 @@ Global
{F88B6070-BE3C-45F9-978C-2ECBA9518C24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F88B6070-BE3C-45F9-978C-2ECBA9518C24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F88B6070-BE3C-45F9-978C-2ECBA9518C24}.Release|Any CPU.Build.0 = Release|Any CPU
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -316,6 +325,7 @@ Global
{07BB09CF-6C6F-4D00-A459-93586345C921} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB}
{D06A88E8-6F42-4F40-943A-E266C0AE6EC9} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB}
{F88B6070-BE3C-45F9-978C-2ECBA9518C24} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB}
{28F5840B-AE87-46DB-9D83-C3C8C77C6A13} = {0C7326F1-F731-4CF9-8A98-80F39541D28F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {36FC6A67-247D-4149-8EDD-79FFD1A75F51}
Expand Down
28 changes: 28 additions & 0 deletions benchmark.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#! /usr/bin/env pwsh

#Requires -PSEdition Core
#Requires -Version 7

param(
[Parameter(Mandatory = $false)][string] $Framework = "net8.0",
[Parameter(Mandatory = $false)][string] $Job = ""
)

$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"

$benchmarks = (Join-Path $PSScriptRoot "perf" "Swashbuckle.AspNetCore.Benchmarks" "Swashbuckle.AspNetCore.Benchmarks.csproj")

$additionalArgs = @()

if (-Not [string]::IsNullOrEmpty($Job)) {
$additionalArgs += "--job"
$additionalArgs += $Job
}

if (-Not [string]::IsNullOrEmpty(${env:GITHUB_SHA})) {
$additionalArgs += "--exporters"
$additionalArgs += "json"
}

dotnet run --project $benchmarks --configuration "Release" --framework $Framework -- $additionalArgs --% --filter *
4 changes: 4 additions & 0 deletions perf/Swashbuckle.AspNetCore.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using BenchmarkDotNet.Running;

var switcher = new BenchmarkSwitcher(typeof(Program).Assembly);
switcher.Run(args);
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Swashbuckle.AspNetCore.Annotations\Swashbuckle.AspNetCore.Annotations.csproj" />
<ProjectReference Include="..\..\src\Swashbuckle.AspNetCore.SwaggerGen\Swashbuckle.AspNetCore.SwaggerGen.csproj" />
<ProjectReference Include="..\..\src\Swashbuckle.AspNetCore.Swagger\Swashbuckle.AspNetCore.Swagger.csproj" />
<ProjectReference Include="..\..\test\Swashbuckle.AspNetCore.SwaggerGen.Test\Swashbuckle.AspNetCore.SwaggerGen.Test.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" />
</ItemGroup>
</Project>
155 changes: 155 additions & 0 deletions perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.XPath;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerGen.Test;
using Swashbuckle.AspNetCore.TestSupport;

namespace Swashbuckle.AspNetCore.Benchmarks;

[MemoryDiagnoser]
public class XmlCommentsBenchmark
{
private XmlCommentsDocumentFilter _documentFilter;
private OpenApiDocument _document;
private DocumentFilterContext _documentFilterContext;

private XmlCommentsOperationFilter _operationFilter;
private OpenApiOperation _operation;
private OperationFilterContext _operationFilterContext;

private XmlCommentsParameterFilter _parameterFilter;
private OpenApiParameter _parameter;
private ParameterFilterContext _parameterFilterContext;

private XmlCommentsRequestBodyFilter _requestBodyFilter;
private OpenApiRequestBody _requestBody;
private RequestBodyFilterContext _requestBodyFilterContext;

private const int AddMemberCount = 10_000;

[GlobalSetup]
public void Setup()
{
// Load XML
XmlDocument xmlDocument;
using (var xmlComments = File.OpenText($"{typeof(FakeControllerWithXmlComments).Assembly.GetName().Name}.xml"))
{
xmlDocument = new XmlDocument();
xmlDocument.Load(xmlComments);
}

// Append dummy members to XML document
XPathNavigator navigator = xmlDocument.CreateNavigator()!;
navigator.MoveToRoot();
navigator.MoveToChild("doc", string.Empty);
navigator.MoveToChild("members", string.Empty);

for (int i = 0; i < AddMemberCount; i++)
{
navigator.PrependChild(@$"<member name=""benchmark_{i}""></member>");
}

using var xmlStream = new MemoryStream();
xmlDocument.Save(xmlStream);
xmlStream.Seek(0, SeekOrigin.Begin);
var xPathDocument = new XPathDocument(xmlStream);

// Document
_document = new OpenApiDocument();
_documentFilterContext = new DocumentFilterContext(
[
new ApiDescription
{
ActionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(FakeControllerWithXmlComments).GetTypeInfo(),
ControllerName = nameof(FakeControllerWithXmlComments),
},
},
new ApiDescription
{
ActionDescriptor = new ControllerActionDescriptor
{
ControllerTypeInfo = typeof(FakeControllerWithXmlComments).GetTypeInfo(),
ControllerName = nameof(FakeControllerWithXmlComments),
},
},
],
null,
null);

_documentFilter = new XmlCommentsDocumentFilter(xPathDocument);

// Operation
_operation = new OpenApiOperation();
var methodInfo = typeof(FakeConstructedControllerWithXmlComments)
.GetMethod(nameof(FakeConstructedControllerWithXmlComments.ActionWithSummaryAndResponseTags));

var apiDescription = ApiDescriptionFactory.Create(methodInfo: methodInfo, groupName: "v1", httpMethod: "POST", relativePath: "resource");
_operationFilterContext = new OperationFilterContext(apiDescription, null, null, methodInfo);
_operationFilter = new XmlCommentsOperationFilter(xPathDocument);

// Parameter
_parameter = new()
{
Schema = new()
{
Type = "string",
Description = "schema-level description",
},
};

var propertyInfo = typeof(XmlAnnotatedType).GetProperty(nameof(XmlAnnotatedType.StringProperty));
var apiParameterDescription = new ApiParameterDescription();
_parameterFilterContext = new ParameterFilterContext(apiParameterDescription, null, null, propertyInfo: propertyInfo);
_parameterFilter = new XmlCommentsParameterFilter(xPathDocument);

// Request Body
_requestBody = new OpenApiRequestBody
{
Content = new Dictionary<string, OpenApiMediaType>()
{
["application/json"] = new()
{
Schema = new()
{
Type = "string",
},
},
},
};
var parameterInfo = typeof(FakeControllerWithXmlComments)
.GetMethod(nameof(FakeControllerWithXmlComments.ActionWithParamTags))!
.GetParameters()[0];

var bodyParameterDescription = new ApiParameterDescription
{
ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo }
};
_requestBodyFilterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null);
_requestBodyFilter = new XmlCommentsRequestBodyFilter(xPathDocument);
}

[Benchmark]
public void Document()
=> _documentFilter.Apply(_document, _documentFilterContext);

[Benchmark]
public void Operation()
=> _operationFilter.Apply(_operation, _operationFilterContext);

[Benchmark]
public void Parameter()
=> _parameterFilter.Apply(_parameter, _parameterFilterContext);

[Benchmark]
public void RequestBody()
=> _requestBodyFilter.Apply(_requestBody, _requestBodyFilterContext);
}

0 comments on commit 3405724

Please sign in to comment.