-
Notifications
You must be signed in to change notification settings - Fork 838
Description
Package
OpenTelemetry.Exporter.OpenTelemetryProtocol
Is your feature request related to a problem?
Improve Protobuff Serialization Strategy: Replace ThreadStatic Array with Segmented IBufferWriter<byte>
Description
Currently, the protobuf serialization strategy relies on a ThreadStatic
-anchored byte array that grows as needed, involving data copying on each resize. This approach has several limitations in terms of performance, memory efficiency, and scalability.
Proposal
Replace the current buffer implementation with a segmented buffer that:
- Implements
IBufferWriter<byte>
- Produces a
ReadOnlySequence<byte>
for downstream consumption
Benefits of Segmented Buffer Approach
✅ Zero-Copy Composition
ReadOnlySequence<byte>
allows chaining multiple memory segments without copying data.- Avoids the overhead of reallocating and copying data as the buffer grows.
✅ Better Memory Utilization
- Segments can be pooled and reused, reducing GC pressure.
- Avoids large contiguous allocations, which are more prone to fragmentation and LOH (Large Object Heap) allocations.
✅ Thread Safety and Isolation
ThreadStatic
buffers are shared across all usages on the same thread, which can lead to subtle bugs or data leaks.- A segmented buffer can be scoped per operation, ensuring isolation and correctness.
✅ Scalability
- Segmented buffers scale better in high-throughput or concurrent environments.
- They can be backed by
ArrayPool<byte>
or custom memory pools for fine-grained control.
✅ Compatibility with Pipelines and Span-based APIs
IBufferWriter<byte>
is the standard interface used bySystem.IO.Pipelines
and other modern .NET APIs.- Enables seamless integration with serializers, encoders, and network stacks.
Drawbacks of Current ThreadStatic Array Approach
- ❌ Requires manual resizing and copying, which is error-prone and inefficient.
- ❌ Can lead to memory leaks or excessive memory retention if not carefully managed.
- ❌ Not composable or compatible with modern .NET streaming APIs.
Suggested Implementation
Introduce a PooledSegmentedBufferWriter
class that:
- Implements
IBufferWriter<byte>
- Internally manages a list of pooled segments
- Exposes a
ReadOnlySequence<byte>
for reading
What is the expected behavior?
The protobuf serialization process should use a PooledSegmentedBufferWriter
to efficiently accumulate serialized data without reallocating or copying memory as the buffer grows. The writer should:
- Dynamically allocate and reuse pooled memory segments.
- Implement
IBufferWriter<byte>
to support efficient, span-based writing. - Produce a
ReadOnlySequence<byte>
that represents the complete serialized payload without requiring additional copying or transformation. - Avoid reliance on
ThreadStatic
buffers, ensuring thread safety and reducing memory retention risks. - Integrate seamlessly with existing serialization pipelines and support high-throughput scenarios with minimal GC overhead.
Which alternative solutions or features have you considered?
Alternative 1: ThreadStatic Byte Array with Manual Resizing (Current Implementation)
This approach uses a ThreadStatic
-anchored byte array that grows as needed. While it avoids allocations in some cases by reusing the same buffer per thread, it has several drawbacks:
- Requires manual resizing and copying, which is inefficient and error-prone.
- Can lead to memory retention issues if buffers grow large and are never reset.
- Not safe across async boundaries or parallel operations.
- Difficult to integrate with modern span-based or streaming APIs.
Alternative 2: ArrayPool with Manual Buffer Management
Using ArrayPool<byte>
to rent and return buffers is a common way to reduce allocations:
- Avoids repeated allocations by reusing pooled arrays.
- Still requires manual resizing and copying when the buffer grows.
- Does not support segmented composition — the buffer must be contiguous.
- Managing lifetime and return of the buffer adds complexity and risk of leaks or corruption.
Alternative 3: RecyclableMemoryStream
RecyclableMemoryStream
from the Microsoft.IO package is a high-performance, pooled stream implementation that:
- Implements
IBufferWriter<byte>
out of the box. - Can produce a
ReadOnlySequence<byte>
. - Reduces LOH allocations and GC pressure through segment pooling.
While this is a strong candidate, it introduces an external dependency and adds some complexity in terms of configuration and diagnostics. It also abstracts away some control over segment size and memory layout, which may be important in performance-critical scenarios.
Why PooledSegmentedBufferWriter
is the Best Option
- Offers fine-grained control over segment size, pooling strategy, and memory layout.
- Fully compatible with
IBufferWriter<byte>
andReadOnlySequence<byte>
, enabling efficient
Additional context
No response
/cc @Kielek, @martincostello