Skip to content

Commit

Permalink
Merge pull request #1457 from AArnott/reduceLOHimpact
Browse files Browse the repository at this point in the history
Add option to avoid large buffer allocations
  • Loading branch information
AArnott authored Jun 27, 2022
2 parents f94fbf5 + 9a2acd8 commit 64ee45c
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ namespace MessagePack
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Each overload has sufficiently unique required parameters.")]
public static partial class MessagePackSerializer
{
private const int MaxHintSize = 1024 * 1024;
private static MessagePackSerializerOptions defaultOptions;

/// <summary>
Expand Down Expand Up @@ -349,7 +348,7 @@ public static T Deserialize<T>(Stream stream, MessagePackSerializerOptions optio
do
{
cancellationToken.ThrowIfCancellationRequested();
Span<byte> span = sequence.GetSpan(stream.CanSeek ? (int)Math.Min(MaxHintSize, stream.Length - stream.Position) : 0);
Span<byte> span = sequence.GetSpan(stream.CanSeek ? (int)Math.Min(options.SuggestedContiguousMemorySize, stream.Length - stream.Position) : 0);
bytesRead = stream.Read(span);
sequence.Advance(bytesRead);
}
Expand Down Expand Up @@ -397,7 +396,7 @@ public static async ValueTask<T> DeserializeAsync<T>(Stream stream, MessagePackS
int bytesRead;
do
{
Memory<byte> memory = sequence.GetMemory(stream.CanSeek ? (int)Math.Min(MaxHintSize, stream.Length - stream.Position) : 0);
Memory<byte> memory = sequence.GetMemory(stream.CanSeek ? (int)Math.Min(options.SuggestedContiguousMemorySize, stream.Length - stream.Position) : 0);
bytesRead = await stream.ReadAsync(memory, cancellationToken).ConfigureAwait(false);
sequence.Advance(bytesRead);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ protected MessagePackSerializerOptions(MessagePackSerializerOptions copyFrom)
this.Resolver = copyFrom.Resolver;
this.Compression = copyFrom.Compression;
this.CompressionMinLength = copyFrom.CompressionMinLength;
this.SuggestedContiguousMemorySize = copyFrom.SuggestedContiguousMemorySize;
this.OldSpec = copyFrom.OldSpec;
this.OmitAssemblyVersion = copyFrom.OmitAssemblyVersion;
this.AllowAssemblyVersionMismatch = copyFrom.AllowAssemblyVersionMismatch;
Expand Down Expand Up @@ -92,6 +93,16 @@ protected MessagePackSerializerOptions(MessagePackSerializerOptions copyFrom)
/// </remarks>
public int CompressionMinLength { get; private set; } = 64;

/// <summary>
/// Gets the size of contiguous memory blocks in bytes that may be allocated for buffering purposes.
/// </summary>
/// <value>The default value is 1MB.</value>
/// <remarks>
/// Larger values may perform a bit faster, but may result in adding a runtime perf tax due to using the
/// <see href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap">Large Object Heap</see>.
/// </remarks>
public int SuggestedContiguousMemorySize { get; private set; } = 1024 * 1024;

/// <summary>
/// Gets a value indicating whether to serialize with <see cref="MessagePackWriter.OldSpec"/> set to some value
/// causing messagepack spec compliance to be explicitly set to the old or new format.
Expand Down Expand Up @@ -227,6 +238,28 @@ public MessagePackSerializerOptions WithCompressionMinLength(int compressionMinL
return result;
}

/// <summary>
/// Gets a copy of these options with the <see cref="SuggestedContiguousMemorySize"/> property set to a new value.
/// </summary>
/// <param name="suggestedContiguousMemorySize">The new value for the <see cref="SuggestedContiguousMemorySize"/> property. Must be at least 256.</param>
/// <returns>The new instance; or the original if the value is unchanged.</returns>
public MessagePackSerializerOptions WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize)
{
if (this.SuggestedContiguousMemorySize == suggestedContiguousMemorySize)
{
return this;
}

if (suggestedContiguousMemorySize < 256)
{
throw new ArgumentOutOfRangeException(nameof(suggestedContiguousMemorySize), "This should be at least 256");
}

var result = this.Clone();
result.SuggestedContiguousMemorySize = suggestedContiguousMemorySize;
return result;
}

/// <summary>
/// Gets a copy of these options with the <see cref="OldSpec"/> property set to a new value.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public class MessagePackSerializerOptionsTests
.WithOmitAssemblyVersion(true)
.WithResolver(BuiltinResolver.Instance)
.WithOldSpec(false)
.WithSecurity(MySecurityOptions.Instance);
.WithSecurity(MySecurityOptions.Instance)
.WithSuggestedContiguousMemorySize(64 * 1024);

[Fact]
public void AllowAssemblyVersionMismatch()
Expand Down Expand Up @@ -47,6 +48,16 @@ public void CompressionMinLength()
Assert.Equal(128, options.CompressionMinLength);
}

[Fact]
public void SuggestedContiguousMemorySize()
{
Assert.Equal(1024 * 1024, MessagePackSerializerOptions.Standard.SuggestedContiguousMemorySize);
Assert.Throws<ArgumentOutOfRangeException>(() => MessagePackSerializerOptions.Standard.WithSuggestedContiguousMemorySize(0));
Assert.Throws<ArgumentOutOfRangeException>(() => MessagePackSerializerOptions.Standard.WithSuggestedContiguousMemorySize(4));
MessagePackSerializerOptions options = MessagePackSerializerOptions.Standard.WithSuggestedContiguousMemorySize(512);
Assert.Equal(512, options.SuggestedContiguousMemorySize);
}

[Fact]
public void OldSpec()
{
Expand Down Expand Up @@ -74,6 +85,7 @@ public void WithOldSpec_PreservesOtherProperties()
var mutated = NonDefaultOptions.WithOldSpec(true);
Assert.True(mutated.OldSpec.Value);
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
Expand All @@ -92,12 +104,26 @@ public void WithLZ4Compression_PreservesOtherProperties()
Assert.Same(MySecurityOptions.Instance, mutated.Security);
}

[Fact]
public void WithSuggestedContiguousMemorySize_PreservesOtherProperties()
{
var mutated = NonDefaultOptions.WithSuggestedContiguousMemorySize(612);
Assert.Equal(612, mutated.SuggestedContiguousMemorySize);
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
Assert.Same(MySecurityOptions.Instance, mutated.Security);
}

[Fact]
public void WithAllowAssemblyVersionMismatch_PreservesOtherProperties()
{
var mutated = NonDefaultOptions.WithAllowAssemblyVersionMismatch(false);
Assert.False(mutated.AllowAssemblyVersionMismatch);
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
Expand All @@ -110,6 +136,7 @@ public void WithOmitAssemblyVersion_PreservesOtherProperties()
var mutated = NonDefaultOptions.WithOmitAssemblyVersion(false);
Assert.False(mutated.OmitAssemblyVersion);
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
Expand All @@ -122,6 +149,7 @@ public void WithResolver_PreservesOtherProperties()
var mutated = NonDefaultOptions.WithResolver(ContractlessStandardResolver.Instance);
Assert.Same(ContractlessStandardResolver.Instance, mutated.Resolver);
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
Expand All @@ -135,6 +163,7 @@ public void WithSecurity_PreservesOtherProperties()
Assert.Same(MessagePackSecurity.TrustedData, mutated.Security);
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
Expand Down
2 changes: 2 additions & 0 deletions src/MessagePack/net6.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ MessagePack.Formatters.TimeOnlyFormatter
MessagePack.Formatters.TimeOnlyFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions options) -> System.TimeOnly
MessagePack.Formatters.TimeOnlyFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.TimeOnly value, MessagePack.MessagePackSerializerOptions options) -> void
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
MessagePack.MessagePackSerializerOptions.WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize) -> MessagePack.MessagePackSerializerOptions
static readonly MessagePack.Formatters.DateOnlyFormatter.Instance -> MessagePack.Formatters.DateOnlyFormatter
static readonly MessagePack.Formatters.TimeOnlyFormatter.Instance -> MessagePack.Formatters.TimeOnlyFormatter
2 changes: 2 additions & 0 deletions src/MessagePack/netcoreapp3.1/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ MessagePack.Formatters.StringInterningFormatter.Deserialize(ref MessagePack.Mess
MessagePack.Formatters.StringInterningFormatter.Serialize(ref MessagePack.MessagePackWriter writer, string value, MessagePack.MessagePackSerializerOptions options) -> void
MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> void
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
MessagePack.MessagePackSerializerOptions.WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize) -> MessagePack.MessagePackSerializerOptions
4 changes: 3 additions & 1 deletion src/MessagePack/netstandard2.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ MessagePack.Formatters.StringInterningFormatter.Deserialize(ref MessagePack.Mess
MessagePack.Formatters.StringInterningFormatter.Serialize(ref MessagePack.MessagePackWriter writer, string value, MessagePack.MessagePackSerializerOptions options) -> void
MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> void
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
MessagePack.MessagePackSerializerOptions.WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize) -> MessagePack.MessagePackSerializerOptions

0 comments on commit 64ee45c

Please sign in to comment.