Skip to content

Commit 64ee45c

Browse files
authored
Merge pull request MessagePack-CSharp#1457 from AArnott/reduceLOHimpact
Add option to avoid large buffer allocations
2 parents f94fbf5 + 9a2acd8 commit 64ee45c

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)