-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Unnecessary buffer allocations when writing base64 values with Utf8JsonWriter
.
#97628
Comments
Tagging subscribers to this area: @dotnet/area-system-text-encoding Issue DetailsDescription
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Base64EncodeAndWrite(ReadOnlySpan<byte> bytes, Span<byte> output, int encodingLength)
{
byte[]? outputText = null;
Span<byte> encodedBytes = encodingLength <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(outputText = ArrayPool<byte>.Shared.Rent(encodingLength));
OperationStatus status = Base64.EncodeToUtf8(bytes, encodedBytes, out int consumed, out int written);
Debug.Assert(status == OperationStatus.Done);
Debug.Assert(consumed == bytes.Length);
encodedBytes = encodedBytes.Slice(0, written);
Span<byte> destination = output.Slice(BytesPending);
Debug.Assert(destination.Length >= written);
encodedBytes.Slice(0, written).CopyTo(destination);
BytesPending += written;
if (outputText != null)
{
ArrayPool<byte>.Shared.Return(outputText);
}
} The I think we can skip the temporary buffer and perform the encoding directly into the output buffer like in the snippet below: private void Base64EncodeAndWrite(ReadOnlySpan<byte> bytes, Span<byte> output, int encodingLength)
{
Span<byte> destination = output.Slice(BytesPending);
OperationStatus status = Base64.EncodeToUtf8(
bytes,
destination,
out int consumed,
out int written
);
// removed Debug.Assert statements for brevity
BytesPending += written;
} If you think the proposed change is okay, I can go ahead and create a PR. ConfigurationRegression?DataHere's sample where
|
Seems like something we could address in conjunction with #67337 |
Hi @eiriktsarpalis, should I work on a PR or wait for #67337 to be addressed? |
Feel free to submit a PR if you can, #67337 is not being prioritized currently. Thanks. |
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis Issue DetailsDescription
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Base64EncodeAndWrite(ReadOnlySpan<byte> bytes, Span<byte> output, int encodingLength)
{
byte[]? outputText = null;
Span<byte> encodedBytes = encodingLength <= JsonConstants.StackallocByteThreshold ?
stackalloc byte[JsonConstants.StackallocByteThreshold] :
(outputText = ArrayPool<byte>.Shared.Rent(encodingLength));
OperationStatus status = Base64.EncodeToUtf8(bytes, encodedBytes, out int consumed, out int written);
Debug.Assert(status == OperationStatus.Done);
Debug.Assert(consumed == bytes.Length);
encodedBytes = encodedBytes.Slice(0, written);
Span<byte> destination = output.Slice(BytesPending);
Debug.Assert(destination.Length >= written);
encodedBytes.Slice(0, written).CopyTo(destination);
BytesPending += written;
if (outputText != null)
{
ArrayPool<byte>.Shared.Return(outputText);
}
} The I think we can skip the temporary buffer and perform the encoding directly into the output buffer like in the snippet below: private void Base64EncodeAndWrite(ReadOnlySpan<byte> bytes, Span<byte> output, int encodingLength)
{
Span<byte> destination = output.Slice(BytesPending);
OperationStatus status = Base64.EncodeToUtf8(
bytes,
destination,
out int consumed,
out int written
);
// removed Debug.Assert statements for brevity
BytesPending += written;
} If you think the proposed change is okay, I can go ahead and create a PR. Configuration.NET 8 Regression?DataHere's sample call stack where
|
Description
Utf8JsonWriter.WriteBase64StringValue(ReadOnlySpan<byte>)
implementation calls the following method to base64-encode the input bytes and write the result to the output buffer:The
Base64EncodeAndWrite
method allocates/rents theencodedBytes
buffer, uses it to perform the base64 encoding then copies the contents to the output buffer. When you have a lot of concurrent writes (from different requests) and values larger than 256 bytes, the allocations can start to add up.I think we can skip the temporary buffer and perform the encoding directly into the output buffer like in the snippet below:
If you think the proposed change is okay, I can go ahead and create a PR.
Configuration
.NET 8
Regression?
Data
Here's sample call stack where
Base64EncodeAndWrite
contributes tobyte[]
allocations in the large-object-heap.The text was updated successfully, but these errors were encountered: