Skip to content

Use ReadOnlySpan<byte> for zero-allocation JSON parsing #5

@GmausDev

Description

@GmausDev

Summary

Optimize JSON deserialization by using ReadOnlySpan<byte> and Utf8JsonReader to reduce string allocations during response parsing.

Current Behavior

var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
var result = JsonSerializer.Deserialize(responseBody, jsonTypeInfo);

This creates an intermediate string allocation for the entire response body.

Proposed Solution

Use stream-based or span-based deserialization:

// Option 1: Stream-based (preferred for large responses)
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
var result = await JsonSerializer.DeserializeAsync(stream, jsonTypeInfo, cancellationToken);

// Option 2: Span-based (for responses that fit in stack/pooled buffer)
var buffer = await response.Content.ReadAsByteArrayAsync(cancellationToken);
var result = JsonSerializer.Deserialize(buffer.AsSpan(), jsonTypeInfo);

Advanced: Pooled Buffers

For optimal performance, use ArrayPool<byte>:

var length = (int)response.Content.Headers.ContentLength!;
var buffer = ArrayPool<byte>.Shared.Rent(length);
try
{
    await response.Content.ReadAsByteArrayAsync(...);
    var result = JsonSerializer.Deserialize(buffer.AsSpan(0, length), jsonTypeInfo);
}
finally
{
    ArrayPool<byte>.Shared.Return(buffer);
}

Expected Benefits

  • Reduced string allocations - No intermediate UTF-16 string
  • Lower memory pressure - Direct UTF-8 parsing
  • Faster parsing - Skip UTF-8 to UTF-16 conversion

Priority

🟢 P2 - Medium Impact

Technical Notes

  • Stream-based is simpler and handles large responses well
  • Span-based requires knowing content length upfront
  • ArrayPool approach is most efficient but adds complexity
  • Consider response size thresholds for different strategies

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions