-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Replace some stackallocs with collection expressions #93126
Conversation
These will result in using an InlineArray rather than a localloc
Tagging subscribers to this area: @dotnet/area-meta Issue DetailsThese will result in using an InlineArray rather than a localloc
|
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.
LGTM.
Would be good to resolve the styling question I asked on the other PR and update if we decide having the space is better
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.
@stephentoub I played around with collection expression, and therefore your PRs are always a welcoming guide to get familiar with new features.
But here something seems strange, where either I miss something or this PR is actually a de-optimization.
will result in using an InlineArray rather than a localloc
When looking at the generated IL or reverse engineered C# with sharplab (main, 12 Oct 2023, so quite actual) I see array allocations instead the use of InlineArrays.
Can you please have a look and clarify?
I didn't comment on all places, to avoid noise.
@@ -24,7 +24,7 @@ public static string[] GetLogicalDrives() | |||
int count = BitOperations.PopCount((uint)drives); | |||
|
|||
string[] result = new string[count]; | |||
Span<char> root = stackalloc char[] { 'A', ':', '\\' }; | |||
Span<char> root = ['A', ':', '\\']; |
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.
According to sharplab, main 12 Oct 2023 this allocates.
Looks like the collection expression only avoid the allocation for ROS, but a ROS can't be used here.
@@ -922,7 +922,7 @@ public override string ToString() | |||
AssertValid(); | |||
|
|||
// Make local copy of data to avoid modifying input. | |||
Span<uint> rgulNumeric = stackalloc uint[4] { _data1, _data2, _data3, _data4 }; | |||
Span<uint> rgulNumeric = [_data1, _data2, _data3, _data4]; |
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.
sharplab, main 12 Oct 2023 allocates.
@@ -323,15 +323,15 @@ public static unsafe void Copy(Array sourceArray, int sourceIndex, Array destina | |||
if (Rank != 2) | |||
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_Need2DArray); | |||
|
|||
return InternalGetValue(GetFlattenedIndex(stackalloc int[] { index1, index2 })); | |||
return InternalGetValue(GetFlattenedIndex([index1, index2])); |
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.
sharplab, main 12 Oct 2023 allocates.
@@ -331,7 +331,7 @@ private static void SurrogateToUpperNLS(char h, char l, out char hr, out char lr | |||
Debug.Assert(char.IsHighSurrogate(h)); | |||
Debug.Assert(char.IsLowSurrogate(l)); | |||
|
|||
Span<char> chars = stackalloc char[] { h, l }; | |||
ReadOnlySpan<char> chars = [h, l]; |
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.
sharplab, main 12 Oct 2023 allocates.
sharplab is targeting a runtime that doesn't have InlineArray yet. Its main is about roslyn, not runtime. |
Locally with .NET 8 RC2 I also see the array allocations ( For example: static void Test0(int a, int b)
{
Span<int> foo = [a, b];
ReadOnlySpan<int> bar = [a, b];
} emits
Or when running: int a = 3;
int b = 4;
long allocated0 = GC.GetTotalAllocatedBytes(precise: true);
Test0(a, b);
long allocated1 = GC.GetTotalAllocatedBytes(precise: true);
Console.WriteLine(allocated1 - allocated0);
static void Test0(int a, int b)
{
Span<int> foo = [a, b];
ReadOnlySpan<int> bar = [a, b];
} it prints (in Release):
but I'd expect with that optimization to be 0. Or when looking at godbolt (main) I see the two allocations that should not be there. |
Sounds like your C# compiler is too old. For your Test0, I get the equivalent of this: private static void Test0(int a, int b)
{
<>y__InlineArray2<int> buffer = default(<>y__InlineArray2<int>);
global::<PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<int>, int>(ref buffer, 0) = a;
global::<PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<int>, int>(ref buffer, 1) = b;
global::<PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray2<int>, int>(ref buffer, 2);
<>y__InlineArray2<int> buffer2 = default(<>y__InlineArray2<int>);
global::<PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<int>, int>(ref buffer2, 0) = a;
global::<PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<int>, int>(ref buffer2, 1) = b;
global::<PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray2<int>, int>(in buffer2, 2);
} |
Fine, than it's as intended / expected. Thanks for clarification!
Today .NET 8 (RC2 and RTM (latest 8.0.1xx)) is already too old for this optimization... |
That should not be the case. Are you building in VS or from the command line? Regardless, you need a compiler with this: |
VS tricked me into this. Thanks! |
This broke a bunch of |
Sounds like it's an issue with the mono interpreter not handling [InlineArray]s correctly. |
These will result in using an InlineArray rather than a localloc