Skip to content

Commit

Permalink
improve List<T>, T[] serialization performance
Browse files Browse the repository at this point in the history
  • Loading branch information
neuecc committed Nov 17, 2022
1 parent e7aa0cf commit 1a18740
Show file tree
Hide file tree
Showing 21 changed files with 856 additions and 63 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,22 @@ There are a few restrictions on the types that can be generated. Among the primi

`[GenerateTypeScript]` can only be applied to classes and is currently not supported by struct.

### Configure import file extension

In default, MemoryPack generates file extension as `.js` like `import { MemoryPackWriter } from "./MemoryPackWriter.js";`. If you want to change other extension or empty, use `MemoryPackGenerator_TypeScriptImportExtension` to configure it.

```xml
<ItemGroup>
<CompilerVisibleProperty Include="MemoryPackGenerator_TypeScriptOutputDirectory" />
<CompilerVisibleProperty Include="MemoryPackGenerator_TypeScriptImportExtension" />
</ItemGroup>
<PropertyGroup>
<MemoryPackGenerator_TypeScriptOutputDirectory>$(MSBuildProjectDirectory)\wwwroot\js\memorypack</MemoryPackGenerator_TypeScriptOutputDirectory>
<!-- allows empty -->
<MemoryPackGenerator_TypeScriptImportExtension></MemoryPackGenerator_TypeScriptImportExtension>
</PropertyGroup>
```

Streaming Serialization
---
`MemoryPack.Streaming` provides additional `MemoryPackStreamingSerializer`, it serialize/deserialize collection data streamingly.
Expand Down
75 changes: 75 additions & 0 deletions sandbox/Benchmark/Benchmarks/ListFormatterVsDirect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Benchmark.BenchmarkNetUtilities;
using Benchmark.Models;
using MemoryPack;
using MemoryPack.Formatters;
using Orleans.Serialization.Buffers;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Benchmark.Benchmarks;

public class ListFormatterVsDirect
{
List<MyClass> value;
byte[] bytes;
IMemoryPackFormatter<List<MyClass?>> formatter;
ArrayBufferWriter<byte> buffer;
MemoryPackWriterOptionalState state;
MemoryPackReaderOptionalState state2;

public ListFormatterVsDirect()
{
value = Enumerable.Range(0, 100)
.Select(_ => new MyClass { X = 100, Y = 99999999, Z = 4444, FirstName = "Hoge Huga Tako", LastName = "あいうえおかきくけこ" })
.ToList();
bytes = MemoryPackSerializer.Serialize(value);
formatter = new ListFormatter<MyClass>();
buffer = new ArrayBufferWriter<byte>(bytes.Length);

state = MemoryPackWriterOptionalStatePool.Rent(null);
state2 = MemoryPackReaderOptionalStatePool.Rent(null);
}

[Benchmark, BenchmarkCategory(Categories.Serialize)]
public void SerializeFormatter()
{
var writer = new MemoryPackWriter<ArrayBufferWriter<byte>>(ref buffer, state);
formatter.Serialize(ref writer, ref value!);
writer.Flush();
buffer.Clear();
}

[Benchmark, BenchmarkCategory(Categories.Serialize)]
public void SerializePackable()
{
var writer = new MemoryPackWriter<ArrayBufferWriter<byte>>(ref buffer, state);
MemoryPack.Formatters.ListFormatter.SerializePackable(ref writer, ref value!);
writer.Flush();
buffer.Clear();
}


[Benchmark, BenchmarkCategory(Categories.Deserialize)]
public void DeserializeFormatter()
{
List<MyClass?>? list = null;
var reader = new MemoryPackReader(bytes, state2);
//reader.ReadPackableArray
// var a = MemoryPack.Formatters.ListFormatter.DeserializePackable<(ref reader);
formatter.Deserialize(ref reader, ref list);
reader.Dispose();
}

[Benchmark, BenchmarkCategory(Categories.Deserialize)]
public void DeserializePackable()
{
List<MyClass?>? list = null;
var reader = new MemoryPackReader(bytes, state2);
ListFormatter.DeserializePackable(ref reader, ref list!);
reader.Dispose();
}
}
4 changes: 2 additions & 2 deletions sandbox/Benchmark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

// BenchmarkRunner.Run<ConcurrentQueueVsStack>(config, args);


BenchmarkRunner.Run<ListFormatterVsDirect>(config, args);


//BenchmarkRunner.Run<Utf16VsUtf8>(config, args);
Expand All @@ -55,7 +55,7 @@
// BenchmarkRunner.Run<DeserializeTest<NeuralNetworkLayerModel>>(config, args);


BenchmarkRunner.Run<StaticDictionaryFormatterCheck>(config, args);
//BenchmarkRunner.Run<StaticDictionaryFormatterCheck>(config, args);
//BenchmarkRunner.Run<SerializeTest<JsonResponseModel>>(config, args);
//BenchmarkRunner.Run<DeserializeTest<JsonResponseModel>>(config, args);
//BenchmarkRunner.Run<SerializeTest<Vector3[]>>(config, args);
Expand Down
23 changes: 23 additions & 0 deletions sandbox/SandboxConsoleApp/Models.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using MemoryPack;
using MemoryPack.Formatters;
using MemoryPack.Internal;
using Newtonsoft.Json.Linq;
using System;
using System.Buffers;
using System.Collections.Generic;
Expand All @@ -13,6 +14,15 @@
namespace SandboxConsoleApp;


[MemoryPackable]
public partial class Mop
{
public NoGen? MyProperty { get; set; }
public LisList? MyLisList { get; set; }
public List<Suage>? SuageMan { get; set; }
}


[MemoryPackable]
public partial class NotSample
{
Expand Down Expand Up @@ -53,3 +63,16 @@ public partial class Suage
// this.Prop2 = prop2;
//}
}



[MemoryPackable(GenerateType.NoGenerate)]
public partial class NoGen
{
}

[MemoryPackable(GenerateType.Collection)]
public partial class LisList : List<int>
{

}
2 changes: 2 additions & 0 deletions sandbox/SandboxWebApp/SandboxWebApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

<ItemGroup>
<CompilerVisibleProperty Include="MemoryPackGenerator_TypeScriptOutputDirectory" />
<CompilerVisibleProperty Include="MemoryPackGenerator_TypeScriptImportExtension" />
</ItemGroup>
<PropertyGroup>
<MemoryPackGenerator_TypeScriptOutputDirectory>$(MSBuildProjectDirectory)\wwwroot\js\memorypack</MemoryPackGenerator_TypeScriptOutputDirectory>
<MemoryPackGenerator_TypeScriptImportExtension>.js</MemoryPackGenerator_TypeScriptImportExtension>
</PropertyGroup>

<ItemGroup>
Expand Down
82 changes: 81 additions & 1 deletion src/MemoryPack.Core/Formatters/CollectionFormatters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

Expand Down Expand Up @@ -49,6 +48,87 @@ public static partial class MemoryPackFormatterProvider

namespace MemoryPack.Formatters
{
[Preserve]
public static class ListFormatter
{
[Preserve]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SerializePackable<T, TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref List<T?>? value)
where T : IMemoryPackable<T>
#if NET7_0_OR_GREATER
where TBufferWriter : IBufferWriter<byte>
#else
where TBufferWriter : class, IBufferWriter<byte>
#endif
{
if (value == null)
{
writer.WriteNullCollectionHeader();
return;
}

#if NET7_0_OR_GREATER
writer.WritePackableSpan(CollectionsMarshal.AsSpan(value));
#else
var formatter = writer.GetFormatter<T?>();
writer.WriteCollectionHeader(value.Count);
foreach (var item in value)
{
var v = item;
formatter.Serialize(ref writer, ref v);
}
#endif
}

[Preserve]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T?>? DeserializePackable<T>(ref MemoryPackReader reader)
where T : IMemoryPackable<T>
{
List<T?>? value = default;
DeserializePackable<T>(ref reader, ref value);
return value;
}

[Preserve]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DeserializePackable<T>(ref MemoryPackReader reader, scoped ref List<T?>? value)
where T : IMemoryPackable<T>
{
if (!reader.TryReadCollectionHeader(out var length))
{
value = null;
return;
}

if (value == null)
{
value = new List<T?>(length);
}
#if NET7_0_OR_GREATER
else if (value.Count == length)
{
value.Clear();
}

var span = CollectionsMarshalEx.CreateSpan(value, length);
reader.ReadPackableSpanWithoutReadLengthHeader(length, ref span);
#else
else
{
value.Clear();
}
var formatter = reader.GetFormatter<T?>();
for (var i = 0; i < length; i++)
{
T? v = default;
formatter.Deserialize(ref reader, ref v);
value.Add(v);
}
#endif
}
}

[Preserve]
public sealed class ListFormatter<T> : MemoryPackFormatter<List<T?>>
{
Expand Down
Loading

0 comments on commit 1a18740

Please sign in to comment.