Skip to content

Reduce Allocations in Envelope Serialization #273

@JanHyka

Description

@JanHyka

Describe the feature

The current implementation of EnvelopeSerializer utilizes JsonNode for message serialization. While functional, this approach introduces unnecessary allocations and increased memory usage, particularly in high-throughput scenarios. There is an opportunity to significantly reduce allocations and improve performance by leveraging Utf8JsonWriter over rented buffers, thereby enabling buffer reuse and minimizing memory overhead per message.

Use Case

Serialization of any enveloped payload passing through the library.

Proposed Solution

Serialization Optimization

Replace JsonNode Usage:

Transition the serialization process from JsonNode to Utf8JsonWriter, utilizing rented memory buffers. This change will reduce object allocations and improve memory management, especially under heavy load.

Interface Compatibility:

To maintain backward compatibility, the EnvelopeSerializer.SerializeAsync method should continue to return a string. In the future, further optimizations could be realized by returning a buffer directly, but such a change would introduce a breaking change to the API.

Potential for Future Improvements:

Passing a buffer to EnvelopeSerializer.SerializeAsync would allow dropping string allocations. With possible long term solution entirely working on rented buffers only across all layers.

Interface Enhancements

Introduce Non-Allocating Serialization:

The current IMessageSerializer interface’s MessageSerializerResult Serialize method is incompatible with non-allocating serialization scenarios. To address this, a new method is proposed:

ValueTask<string> SerializeToBufferAsync(Utf8JsonWriter writer, object? value, ...);
where return value is content type.

  • Note: Evaluate whether it is feasible to utilize IBufferedWriter (to avoid tight coupling to Utf8JsonWriter) without disrupting the typical Utf8JsonWriter usage patterns.

Incremental Interface Introduction:

To ensure a smooth transition, introduce a new interface, IJsonWriterMessageSerializer, which derives from IMessageSerializer and adds the proposed non-allocating serialization method. This approach allows incremental adoption without breaking existing consumers.

public interface IJsonWriterMessageSerializer : IMessageSerializer { ValueTask<string> SerializeToBufferAsync(Utf8JsonWriter writer, object? value, ...); }

Runtime Detection and Adaptation:

Serialization code should detect at runtime whether a serializer implements IJsonWriterMessageSerializer. If so, it should delegate to the new method. This introduces a minimal runtime overhead (an additional type check) but enables immediate performance gains for updated serializers.

Other Information

PoC with expectable savings stored in #272
PoC solution cleans all rented memory. This adds some overhead but keeps the solution safer.

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

AWS.Messaging (or related) package versions

AWS.Messaging 1.0.1

Targeted .NET Platform

.NET 8

Operating System and version

any

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature-requestA feature should be added or improved.p2This is a standard priority issuequeued

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions