-
Notifications
You must be signed in to change notification settings - Fork 344
Conversation
} | ||
} | ||
} | ||
|
||
// for invariant culture, we should never reach this point, as invariant uint text is never longer than 127 bytes. | ||
// I left this code here, as we will need it for custom cultures and possibly when we shrink the stack allocated buffer. | ||
combinedSpan = bufferSequence.ToSpan(); | ||
if (!PrimitiveParser.InvariantUtf8.TryParseUInt64(first.Span, out value, out consumed)) { |
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.
Was 'first.Span' a bug here?
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.
I am not sure but it seems like it given combinedSpan is assigned and then not used.
cc @KrzysztofCwalina
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.
I would not be shocked if this codepath never executed (as the comment implies)
@KrzysztofCwalina @ahsonkhan @shiftylogic - please review. |
@stephentoub @jaredpar - FYI. Using safe |
@dotnet-bot test Innerloop OSX10.12 Debug Build and Test |
// make 'remainder' formally stack-referring or we won't be able to reference stack data later | ||
ReadOnlySpan<byte> remainder = stackalloc byte[0]; | ||
var remainder = true ? new ReadOnlySpan<byte>() : stackalloc byte[0]; |
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.
nit: change from var to ReadOnlySpan, esp since in the future, the r.h.s would look like stackalloc byte[0];
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.
Will do. I was not sure if mentioning the type twice would be too verbose.
// TODO: "ReadOnlySpan<byte> textSpan = stackalloc byte[0]" would fit better here, | ||
// but it emits substandard IL, see https://github.com/dotnet/roslyn/issues/21952 | ||
// | ||
// make 'textSpan' formally stack-referring or we won't be able to reference stack data 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.
I do not understand this comment.
What does it mean by 'formally stack-referring'. Also, shouldn't it read:
make 'textSpan' formally stack-referring or we will be able to reference stack data later
If textSpan is a stackalloc variable, we can't reference it outside the method, correct? The comment seems to suggest that if we don't make it 'stack-referring', we won't be able to reference it later, which has the opposite meaning.
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.
If we do not assign anything, 'textSpan' will default to be a returnable variable. We will be able to return it from the method, but will not be able to assign stackalloc'd spans. That is what you normally want, but not in this case. Here we need to initialize to something stackallocated to intentionally "taint" the variable as "possibly referring to stack data".
'stackalloc byte[0]' would be a nice choice since it is basically the same as 'default(Span)', but formally stack-allocated. However we do a poor job with optimizing such trivial case, so I am using a more verbose, but reliable, way to essentially assign a default span, while forcing the variable be classified as stack-referring.
I will try to change the comment to be more clear.
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
This is pretty nice cleanup |
Span<byte> combined = combinedBufferLength < 128 ? | ||
stackalloc byte[combinedBufferLength] : | ||
// TODO (pri 3): I think this could be eliminated by chunking values | ||
combined = new byte[combinedBufferLength]; |
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.
Is this "combined =" on purpose or a mistake?
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.
typo, it is a redundant assignment
var pBuffer = stackalloc byte[AuthenticationHeaderBufferSize]; | ||
buffer = new Span<byte>(pBuffer, AuthenticationHeaderBufferSize); | ||
} | ||
Span<byte> buffer = stackalloc byte[AuthenticationHeaderBufferSize]; |
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.
Nice!
// TODO (pri 3): I think this could be eliminated by chunking values | ||
combined = new byte[combinedBufferLength]; | ||
} | ||
var combined = combinedBufferLength < 128 ? |
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.
I think we should settle on the following idiom:
Span<byte> buffer = size < 128 ? stackalloc byte[size] : new byte[size];
.. as opposed to assigning to var and then having to cast.
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.
As it stands now the dominant type of a ternary expression is selected from one of consequence or alternative branches.
If you have a span as a type of either of the branches, it will dominate. Otherwise compiler faces an ambiguity since no dominant type can be chosen.
I will bring this up to language folks.
There are some tradeoffs that we can have around the natural type of stackalloc
.
// Assign 'remainder' to something formally stack-referring. | ||
// The default classification is "returnable, not referring to stack", we want the opposite in this case. | ||
ReadOnlySpan<byte> remainder = true ? new ReadOnlySpan<byte>() : stackalloc byte[0]; | ||
Span<byte> stackSpan = stackalloc byte[stackLength]; |
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.
Will this be fixed before we ship?
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 we just use ReadOnlySpan.Empty here?
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.
@KrzysztofCwalina - yes, will fix this before shipping. The trivial stackalloc byte[0]
is emitted "correctly", but the IL is embarrassingly redundant, even worse after JIT-ting.
Optimizing this case in C# compiler backend will benefit existing code as well, so the the value vs. effort is very favorable here.
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.
@shiftylogic - if you use ReadOnlySpan.Empty or any other not stack allocated data to initialize the span you are telling the compiler - "I want to be able to return this from the method or mix with other returnables, please make sure I do not refer to stack data by accident".
That is generally the case.
Here we have a relatively rare opposite - We are slicing through stack allocated data and store in this variable, but have no intent to return it.
stackalloc byte[0]
will convey to the compiler - "please allow referring to stack data, make sure I can never return this from the method"
Wow, this is such a groundbreaking change to our platform where features in the run-time, libraries, and compilers come together so nicely. |
The most impressive was how quickly the combined efforts of all three teams made it a real thing. A year ago it was a prototype in CorefxLab. |
No description provided.