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

Improve IncludeXmlComments performance (2) #3044

Merged
merged 10 commits into from
Aug 27, 2024

Conversation

mus65
Copy link
Contributor

@mus65 mus65 commented Aug 25, 2024

Pull Request

The issue or feature being addressed

Fixes #2164

Details on the issue fix or feature implementation

This is a rebase and enhancement of #2443 (thanks to @stevendarby, added you as co-author).

This significantly improves the performance of the XML comment filters. The original fix #2443 created a dictionary of the members to avoid iterating the whole XPath for every single item.

I made some further improvements by replacing all usages of Select and SelectSingleNode with SelectChildren (added some helper extensions for this). This completely avoids XPath expressions and the documentation even mentions SelectChildren being faster.

I made the new constructors internal for now (which is also why I changed ParameterFilter to AddParameterFilterInstance etc.. DI fails otherwise). It felt wrong to me to make these public, but I can make these public if you want.

Tests

The existing tests imho already cover this code pretty well. I only added a test case for a bug I noticed in #2443 (there was a return instead of a continue in XmlCommentsDocumentFilter).

Performance

#2443 already mentions significant improvements (25s -> 4s). With another local test case I have with a 1.5MB XML file, generation time decreases from ~1900ms to ~200ms.

#2443 also mentions some concern about memory consumption, so I made some manual tests with a very big (30MB) XML document file: the XPathDocument itself already loads the document completely into memory which uses about 60 MB. Creating the dictionary creates an additional 40MB. So there is some additional memory consumption, but considering that 30MB is pretty extreme and most files should be < 1MB, the memory increase should be negligible for most applications. In my local test case with a 1.5 MB File, memory consumption after GC.Collect increased by about 1MB.

Co-authored-by: steven.darby <steven.darby@tribalgroup.com>
@martincostello
Copy link
Collaborator

With another local test case I have with a 1.5MB XML file, generation time decreases from ~1900ms to ~200ms.

Could you create a BenchmarkDotNet benchmark that replicates what you did and share it as a comment so we can re-run it ourselves?

mus65 and others added 6 commits August 25, 2024 17:45
Co-authored-by: Martin Costello <martin@martincostello.com>
Co-authored-by: Martin Costello <martin@martincostello.com>
and add SelectChild/GetAttribute extensions for
consistency.
@mus65
Copy link
Contributor Author

mus65 commented Aug 25, 2024

With another local test case I have with a 1.5MB XML file, generation time decreases from ~1900ms to ~200ms.

Could you create a BenchmarkDotNet benchmark that replicates what you did and share it as a comment so we can re-run it ourselves?

I'll look into it. Could take some time though. I'm not really familiar with BenchmarkDotNet yet and I can't share my local test case.

@mus65
Copy link
Contributor Author

mus65 commented Aug 26, 2024

I created a benchmark in a separate branch here. As expected, the new implementation is a lot
faster, even with a small member count. The performance of the new implementation stays the same,
regardless of member count. The old implementation gets significantly slower the more members
exist. addMemberCount can be changed in the benchmark to try different member counts.

Old implementation with small member count (n = 79):

Method Mean Error StdDev Median Gen0 Gen1 Allocated
Document 4.074 us 0.0811 us 0.1780 us 4.002 us 2.2278 0.5569 20.53 KB
Operation 12.692 us 0.0828 us 0.0775 us 12.712 us 5.0659 0.0610 47.06 KB
Parameter 6.878 us 0.1368 us 0.2360 us 6.786 us 2.6321 0.0076 24.29 KB
RequestBody 4.122 us 0.0185 us 0.0164 us 4.125 us 2.2278 0.0610 20.64 KB

Old implementation with big member count (n = 10.000):

Method Mean Error StdDev Median Gen0 Gen1 Allocated
Document 541.3 us 10.56 us 13.73 us 542.8 us 78.1250 1.9531 723.66 KB
Operation 1,061.1 us 20.70 us 31.61 us 1,074.7 us 156.2500 - 1453.34 KB
Parameter 489.3 us 6.42 us 5.01 us 489.4 us 79.1016 - 727.42 KB
RequestBody 476.8 us 9.43 us 16.52 us 467.4 us 78.1250 - 723.79 KB

New implementation with any member count:

