-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
[Http] Remove some unsafe code and save a string allocation #31267
Merged
Merged
Changes from 3 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
08edaf0
[Http] Remove some unsafe code and save a string allocation
BrennanConroy 6d984ce
fb
BrennanConroy 2455646
fb
BrennanConroy a03d204
fb
BrennanConroy 58e6752
stackalloc
BrennanConroy a6e5563
Get exact base64 length, and remove another allocation
BrennanConroy 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 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
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
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
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
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
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
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.
Don't create a ReadOnlySequence, that's the wrong overload 😄
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.
There is no
Span<char>
orReadOnlySpan<char>
overload that I could see.The closest is
int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes);
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 see, yes, we don't want to allocate a buffer here 😄 . This is the missing overload of
Convert.ToBase64String
that we need! Conversion from utf16 to utf16:@GrabYourPitchforks thoughts? I'd like to get an API added for this on Convert wdyt?
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.
@BrennanConroy go ahead with what you have for now. I'll file an issue on dotnet/runtime.
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.
What about using https://docs.microsoft.com/en-us/dotnet/api/system.buffers.text.base64?
You'd need to encode string into UTF8 bytes using
stackalloc byte[]
first. You could then encode in place (stackalloc would need to be long enough to go from bytes to base64), set consts prefix and suffix bytes (again, stackalloc byte[] would need to be long enough), and then turn the byte[] back into a string.You can reduce this method down to one string allocation. I don't know how often this is called so probably not worth the complexity.
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.
https://github.com/dotnet/runtime/blob/82ca681cbac89d813a3ce397e0c665e6c051ed67/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs#L155 that's the same and simpler calculation. Or use that method, it's already public.
Right. But this needs a new
Convert.ToBase64Span
method.My concern is only about having two base64-API sets. One in Convert-class and one in System.Buffers.Text.Base64. I'd prefer the latter, as it's name clearly indicates it is about base64.
@BrennanConroy just to note this method is not vectorized, whilst
Base64.EncodeToUtf8
is for size >= 16. So depending on the measure here it may be more advantageous (see second snippet above).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.
For some reason the following code is slower and supposedly allocates more than the current impl (using BenchmarkDotnet):
And test:
Comment above:
Current:
I'm going to stop work on this and merge as is assuming there are no glaring issues with the recent changes during PR review.
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 Roslyn optimization that embeds arrays of consts exposed as ReadOnlySpans into the data section, thereby avoiding a per-access array allocation, only applies when the array is a byte[], sbyte[], or bool[], i.e. when it's an array of byte-sized types. Otherwise, there are endianness concerns. dotnet/runtime#24961 would be needed and then Roslyn respecting it before it could be used with other types, like the char[] in your repro.
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.
Makes sense, didn't think about that optimization only applying to
ReadOnlySpan<byte>
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.
Yeah, it's subtle.