This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Optimization: Further reduce allocations in GetCustomAtttributes() paths #20795
Merged
jkotas
merged 8 commits into
dotnet:master
from
NickCraver:craver/optimize-attributes-2
Nov 17, 2018
Merged
Optimization: Further reduce allocations in GetCustomAtttributes() paths #20795
jkotas
merged 8 commits into
dotnet:master
from
NickCraver:craver/optimize-attributes-2
Nov 17, 2018
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Inside CustomAttribute, PseudoCustomAttribute is the mechanism used to return Attribute instances for what are really flags and such underneath in most cases. This is just an implentation detail, but this detail creates an intermediate array just for passing things around inside CustomAttribute...that's not necessary. This replaces the Attribute[] allocations with the ListBuilder<Attribute> struct for accumulating and relaying this attributes to parent methods. Many overloads had a lot of duplicate coalescing code on this path as well...DRYed up that series of methods. This change reduces allocation overhead for all calls by 40 bytes. In the case of a miss where none would be returned (e.g. .GetCustomAttributes<Attribute>() on a non-attributed class), this reduces allocations down to zero.
…e cases The split here was simple, but allocated an extra intermediate array for each parent type when searching for attributes in the inheritance case. Instead of doing that, we now move that to the second use case exclusively (which uses/returns the array created). The other use case adds to the ListBuilder<object> struct to accumulate through the inheritance hierarchy crawl (rather than an after-the-fact copy). Note some parameter removals on the filter side...those weren't used already (and got in the way of this change).
Just removing the cruft that isn't actually used in here anymore (some of it in quite some time). Trivial amounts of speed gain, but every little bit helps!
NickCraver
changed the title
Optimization: Further reduce many allocations in GetCustomAtttributes() paths
Optimization: Further reduce allocations in GetCustomAtttributes() paths
Nov 4, 2018
jkotas
reviewed
Nov 5, 2018
src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
Outdated
Show resolved
Hide resolved
jkotas
reviewed
Nov 5, 2018
src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
Outdated
Show resolved
Hide resolved
jkotas
reviewed
Nov 5, 2018
src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
Outdated
Show resolved
Hide resolved
jkotas
reviewed
Nov 5, 2018
src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
Outdated
Show resolved
Hide resolved
Thsi eats the resize on the > 1 case, but that's so rare it's a net win to start with 0 here.
A bit if a performance boost from the struct copy removal
jkotas
reviewed
Nov 5, 2018
src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
Outdated
Show resolved
Hide resolved
jkotas
reviewed
Nov 5, 2018
src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs
Outdated
Show resolved
Hide resolved
…aths In relation to dotnet#20795, these old calls seem to be for consistency but they're not likely to change and a comment (I think) accomplishes the same goal with less code...so here's a commit demonstrating it.
jkotas
reviewed
Nov 6, 2018
Make it clear that pseudo attributes are a distinct thing (e.g. why not in *this* method) than the return line.
jkotas
approved these changes
Nov 6, 2018
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.
Thank you!
NickCraver
added a commit
to NickCraver/performance
that referenced
this pull request
Dec 9, 2018
This benchmarks specifically GetCustomAttributes and IsDefined (used in dotnet/coreclr#20779 and dotnet/coreclr#20795) Note: there are many code paths through the attribute pipeline which vary drastically in terms of performance and allocations - so there's what seems to be an excessive number of benchmarks here, but they are all pretty unique.
adamsitnik
pushed a commit
to dotnet/performance
that referenced
this pull request
Dec 9, 2018
* Adds initial benchmarks for System.Reflection: attributes This benchmarks specifically GetCustomAttributes and IsDefined (used in dotnet/coreclr#20779 and dotnet/coreclr#20795) Note: there are many code paths through the attribute pipeline which vary drastically in terms of performance and allocations - so there's what seems to be an excessive number of benchmarks here, but they are all pretty unique. * Add new category to perflab\ReflectionPerf * Consolidate classes PR feedback: don't follow the Math conventions here...go with what it should be :)
picenka21
pushed a commit
to picenka21/runtime
that referenced
this pull request
Feb 18, 2022
…ths (dotnet/coreclr#20795) * Remove PseudoCustomAttribute allocations Inside CustomAttribute, PseudoCustomAttribute is the mechanism used to return Attribute instances for what are really flags and such underneath in most cases. This is just an implentation detail, but this detail creates an intermediate array just for passing things around inside CustomAttribute...that's not necessary. This replaces the Attribute[] allocations with the ListBuilder<Attribute> struct for accumulating and relaying this attributes to parent methods. Many overloads had a lot of duplicate coalescing code on this path as well...DRYed up that series of methods. This change reduces allocation overhead for all calls by 40 bytes. In the case of a miss where none would be returned (e.g. .GetCustomAttributes<Attribute>() on a non-attributed class), this reduces allocations down to zero. * CustomAttribute: split GetCustomAttributes into total and additive use cases The split here was simple, but allocated an extra intermediate array for each parent type when searching for attributes in the inheritance case. Instead of doing that, we now move that to the second use case exclusively (which uses/returns the array created). The other use case adds to the ListBuilder<object> struct to accumulate through the inheritance hierarchy crawl (rather than an after-the-fact copy). Note some parameter removals on the filter side...those weren't used already (and got in the way of this change). * CustomAttributes: Cleanup of unused ops Just removing the cruft that isn't actually used in here anymore (some of it in quite some time). Trivial amounts of speed gain, but every little bit helps! * Param typo and red passing for GetCombinedList() * Assume zero pseudo attributes by default (the most likely case) Thsi eats the resize on the > 1 case, but that's so rare it's a net win to start with 0 here. * Pseudo attributes: use out instead of return A bit if a performance boost from the struct copy removal * Pseudo attributes: switch to out everywhere and remove uneeded code paths In relation to dotnet/coreclr#20795, these old calls seem to be for consistency but they're not likely to change and a comment (I think) accomplishes the same goal with less code...so here's a commit demonstrating it. * Comment spacing for pseudo attribute fetching Make it clear that pseudo attributes are a distinct thing (e.g. why not in *this* method) than the return line. Commit migrated from dotnet/coreclr@477c252
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is a continuation of #20779 optimizing various
Attribute
allocations.Two items addressed here (split into 3 commits) are:
PseudoCustomAttribute
's intermediate array allocation (used when synthesizeAttribute
instances from things like[Serializable]
under the covers). Previously in the case of any synthesized attributes being present, anAttribute[]
array was created to return these to the caller method, and was discarded afterwards. It also DRYs up a lot of repeated code there.GetCustomAttributes()
inner-most methods was doing similar, returning anobject[]
each time. When crawling up a class's inheritance hierarchy in theinherit: true
case, this resulted in an array per parent. Then were then copied to the overall list and the array was discarded. The other overload calling into the core method here actually uses the createdobject[]
array to return it back to the external caller (and remains necessary). The split here adds to theListBuilder<object>
instead for the inner method, and the array is only created when needed in the second caller. So now, when crawling up the hierarchy of parents we just add to the same list each time.Note: the
GetCustomAttributes()
=>AddCustomAttributes()
split could use careful eyes around the behavior difference of the iteration levels.FilterCustomAttributeRecord()
had thederivedAttributes
list of the previous inheritor at the time of evaluation.FilterCustomAttributeRecord()
evaluation.Looking at
FilterCustomAttributeRecord
and how it's evaluated, I don't think this matters...but yeah: should be scrutinized. I wouldn't be surprised if tests didn't cover a difference here if there is one that matters.Based on the same benchmarks as before (source in #20779):
Before
After