Method Mean Error StdDev Median Gen0 Gen1 Allocated
Document 2.675 us 0.0530 us 0.0955 us 2.726 us 1.8959 0.6294 17.45 KB
Operation 4.851 us 0.0907 us 0.0848 us 4.869 us 3.7842 0.1526 34.84 KB
Parameter 2.421 us 0.0313 us 0.0293 us 2.411 us 1.8959 0.0687 17.46 KB
RequestBody 2.545 us 0.0136 us 0.0127 us 2.545 us 1.8158 - 16.73 KB

@stevendarby
Copy link

@mus65 thank you for picking this up and improving it, great stuff seeing the benchmarks too

@martincostello
Copy link
Collaborator

Thanks @mus65 for doing those benchmarks - I'll take a closer look at them tomorrow 👍

martincostello added a commit to martincostello/Swashbuckle.AspNetCore that referenced this pull request Aug 27, 2024
Add project for running benchmarks.
Adapted from mus65@65bfd97.
See domaindrivendev#3044.

Co-Authored-By: mus65 <1933789+mus65@users.noreply.github.com>
@martincostello
Copy link
Collaborator

martincostello commented Aug 27, 2024

Thanks again for adding these benchmarks. I've pushed up a version of them in #3050 for us to use going forwards.

These are the results I get on my laptop - TL;DR - better by every measure 🥳

N=79

Base

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22621.4037/22H2/2022Update/SunValley2)
12th Gen Intel Core i7-1270P, 1 CPU, 16 logical and 12 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
Method Mean Error StdDev Gen0 Gen1 Allocated
Document 10.468 μs 0.4412 μs 1.2517 μs 2.8534 0.7172 26.29 KB
Operation 23.157 μs 0.4611 μs 0.5663 μs 6.3477 - 58.5 KB
Parameter 12.081 μs 0.2327 μs 0.2680 μs 3.2501 0.0916 29.98 KB
RequestBody 9.833 μs 0.3246 μs 0.9366 μs 2.8381 - 26.2 KB

This PR

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22621.4037/22H2/2022Update/SunValley2)
12th Gen Intel Core i7-1270P, 1 CPU, 16 logical and 12 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
Method Mean Error StdDev Gen0 Gen1 Allocated
Document 3.864 μs 0.2210 μs 0.6341 μs 1.9188 0.4807 17.66 KB
Operation 6.812 μs 0.3186 μs 0.9294 μs 3.8147 - 35.17 KB
Parameter 3.410 μs 0.1073 μs 0.2991 μs 1.9112 0.1030 17.6 KB
RequestBody 3.656 μs 0.1797 μs 0.5271 μs 1.8158 - 16.73 KB

N=10,000

Base

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22621.4037/22H2/2022Update/SunValley2)
12th Gen Intel Core i7-1270P, 1 CPU, 16 logical and 12 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
Method Mean Error StdDev Gen0 Gen1 Allocated
Document 598.6 μs 8.41 μs 7.02 μs 78.1250 1.9531 724.36 KB
Operation 1,105.6 μs 19.72 μs 17.48 μs 156.2500 - 1453.67 KB
Parameter 516.7 μs 9.92 μs 8.28 μs 79.1016 - 727.55 KB
RequestBody 487.9 μs 4.22 μs 3.52 μs 78.1250 - 723.79 KB

This PR

BenchmarkDotNet v0.14.0, Windows 11 (10.0.22621.4037/22H2/2022Update/SunValley2)
12th Gen Intel Core i7-1270P, 1 CPU, 16 logical and 12 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
Method Mean Error StdDev Median Gen0 Gen1 Allocated
Document 3.872 μs 0.1670 μs 0.4655 μs 3.857 μs 1.9150 0.4807 17.66 KB
Operation 12.341 μs 1.2843 μs 3.7869 μs 14.136 μs 3.8147 - 35.17 KB
Parameter 7.158 μs 0.3641 μs 1.0679 μs 7.320 μs 1.8921 0.0916 17.6 KB
RequestBody 7.953 μs 0.3605 μs 1.0629 μs 8.024 μs 1.8005 - 16.73 KB

@codecov-commenter
Copy link

codecov-commenter commented Aug 27, 2024

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 98.83721% with 1 line in your changes missing coverage. Please review.

Project coverage is 90.53%. Comparing base (16e2a94) to head (45a97e9).
Report is 4 commits behind head on master.

Files Patch % Lines
...waggerGen/XmlComments/XmlCommentsDocumentHelper.cs 87.50% 1 Missing ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3044      +/-   ##
==========================================
+ Coverage   90.46%   90.53%   +0.06%     
==========================================
  Files          74       76       +2     
  Lines        2980     3002      +22     
  Branches      470      479       +9     
