Skip to content

Commit 9a2acd8

Browse files
committed
Add option to avoid large buffer allocations
Allocating 1MB buffers contribute to the large object heap and should be avoidable for applications that it is a problem for.
1 parent 888ecb2 commit 9a2acd8

File tree

6 files changed

+72
-5
lines changed

6 files changed

+72
-5
lines changed

src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ namespace MessagePack
1818
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Each overload has sufficiently unique required parameters.")]
1919
public static partial class MessagePackSerializer
2020
{
21-
private const int MaxHintSize = 1024 * 1024;
2221
private static MessagePackSerializerOptions defaultOptions;
2322

2423
/// <summary>
@@ -349,7 +348,7 @@ public static T Deserialize<T>(Stream stream, MessagePackSerializerOptions optio
349348
do
350349
{
351350
cancellationToken.ThrowIfCancellationRequested();
352-
Span<byte> span = sequence.GetSpan(stream.CanSeek ? (int)Math.Min(MaxHintSize, stream.Length - stream.Position) : 0);
351+
Span<byte> span = sequence.GetSpan(stream.CanSeek ? (int)Math.Min(options.SuggestedContiguousMemorySize, stream.Length - stream.Position) : 0);
353352
bytesRead = stream.Read(span);
354353
sequence.Advance(bytesRead);
355354
}
@@ -397,7 +396,7 @@ public static async ValueTask<T> DeserializeAsync<T>(Stream stream, MessagePackS
397396
int bytesRead;
398397
do
399398
{
400-
Memory<byte> memory = sequence.GetMemory(stream.CanSeek ? (int)Math.Min(MaxHintSize, stream.Length - stream.Position) : 0);
399+
Memory<byte> memory = sequence.GetMemory(stream.CanSeek ? (int)Math.Min(options.SuggestedContiguousMemorySize, stream.Length - stream.Position) : 0);
401400
bytesRead = await stream.ReadAsync(memory, cancellationToken).ConfigureAwait(false);
402401
sequence.Advance(bytesRead);
403402
}

src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializerOptions.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ protected MessagePackSerializerOptions(MessagePackSerializerOptions copyFrom)
5858
this.Resolver = copyFrom.Resolver;
5959
this.Compression = copyFrom.Compression;
6060
this.CompressionMinLength = copyFrom.CompressionMinLength;
61+
this.SuggestedContiguousMemorySize = copyFrom.SuggestedContiguousMemorySize;
6162
this.OldSpec = copyFrom.OldSpec;
6263
this.OmitAssemblyVersion = copyFrom.OmitAssemblyVersion;
6364
this.AllowAssemblyVersionMismatch = copyFrom.AllowAssemblyVersionMismatch;
@@ -92,6 +93,16 @@ protected MessagePackSerializerOptions(MessagePackSerializerOptions copyFrom)
9293
/// </remarks>
9394
public int CompressionMinLength { get; private set; } = 64;
9495

96+
/// <summary>
97+
/// Gets the size of contiguous memory blocks in bytes that may be allocated for buffering purposes.
98+
/// </summary>
99+
/// <value>The default value is 1MB.</value>
100+
/// <remarks>
101+
/// Larger values may perform a bit faster, but may result in adding a runtime perf tax due to using the
102+
/// <see href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap">Large Object Heap</see>.
103+
/// </remarks>
104+
public int SuggestedContiguousMemorySize { get; private set; } = 1024 * 1024;
105+
95106
/// <summary>
96107
/// Gets a value indicating whether to serialize with <see cref="MessagePackWriter.OldSpec"/> set to some value
97108
/// causing messagepack spec compliance to be explicitly set to the old or new format.
@@ -227,6 +238,28 @@ public MessagePackSerializerOptions WithCompressionMinLength(int compressionMinL
227238
return result;
228239
}
229240

241+
/// <summary>
242+
/// Gets a copy of these options with the <see cref="SuggestedContiguousMemorySize"/> property set to a new value.
243+
/// </summary>
244+
/// <param name="suggestedContiguousMemorySize">The new value for the <see cref="SuggestedContiguousMemorySize"/> property. Must be at least 256.</param>
245+
/// <returns>The new instance; or the original if the value is unchanged.</returns>
246+
public MessagePackSerializerOptions WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize)
247+
{
248+
if (this.SuggestedContiguousMemorySize == suggestedContiguousMemorySize)
249+
{
250+
return this;
251+
}
252+
253+
if (suggestedContiguousMemorySize < 256)
254+
{
255+
throw new ArgumentOutOfRangeException(nameof(suggestedContiguousMemorySize), "This should be at least 256");
256+
}
257+
258+
var result = this.Clone();
259+
result.SuggestedContiguousMemorySize = suggestedContiguousMemorySize;
260+
return result;
261+
}
262+
230263
/// <summary>
231264
/// Gets a copy of these options with the <see cref="OldSpec"/> property set to a new value.
232265
/// </summary>

src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerOptionsTests.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public class MessagePackSerializerOptionsTests
1414
.WithOmitAssemblyVersion(true)
1515
.WithResolver(BuiltinResolver.Instance)
1616
.WithOldSpec(false)
17-
.WithSecurity(MySecurityOptions.Instance);
17+
.WithSecurity(MySecurityOptions.Instance)
18+
.WithSuggestedContiguousMemorySize(64 * 1024);
1819

1920
[Fact]
2021
public void AllowAssemblyVersionMismatch()
@@ -47,6 +48,16 @@ public void CompressionMinLength()
4748
Assert.Equal(128, options.CompressionMinLength);
4849
}
4950

51+
[Fact]
52+
public void SuggestedContiguousMemorySize()
53+
{
54+
Assert.Equal(1024 * 1024, MessagePackSerializerOptions.Standard.SuggestedContiguousMemorySize);
55+
Assert.Throws<ArgumentOutOfRangeException>(() => MessagePackSerializerOptions.Standard.WithSuggestedContiguousMemorySize(0));
56+
Assert.Throws<ArgumentOutOfRangeException>(() => MessagePackSerializerOptions.Standard.WithSuggestedContiguousMemorySize(4));
57+
MessagePackSerializerOptions options = MessagePackSerializerOptions.Standard.WithSuggestedContiguousMemorySize(512);
58+
Assert.Equal(512, options.SuggestedContiguousMemorySize);
59+
}
60+
5061
[Fact]
5162
public void OldSpec()
5263
{
@@ -74,6 +85,7 @@ public void WithOldSpec_PreservesOtherProperties()
7485
var mutated = NonDefaultOptions.WithOldSpec(true);
7586
Assert.True(mutated.OldSpec.Value);
7687
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
88+
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
7789
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
7890
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
7991
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
@@ -92,12 +104,26 @@ public void WithLZ4Compression_PreservesOtherProperties()
92104
Assert.Same(MySecurityOptions.Instance, mutated.Security);
93105
}
94106

107+
[Fact]
108+
public void WithSuggestedContiguousMemorySize_PreservesOtherProperties()
109+
{
110+
var mutated = NonDefaultOptions.WithSuggestedContiguousMemorySize(612);
111+
Assert.Equal(612, mutated.SuggestedContiguousMemorySize);
112+
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
113+
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
114+
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
115+
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
116+
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
117+
Assert.Same(MySecurityOptions.Instance, mutated.Security);
118+
}
119+
95120
[Fact]
96121
public void WithAllowAssemblyVersionMismatch_PreservesOtherProperties()
97122
{
98123
var mutated = NonDefaultOptions.WithAllowAssemblyVersionMismatch(false);
99124
Assert.False(mutated.AllowAssemblyVersionMismatch);
100125
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
126+
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
101127
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
102128
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
103129
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
@@ -110,6 +136,7 @@ public void WithOmitAssemblyVersion_PreservesOtherProperties()
110136
var mutated = NonDefaultOptions.WithOmitAssemblyVersion(false);
111137
Assert.False(mutated.OmitAssemblyVersion);
112138
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
139+
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
113140
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
114141
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
115142
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
@@ -122,6 +149,7 @@ public void WithResolver_PreservesOtherProperties()
122149
var mutated = NonDefaultOptions.WithResolver(ContractlessStandardResolver.Instance);
123150
Assert.Same(ContractlessStandardResolver.Instance, mutated.Resolver);
124151
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
152+
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
125153
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
126154
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
127155
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);
@@ -135,6 +163,7 @@ public void WithSecurity_PreservesOtherProperties()
135163
Assert.Same(MessagePackSecurity.TrustedData, mutated.Security);
136164
Assert.Same(NonDefaultOptions.Resolver, mutated.Resolver);
137165
Assert.Equal(NonDefaultOptions.Compression, mutated.Compression);
166+
Assert.Equal(NonDefaultOptions.SuggestedContiguousMemorySize, mutated.SuggestedContiguousMemorySize);
138167
Assert.Equal(NonDefaultOptions.OldSpec, mutated.OldSpec);
139168
Assert.Equal(NonDefaultOptions.AllowAssemblyVersionMismatch, mutated.AllowAssemblyVersionMismatch);
140169
Assert.Equal(NonDefaultOptions.OmitAssemblyVersion, mutated.OmitAssemblyVersion);

src/MessagePack/net6.0/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ MessagePack.Formatters.TimeOnlyFormatter
99
MessagePack.Formatters.TimeOnlyFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions options) -> System.TimeOnly
1010
MessagePack.Formatters.TimeOnlyFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.TimeOnly value, MessagePack.MessagePackSerializerOptions options) -> void
1111
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
12+
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
1213
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
14+
MessagePack.MessagePackSerializerOptions.WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize) -> MessagePack.MessagePackSerializerOptions
1315
static readonly MessagePack.Formatters.DateOnlyFormatter.Instance -> MessagePack.Formatters.DateOnlyFormatter
1416
static readonly MessagePack.Formatters.TimeOnlyFormatter.Instance -> MessagePack.Formatters.TimeOnlyFormatter

src/MessagePack/netcoreapp3.1/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ MessagePack.Formatters.StringInterningFormatter.Deserialize(ref MessagePack.Mess
33
MessagePack.Formatters.StringInterningFormatter.Serialize(ref MessagePack.MessagePackWriter writer, string value, MessagePack.MessagePackSerializerOptions options) -> void
44
MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> void
55
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
6+
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
67
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
8+
MessagePack.MessagePackSerializerOptions.WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize) -> MessagePack.MessagePackSerializerOptions

src/MessagePack/netstandard2.0/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ MessagePack.Formatters.StringInterningFormatter.Deserialize(ref MessagePack.Mess
33
MessagePack.Formatters.StringInterningFormatter.Serialize(ref MessagePack.MessagePackWriter writer, string value, MessagePack.MessagePackSerializerOptions options) -> void
44
MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> void
55
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
6-
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
6+
MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int
7+
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
8+
MessagePack.MessagePackSerializerOptions.WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize) -> MessagePack.MessagePackSerializerOptions

0 commit comments

Comments
 (0)