Skip to content

Commit e40fc22

Browse files
authored
Merge pull request #49 from Cysharp/improve-dictionary
Improve Dictionary serialization
2 parents 9746985 + 86fa2ce commit e40fc22

File tree

14 files changed

+485
-306
lines changed

14 files changed

+485
-306
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
using Benchmark.BenchmarkNetUtilities;
2+
using BenchmarkDotNet.Configs;
3+
using MemoryPack;
4+
using MemoryPack.Formatters;
5+
using MemoryPack.Internal;
6+
using Microsoft.Diagnostics.Tracing;
7+
using Orleans.Serialization.Buffers;
8+
using System;
9+
using System.Buffers;
10+
using System.Collections.Generic;
11+
using System.Formats.Asn1;
12+
using System.Linq;
13+
using System.Runtime.CompilerServices;
14+
using System.Text;
15+
using System.Text.Json;
16+
using System.Threading.Tasks;
17+
18+
namespace Benchmark.Benchmarks;
19+
20+
[CategoriesColumn]
21+
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
22+
public class StaticDictionaryFormatterCheck
23+
{
24+
Dictionary<string, int> target;
25+
IMemoryPackFormatter<Dictionary<string, int>> current;
26+
IMemoryPackFormatter<Dictionary<string, int>> improvement;
27+
28+
Dictionary<string, int> dict;
29+
ArrayBufferWriter<byte> buffer;
30+
MemoryPackWriterOptionalState state;
31+
MemoryPackReaderOptionalState state2;
32+
byte[] bytes;
33+
34+
public StaticDictionaryFormatterCheck()
35+
{
36+
target = Enumerable.Range(1, 100)
37+
.ToDictionary(x => x.ToString(), x => x);
38+
39+
current = new DictionaryFormatter<string, int>();
40+
improvement = new DictionaryFormatter2<string, int>();
41+
42+
dict = new Dictionary<string, int>(100);
43+
44+
bytes = MemoryPackSerializer.Serialize(target);
45+
46+
buffer = new ArrayBufferWriter<byte>(bytes.Length);
47+
48+
state = MemoryPackWriterOptionalStatePool.Rent(null);
49+
state2 = MemoryPackReaderOptionalStatePool.Rent(null);
50+
}
51+
52+
//[Benchmark, BenchmarkCategory(Categories.Serialize)]
53+
//public void SerializeCurrent()
54+
//{
55+
// var writer = new MemoryPackWriter<ArrayBufferWriter<byte>>(ref buffer, state);
56+
// current.Serialize(ref writer, ref target!);
57+
// writer.Flush();
58+
// buffer.Clear();
59+
//}
60+
61+
//[Benchmark, BenchmarkCategory(Categories.Serialize)]
62+
//public void SerializeImprovement()
63+
//{
64+
// var writer = new MemoryPackWriter<ArrayBufferWriter<byte>>(ref buffer, state);
65+
// improvement.Serialize(ref writer, ref target!);
66+
// writer.Flush();
67+
// buffer.Clear();
68+
//}
69+
70+
[Benchmark, BenchmarkCategory(Categories.Deserialize)]
71+
public void DeserializeCurrent()
72+
{
73+
var reader = new MemoryPackReader(bytes, state2);
74+
current.Deserialize(ref reader, ref dict!);
75+
reader.Dispose();
76+
}
77+
78+
[Benchmark, BenchmarkCategory(Categories.Deserialize)]
79+
public void DeserializeImprovement()
80+
{
81+
var reader = new MemoryPackReader(bytes, state2);
82+
83+
improvement.Deserialize(ref reader, ref dict!);
84+
reader.Dispose();
85+
}
86+
}
87+
88+
89+
[Preserve]
90+
sealed class DictionaryFormatter<TKey, TValue> : MemoryPackFormatter<Dictionary<TKey, TValue?>>
91+
where TKey : notnull
92+
{
93+
static DictionaryFormatter()
94+
{
95+
if (!MemoryPackFormatterProvider.IsRegistered<KeyValuePair<TKey, TValue?>>())
96+
{
97+
MemoryPackFormatterProvider.Register(new KeyValuePairFormatter<TKey, TValue?>());
98+
}
99+
}
100+
101+
readonly IEqualityComparer<TKey>? equalityComparer;
102+
103+
public DictionaryFormatter()
104+
: this(null)
105+
{
106+
107+
}
108+
109+
public DictionaryFormatter(IEqualityComparer<TKey>? equalityComparer)
110+
{
111+
this.equalityComparer = equalityComparer;
112+
}
113+
114+
[Preserve]
115+
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref Dictionary<TKey, TValue?>? value)
116+
{
117+
if (value == null)
118+
{
119+
writer.WriteNullCollectionHeader();
120+
return;
121+
}
122+
123+
var formatter = writer.GetFormatter<KeyValuePair<TKey, TValue?>>();
124+
125+
writer.WriteCollectionHeader(value.Count);
126+
foreach (var item in value)
127+
{
128+
var v = item;
129+
formatter.Serialize(ref writer, ref v);
130+
}
131+
}
132+
133+
[Preserve]
134+
public override void Deserialize(ref MemoryPackReader reader, scoped ref Dictionary<TKey, TValue?>? value)
135+
{
136+
if (!reader.TryReadCollectionHeader(out var length))
137+
{
138+
value = null;
139+
return;
140+
}
141+
142+
if (value == null)
143+
{
144+
value = new Dictionary<TKey, TValue?>(length, equalityComparer);
145+
}
146+
else
147+
{
148+
value.Clear();
149+
}
150+
151+
var formatter = reader.GetFormatter<KeyValuePair<TKey, TValue?>>();
152+
for (int i = 0; i < length; i++)
153+
{
154+
KeyValuePair<TKey, TValue?> v = default;
155+
formatter.Deserialize(ref reader, ref v);
156+
value.Add(v.Key, v.Value);
157+
}
158+
}
159+
}
160+
161+
[Preserve]
162+
sealed class DictionaryFormatter2<TKey, TValue> : MemoryPackFormatter<Dictionary<TKey, TValue?>>
163+
where TKey : notnull
164+
{
165+
readonly IEqualityComparer<TKey>? equalityComparer;
166+
167+
public DictionaryFormatter2()
168+
: this(null)
169+
{
170+
171+
}
172+
173+
public DictionaryFormatter2(IEqualityComparer<TKey>? equalityComparer)
174+
{
175+
this.equalityComparer = equalityComparer;
176+
}
177+
178+
[Preserve]
179+
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref Dictionary<TKey, TValue?>? value)
180+
{
181+
if (value == null)
182+
{
183+
writer.WriteNullCollectionHeader();
184+
return;
185+
}
186+
187+
var keyFormatter = writer.GetFormatter<TKey>();
188+
var valueFormatter = writer.GetFormatter<TValue>();
189+
190+
writer.WriteCollectionHeader(value.Count);
191+
foreach (var item in value)
192+
{
193+
KeyValuePairFormatter.Serialize(keyFormatter, valueFormatter, ref writer, item);
194+
}
195+
}
196+
197+
[Preserve]
198+
public override void Deserialize(ref MemoryPackReader reader, scoped ref Dictionary<TKey, TValue?>? value)
199+
{
200+
if (!reader.TryReadCollectionHeader(out var length))
201+
{
202+
value = null;
203+
return;
204+
}
205+
206+
if (value == null)
207+
{
208+
value = new Dictionary<TKey, TValue?>(length, equalityComparer);
209+
}
210+
else
211+
{
212+
value.Clear();
213+
}
214+
215+
var keyFormatter = reader.GetFormatter<TKey>();
216+
var valueFormatter = reader.GetFormatter<TValue>();
217+
for (int i = 0; i < length; i++)
218+
{
219+
KeyValuePairFormatter.Deserialize(keyFormatter, valueFormatter, ref reader, out var k, out var v);
220+
value.Add(k!, v);
221+
}
222+
}
223+
}

sandbox/Benchmark/Program.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,16 @@
4747

4848

4949

50+
5051
//BenchmarkRunner.Run<Utf16VsUtf8>(config, args);
5152

5253
//BenchmarkRunner.Run<SerializeTest<NeuralNetworkLayerModel>>(config, args);
5354

5455
// BenchmarkRunner.Run<DeserializeTest<NeuralNetworkLayerModel>>(config, args);
5556

5657

57-
BenchmarkRunner.Run<SerializeTest<JsonResponseModel>>(config, args);
58+
BenchmarkRunner.Run<StaticDictionaryFormatterCheck>(config, args);
59+
//BenchmarkRunner.Run<SerializeTest<JsonResponseModel>>(config, args);
5860
//BenchmarkRunner.Run<DeserializeTest<JsonResponseModel>>(config, args);
5961
//BenchmarkRunner.Run<SerializeTest<Vector3[]>>(config, args);
6062
//BenchmarkRunner.Run<DeserializeTest<Vector3[]>>(config, args);
@@ -79,6 +81,14 @@
7981

8082
#if DEBUG
8183

84+
var c = new StaticDictionaryFormatterCheck();
85+
c.DeserializeCurrent();
86+
c.DeserializeImprovement();
87+
88+
c.DeserializeCurrent();
89+
c.DeserializeImprovement();
90+
91+
8292
var model = new JsonResponseModel(true);
8393
var model2 = Enumerable.Repeat(new Vector3 { X = 10.3f, Y = 40.5f, Z = 13411.3f }, 1000).ToArray();
8494

0 commit comments

Comments
 (0)