This repository was archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
ImmutableArray<T>.Builder.Add splitted in fast- and cold-path #28184
Closed
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Diagnostics.Contracts; | ||
| using System.Runtime.CompilerServices; | ||
|
|
||
| namespace System.Collections.Immutable | ||
| { | ||
|
|
@@ -247,7 +248,28 @@ public void Insert(int index, T item) | |
| /// Adds an item to the <see cref="ICollection{T}"/>. | ||
| /// </summary> | ||
| /// <param name="item">The object to add to the <see cref="ICollection{T}"/>.</param> | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| public void Add(T item) | ||
| { | ||
| int count = _count; | ||
| T[] elements = _elements; | ||
|
|
||
| // PERF: The uint-casts allow the JIT to eliminate bound-checks. | ||
| // https://github.com/dotnet/coreclr/pull/9773 | ||
| if ((uint)count < (uint)elements.Length) | ||
| { | ||
| elements[count] = item; | ||
| _count = count + 1; | ||
| } | ||
| else | ||
| { | ||
| AddWithResize(item); | ||
| } | ||
| } | ||
|
|
||
| // Specify NoInlining so that we are guaranteed an opportunity to service this method | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AArnott, what did you mean by "guaranteed an opportunity to service this method"? I though the NoInlining was here to avoid including the slow path as part of the caller getting aggressively inlined and bloating its caller unnecessarily. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can see it has the effect you say. I thought that aggressive inlining limited our options to change the method later due to ngen. My proposed comment was focused on that. |
||
| [MethodImpl(MethodImplOptions.NoInlining)] | ||
| private void AddWithResize(T item) | ||
| { | ||
| int newCount = _count + 1; | ||
| this.EnsureCapacity(newCount); | ||
|
|
||
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.
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.
How much of the improvement you're showing is due to AggressiveInlining vs due to the changes in the method body? I'm not convinced this should be AggressiveInlining.
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.
Benchmark
Notes
_NoInline-methods are attributed with[MethodImpl(MethodImplOptions.NoInlining)]__Inline-methods are attributed with[MethodImpl(MethodImplOptions.AggressiveInlining)]Methos without
_Xxxare without any attributes.Results
Discussion
SplitAdd
The JIT won't inline
SplitAdddue to[FAILED: unprofitable inline] Builder:SplitAdd(long):thiswhich seems strange to me, because the dasm for this method is:Really not much code.
So
SplitAddisn't inlined, then whySplitAdd_NoInlinefrom the benchmark shows different numbers? It's becuase of the different prolog, and therex.jmp(although I have to admit that I don't know whatrex.jmpis (yeah, I could search for it) and where it comes from):Note: with AggressiveInling the JIT emits a
calland norex.jmpinstruction.Side note: in #28177 (comment) it's maybe that there was no AggressiveInlining.
TweakedAdd
JIT will inline this method by default, although the dasm is much greater than the one
SplitAdd(here the dasm is shown fromTweakAdd_NoInline):Conclusion
The "raw implementation" (NoInline compared) of
SplitAddis way faster thanTweakedAdd. Because the JIT won't inline SplitAdd we couldIn List.Add the similar pattern with fast- and cold-path is used, and there is also AggressiveInling. So I don't see any reason why we shouldn't go with that.
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 mentioned:
But if I look at just those two rows of your results table in the same comment, it looks like "this PR" slows down the scenario. Am I reading it right? If so, I don't know why we would take this PR.
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.
Theses results are for benchmarks to answert the question about the influence of aggressive inlining.
So the suffixes like
_Inlinehave to taken into account when reading the results.This PR, as implemented, shows the results in the
SplitAdd_Inlinerow, and there is an improvement. Not a huge one, but still noticeable faster.