==========================================
+ Hits         2696     2718      +22     
  Misses        284      284              
Flag Coverage Δ
Linux 90.53% <98.83%> (+0.06%) ⬆️
Windows 90.53% <98.83%> (+0.06%) ⬆️
macOS 90.53% <98.83%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@martincostello martincostello added this to the v6.8.0 milestone Aug 27, 2024
@martincostello martincostello merged commit 90f302d into domaindrivendev:master Aug 27, 2024
9 checks passed
martincostello added a commit that referenced this pull request Sep 8, 2024
Add project for running benchmarks.

Adapted from mus65@65bfd97.

See #3044.

Co-Authored-By: mus65 <1933789+mus65@users.noreply.github.com>
renovate bot referenced this pull request in orso-co/Orso.Arpa.Api Sep 27, 2024
This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[Swashbuckle.AspNetCore](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore)
| `6.7.3` -> `6.8.0` |
[![age](https://developer.mend.io/api/mc/badges/age/nuget/Swashbuckle.AspNetCore/6.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/Swashbuckle.AspNetCore/6.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/Swashbuckle.AspNetCore/6.7.3/6.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Swashbuckle.AspNetCore/6.7.3/6.8.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>domaindrivendev/Swashbuckle.AspNetCore
(Swashbuckle.AspNetCore)</summary>

###
[`v6.8.0`](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v6.8.0)

##### What's Changed

- Added dependency injection/easy registration for async filters by
[@&#8203;tofi92](https://redirect.github.com/tofi92) in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3030](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3030)
- Improve IncludeXmlComments performance (2) by
[@&#8203;mus65](https://redirect.github.com/mus65) in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3044](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3044)
- Apply ParameterFilters and RequestBodyFilters for WithOpenApi
endpoints by
[@&#8203;jgarciadelanoceda](https://redirect.github.com/jgarciadelanoceda)
in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3059](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3059)
- Swagger plugins support by
[@&#8203;jvmlet](https://redirect.github.com/jvmlet) in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3056](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3056)
- Add benchmarks project by
[@&#8203;martincostello](https://redirect.github.com/martincostello) in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3050](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3050)
- Fix NuGet badge by
[@&#8203;martincostello](https://redirect.github.com/martincostello) in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3064](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3064)
- SwaggerGen - Improved handling of dictionaries with enum key by
[@&#8203;flarestudiopl](https://redirect.github.com/flarestudiopl) in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3068](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3068)
- Fix typo by
[@&#8203;tom-star119](https://redirect.github.com/tom-star119) in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3073](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3073)
- Do not fill the RequestBody description with the first parameter of a…
by
[@&#8203;jgarciadelanoceda](https://redirect.github.com/jgarciadelanoceda)
in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3076](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3076)

##### New Contributors

- [@&#8203;tofi92](https://redirect.github.com/tofi92) made their first
contribution in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3030](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3030)
- [@&#8203;mus65](https://redirect.github.com/mus65) made their first
contribution in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3044](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3044)
- [@&#8203;jvmlet](https://redirect.github.com/jvmlet) made their first
contribution in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3056](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3056)
- [@&#8203;flarestudiopl](https://redirect.github.com/flarestudiopl)
made their first contribution in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3068](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3068)
- [@&#8203;tom-star119](https://redirect.github.com/tom-star119) made
their first contribution in
[https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3073](https://redirect.github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3073)

**Full Changelog**:
domaindrivendev/Swashbuckle.AspNetCore@v6.7.3...v6.8.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 10pm every weekday,every
weekend,before 5am every weekday" in timezone Europe/Berlin, Automerge -
At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/orso-co/Orso.Arpa.Api).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzOC45Ny4wIiwidXBkYXRlZEluVmVyIjoiMzguOTcuMCIsInRhcmdldEJyYW5jaCI6ImRldmVsb3AiLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
@martincostello
Copy link
Collaborator

I have some benchmarks I've been using to compare the benchmarks of Swashbuckle, NSwag and the new .NET 9 ASP.NET Core OpenAPI library, and there's a noticeable really improvement from the ingesting changes from this PR.

image

The first major improvement is from updating to .NET 9 RC1 (martincostello/aspnetcore-openapi@7e5e365), then the second improvement with the big reduction in memory usage is from updating to 6.8.0 (martincostello/aspnetcore-openapi@b4aba65)

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

IncludeXmlComments performance issues
4 participants