-
Notifications
You must be signed in to change notification settings - Fork 10.5k
perf: improve allocations in OwinEnvironment
#58917
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
perf: improve allocations in OwinEnvironment
#58917
Conversation
|
TODO:
|
|
concept looks solid, nice; added some thoughts |
|
could you please resolve conflict @DeagleGross |
| return false; | ||
| } | ||
|
|
||
| public struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>, IEnumerator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this not share as public struct Enumerator : IEnumerator<KeyValuePair<string, T>>, IEnumerator with the one above? ConvertingEnumerator or something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renamed to ConvertingEnumerator. I dont think I can make a generic impl for inner enumerator of DictionaryStringArrayWrapper and DictionaryStringValuesWrapper. That was your idea, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'd have to pass in a Convert delegate to reuse the same one. Maybe not worth the bother.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 5 out of 12 changed files in this pull request and generated 2 comments.
Files not reviewed (7)
- AspNetCore.sln: Language not supported
- src/Http/HttpAbstractions.slnf: Language not supported
- src/Http/Owin/benchmarks/Microsoft.AspNetCore.Owin.Microbenchmarks/Microsoft.AspNetCore.Owin.Microbenchmarks.csproj: Language not supported
- src/Http/samples/MinimalSampleOwin/MinimalSampleOwin.csproj: Language not supported
- src/Http/samples/MinimalSampleOwin/Properties/launchSettings.json: Language not supported
- src/Http/Owin/src/DictionaryStringValuesWrapper.cs: Evaluated as low risk
- src/Http/Owin/src/DictionaryStringArrayWrapper.cs: Evaluated as low risk
| return _contextEntries; | ||
| } | ||
|
|
||
| public bool TryGetValue(string key, out FeatureMap entry) |
Copilot
AI
Feb 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method does not correctly handle the case where _contextEntries is not initialized and _deletedKeys contains the key. Add a check for _deletedKeys before returning false when _contextEntries is not initialized.
| return _entries.ContainsKey(key) || _contextDependentEntries.ContainsKey(key); | ||
| } | ||
|
|
||
| public bool Remove(string key) |
Copilot
AI
Feb 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method does not correctly handle the case where _contextEntries is not initialized and _deletedKeys contains the key. Add a check for _deletedKeys before returning false when _contextEntries is not initialized.
|
/azp run |
|
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
OwinEnvironment allocates a
Dictionary<string, FeatureMap>with at least 23 entries ofstringandFeatureMapobjects per request.In most cases, Owin abstraction is used only to get the data in the specific format (i.e. access the HTTP method via
OwinConstants.RequestMethodkey), but not to remove \ add entries or rebuild the wholeFeatureMapdictionary.Therefore I am introducing the
OwinEntriesclass (not visible to users), which allocates the static readonly entries dictionary (similar to what existed before) and uses that for the key-value access (so a single allocation per app lifetime against the per-request allocation). However,OwinEnvironmenthas a rich API to modify the dictionary (i.e. remove entries or clear them completely). Therefore I am doing the following to fully support existing API and dont introduce breaking changes:OwinEnvironment.FeatureMapsreturningIDictionary<string, FeatureMap>there is no way to securely determine if the dictionary instance will be changed, and because of that we can't avoid performing the deep-copy of static_entries(same perf loss as existed). Next interaction withOwinEnvironmentwill be using_contextEntries(request-lifetime) instead of static_entries.OwinEnvironment.Remove(string key)I am using a separateHashSet<string> _deletedKeysto keep track of deleted entries per request lifetime. Even if all original entries are deleted, this is still a more lightweight flow than existed beforeOwinEnvironment.Clear()I am falling back to_contextEntriesusage (request-lifetime)Note: there are some
FeatureMapobjects, which are dependent on theHttpContextpassed intoOwinEnvironment, so I keep them separately in a dedicatedDictionary<string, FeatureMap>. It's contains a single entry so far.I have added the microbenchmark (see PR), with a code that performs multiple requests using the default
HttpContext, and used the newOwinEnvironmentimplementation against the old one:Benchmark results:
(thanks to @deanward81 for the idea)
Closes #58916