Skip to content

feat: Remove default collection initialization for perf reasons - #2284

Merged
Vincent Biret (baywet) merged 22 commits into
mainfrom
feat/memory-perf-improvements
Apr 9, 2025
Merged

feat: Remove default collection initialization for perf reasons#2284
Vincent Biret (baywet) merged 22 commits into
mainfrom
feat/memory-perf-improvements

Conversation

@MaggieKimani1

@MaggieKimani1Maggie Kimani (MaggieKimani1) commented Mar 24, 2025

Copy link
Copy Markdown
Contributor
  • Uses the Lazy pattern to defer initialization of collections till the first time they are accessed.
  • This helps reduce unnecessary resource/memory allocations

Fixes#1971

@MaggieKimani1Maggie Kimani (MaggieKimani1) changed the title feat: use lazy get for collections to reduce resource allocationfeat: use Lazy<T> to initialize collections on accessMar 25, 2025
@MaggieKimani1
Maggie Kimani (MaggieKimani1) marked this pull request as ready for review March 25, 2025 14:36

@baywetVincent Biret (baywet) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For those sets of changes, it's always better to do it at one place with a draft, have a conversation, and then replicate all over.

Comment threadsrc/Microsoft.OpenApi/Models/OpenApiCallback.cs Outdated
Comment threadsrc/Microsoft.OpenApi/Models/OpenApiCallback.cs Outdated
@MaggieKimani1

Copy link
Copy Markdown
ContributorAuthor

Working on reverting these changes to remove default collection initialization altogether. There are lots of failing tests due to null reference exceptions but I'm working on fixing them

@darrelmiller

Copy link
Copy Markdown
Member

remove default collection initialization altogether

Looks like I missed this conversation. Is there a summary of why we want to force users to initialize these collections manually? Is ASP.NET team ok with this change?

@baywet

Copy link
Copy Markdown
Member

If we allocate by default with field initialization (current implementation), we allocate the memory when the object gets created.

If we lazy initialize the collection:

  • we allocate memory we're going to discard right away on deserialization
  • we have no way of telling the difference between empty and null

I believe the reason why we allocated on initialization historically was because:

  • nrt was not a thing, so there was no way for the consumers to get help from the compiler for those scenarios
  • collection expression syntax didn't exist, making the syntax verbose.

In addition to all that, in a lot of cases, the consumers end up doing object initialization syntax, which means they are initializing the collections anyway.

For all those reasons I suggest we stick to the idiomatic expressions of the platform, and leave it null, letting the compiler help consumers while reducing memory usage.

@MaggieKimani1

Copy link
Copy Markdown
ContributorAuthor

remove default collection initialization altogether

Looks like I missed this conversation. Is there a summary of why we want to force users to initialize these collections manually? Is ASP.NET team ok with this change?

cc: Safia Abdalla (@captainsafia)

@MaggieKimani1
Maggie Kimani (MaggieKimani1) marked this pull request as draft April 1, 2025 09:17
@darrelmiller

Darrel (darrelmiller) commented Apr 1, 2025

Copy link
Copy Markdown
Member

we allocate memory we're going to discard right away on deserialization

I had not considered this. And I don't think we should change deserialization to leverage existing collections.

we have no way of telling the difference between empty and null

I think most of the time that isn't an important distinction to make. I think there are only a couple of collections in the model where that is significant. e.g. Paths and security requirements.

the reason why we allocated on initialization historically was because:

The reason was so that a consumer editing/creating a document did not have instantiate a type that they may or may not know what that type is. e.g. sometimes we use dictionary<string,foo> sometimes we have dedicated classes OpenAPIResponses.

Eight years ago the object initialization syntax was not as developed and wasn't used nearly as much.

stick to the idiomatic expressions of the platform

Is it? I agree that C# has been introducing new ways to initialize objects/arrays/collections over the past few years, but is there some official design guidance that this is idiomatic?

Anyway, I think because the support for object initialization is better and because we want to optimize perf for the common case of deserialization, and because we now have NRT, I am more comfortable moving to a model where we don't lazy instantiate.

@MaggieKimani1
Maggie Kimani (MaggieKimani1) marked this pull request as ready for review April 3, 2025 10:12
@MaggieKimani1Maggie Kimani (MaggieKimani1) changed the title feat: use Lazy<T> to initialize collections on accessfeat: Remove default collection initialization for perf reasonsApr 3, 2025

@baywetVincent Biret (baywet) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great progress!

One thing I also wanted us to discuss but forgot to mention earlier: Why are collections returning their interface type? What benefit does it provide? the obvious drawback is that it prevents from using the collection initialization syntax [], especially for dictionaries as we can see in the unit tests. CC Darrel (@darrelmiller)

Comment threadsrc/Microsoft.OpenApi/Services/CopyReferences.cs Outdated
@MaggieKimani1

Copy link
Copy Markdown
ContributorAuthor

One thing I also wanted us to discuss but forgot to mention earlier: Why are collections returning their interface type? What benefit does it provide? the obvious drawback is that it prevents from using the collection initialization syntax [], especially for dictionaries as we can see in the unit tests. CC Darrel (@darrelmiller)

I'm not sure what the initial intention was for defining the dictionaries as interface types instead of concrete types.
Maybe Darrel (@darrelmiller) might have more historical context into it?
I'm open to refactoring it to simplify collection initialization syntax if we're all onboard with this change.

@darrelmiller

Copy link
Copy Markdown
Member

I suspect the reason we implemented these properties as interfaces was to leave the possibility of changing the concrete implementation if necessary. I don't have any objection to changing to the concrete classes now there is a clear benefit.

Comment threadsrc/Microsoft.OpenApi/Models/OpenApiOperation.cs Outdated
Comment threadsrc/Microsoft.OpenApi/Models/OpenApiPathItem.cs Outdated
Comment threadsrc/Microsoft.OpenApi/Models/OpenApiSchema.cs Outdated
Comment threadsrc/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs Outdated

@baywetVincent Biret (baywet) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the great work on this one! Please make sure you squash merge with a conventional commit :)

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate FailedQuality Gate failed

Failed conditions
62.7% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@baywet
Vincent Biret (baywet) requested a review from a teamApril 8, 2025 18:57
@baywet
Vincent Biret (baywet) deleted the feat/memory-perf-improvements branch April 9, 2025 09:29
Bart Koelman (bkoelman) referenced this pull request in domaindrivendev/Swashbuckle.AspNetCore Apr 30, 2025
Restore sorting of `required` in schemas.
Sign up for freeto 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.

[API Review] Memory performance improvements

4 participants

@MaggieKimani1@darrelmiller@baywet@Michael-Wamae