Skip to content

Commit d55af48

Browse files
committed
ArrayPool(Segmented)BufferWriter
1 parent 431c96f commit d55af48

8 files changed

Lines changed: 1024 additions & 2 deletions

File tree

ArrayPoolCollection.Benchmark/ArrayPoolCollection.Benchmark.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
<ItemGroup>
1111
<PackageReference Include="BenchmarkDotNet" Version="0.14.1-nightly.20250107.205" />
12+
<PackageReference Include="MemoryPack" Version="1.21.4" />
1213
</ItemGroup>
1314

1415
<ItemGroup>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System.Buffers;
2+
using System.IO.Pipelines;
3+
using ArrayPoolCollection.Buffers;
4+
using BenchmarkDotNet.Attributes;
5+
using MemoryPack;
6+
7+
namespace ArrayPoolCollection.Benchmark.Experiment;
8+
9+
[MemoryDiagnoser]
10+
public class BufferWriter
11+
{
12+
private readonly TestClass Source = new TestClass(123, "Alice", "GDD", Enumerable.Range(0, 1000000).Select(i => i.ToString()).ToArray());
13+
14+
[Benchmark]
15+
public TestClass ByteArray()
16+
{
17+
var bytes = MemoryPackSerializer.Serialize(Source);
18+
return MemoryPackSerializer.Deserialize<TestClass>(bytes)!;
19+
}
20+
21+
[Benchmark]
22+
public async ValueTask<TestClass> Pipe()
23+
{
24+
var pipe = new Pipe();
25+
MemoryPackSerializer.Serialize(pipe.Writer, Source);
26+
await pipe.Writer.CompleteAsync();
27+
var readResult = await pipe.Reader.ReadAsync();
28+
return MemoryPackSerializer.Deserialize<TestClass>(readResult.Buffer)!;
29+
}
30+
31+
[Benchmark]
32+
public TestClass ArrayBufferWriter()
33+
{
34+
var writer = new ArrayBufferWriter<byte>();
35+
MemoryPackSerializer.Serialize(writer, Source);
36+
return MemoryPackSerializer.Deserialize<TestClass>(writer.WrittenSpan)!;
37+
}
38+
39+
[Benchmark]
40+
public TestClass ArrayPoolBufferWriter()
41+
{
42+
using var writer = new ArrayPoolBufferWriter<byte>(1024);
43+
MemoryPackSerializer.Serialize(writer, Source);
44+
return MemoryPackSerializer.Deserialize<TestClass>(writer.WrittenSpan)!;
45+
}
46+
47+
[Benchmark]
48+
public TestClass ArrayPoolSegmentedBufferWriter()
49+
{
50+
using var writer = new ArrayPoolSegmentedBufferWriter<byte>(1024);
51+
MemoryPackSerializer.Serialize(writer, Source);
52+
return MemoryPackSerializer.Deserialize<TestClass>(writer.GetWrittenSequence())!;
53+
}
54+
55+
[Benchmark]
56+
public TestClass With()
57+
{
58+
return Source with { Name = new string(Source.Name), Address = new string(Source.Address), Status = Source.Status.Select(i => new string(i)).ToArray() };
59+
}
60+
}
61+
62+
[MemoryPackable]
63+
public partial record class TestClass(int Id, string Name, string Address, string[] Status)
64+
{
65+
}

ArrayPoolCollection.Benchmark/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// See https://aka.ms/new-console-template for more information
22
using ArrayPoolCollection.Benchmark;
3+
using ArrayPoolCollection.Benchmark.Experiment;
34
using BenchmarkDotNet.Running;
45

56
BenchmarkRunner.Run<ArrayPoolWrapperBenchmark>(args: Environment.GetCommandLineArgs());
@@ -10,3 +11,4 @@
1011
BenchmarkRunner.Run<ArrayPoolQueueBenchmark>(args: Environment.GetCommandLineArgs());
1112
BenchmarkRunner.Run<ArrayPoolPriorityQueueBenchmark>(args: Environment.GetCommandLineArgs());
1213
BenchmarkRunner.Run<ArrayPoolBitsBenchmark>(args: Environment.GetCommandLineArgs());
14+
BenchmarkRunner.Run<BufferWriter>(args: Environment.GetCommandLineArgs());
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
namespace ArrayPoolCollection.Buffers.Tests;
2+
3+
public class ArrayPoolBufferWriterTests
4+
{
5+
[Fact]
6+
public void Capacity()
7+
{
8+
var writer = new ArrayPoolBufferWriter<byte>(64);
9+
10+
Assert.Equal(64, writer.Capacity);
11+
writer.Advance(16);
12+
Assert.Equal(64, writer.Capacity);
13+
14+
15+
writer.Dispose();
16+
Assert.Throws<ObjectDisposedException>(() => writer.Capacity);
17+
}
18+
19+
[Fact]
20+
public void FreeCapacity()
21+
{
22+
var writer = new ArrayPoolBufferWriter<byte>(64);
23+
24+
Assert.Equal(64, writer.FreeCapacity);
25+
writer.Advance(16);
26+
Assert.Equal(48, writer.FreeCapacity);
27+
28+
29+
writer.Dispose();
30+
Assert.Throws<ObjectDisposedException>(() => writer.FreeCapacity);
31+
}
32+
33+
[Fact]
34+
public void WrittenCount()
35+
{
36+
var writer = new ArrayPoolBufferWriter<byte>(64);
37+
38+
Assert.Equal(0, writer.WrittenCount);
39+
writer.Advance(16);
40+
Assert.Equal(16, writer.WrittenCount);
41+
42+
43+
writer.Dispose();
44+
Assert.Throws<ObjectDisposedException>(() => writer.WrittenCount);
45+
}
46+
47+
[Fact]
48+
public void WrittenMemory()
49+
{
50+
var writer = new ArrayPoolBufferWriter<byte>(64);
51+
52+
Assert.Equal(0, writer.WrittenMemory.Length);
53+
54+
var memory = writer.GetMemory(16);
55+
for (int i = 0; i < memory.Length; i++)
56+
{
57+
memory.Span[i] = (byte)i;
58+
}
59+
writer.Advance(16);
60+
61+
var written = writer.WrittenMemory;
62+
Assert.Equal(16, written.Length);
63+
for (int i = 0; i < written.Length; i++)
64+
{
65+
Assert.Equal((byte)i, written.Span[i]);
66+
}
67+
68+
69+
memory = writer.GetMemory(64);
70+
for (int i = 0; i < memory.Length; i++)
71+
{
72+
memory.Span[i] = (byte)(i + 16);
73+
}
74+
writer.Advance(64);
75+
76+
written = writer.WrittenMemory;
77+
Assert.Equal(80, written.Length);
78+
for (int i = 0; i < written.Length; i++)
79+
{
80+
Assert.Equal((byte)i, written.Span[i]);
81+
}
82+
83+
84+
writer.Dispose();
85+
Assert.Throws<ObjectDisposedException>(() => writer.WrittenMemory);
86+
}
87+
88+
[Fact]
89+
public void WrittenSpan()
90+
{
91+
var writer = new ArrayPoolBufferWriter<byte>(64);
92+
93+
Assert.Equal(0, writer.WrittenSpan.Length);
94+
95+
var span = writer.GetSpan(16);
96+
for (int i = 0; i < span.Length; i++)
97+
{
98+
span[i] = (byte)i;
99+
}
100+
writer.Advance(16);
101+
102+
var written = writer.WrittenSpan;
103+
Assert.Equal(16, written.Length);
104+
for (int i = 0; i < written.Length; i++)
105+
{
106+
Assert.Equal((byte)i, written[i]);
107+
}
108+
109+
110+
span = writer.GetSpan(64);
111+
for (int i = 0; i < span.Length; i++)
112+
{
113+
span[i] = (byte)(i + 16);
114+
}
115+
writer.Advance(64);
116+
117+
written = writer.WrittenSpan;
118+
Assert.Equal(80, written.Length);
119+
for (int i = 0; i < written.Length; i++)
120+
{
121+
Assert.Equal((byte)i, written[i]);
122+
}
123+
124+
125+
writer.Dispose();
126+
Assert.Throws<ObjectDisposedException>(() => writer.WrittenSpan.Length);
127+
}
128+
129+
[Fact]
130+
public void Ctor()
131+
{
132+
using var defaultCtor = new ArrayPoolBufferWriter<byte>();
133+
134+
using var zero = new ArrayPoolBufferWriter<byte>(0);
135+
136+
Assert.Throws<ArgumentOutOfRangeException>(() => new ArrayPoolBufferWriter<byte>(-1));
137+
}
138+
139+
[Fact]
140+
public void Advance()
141+
{
142+
var writer = new ArrayPoolBufferWriter<byte>(64);
143+
144+
Assert.Throws<ArgumentOutOfRangeException>(() => writer.Advance(-1));
145+
writer.Advance(0);
146+
writer.Advance(64);
147+
Assert.Throws<ArgumentOutOfRangeException>(() => writer.Advance(1));
148+
149+
150+
writer.Dispose();
151+
Assert.Throws<ObjectDisposedException>(() => writer.Advance(1));
152+
}
153+
154+
[Fact]
155+
public void Clear()
156+
{
157+
var writer = new ArrayPoolBufferWriter<byte>(64);
158+
159+
writer.Clear();
160+
Assert.Equal(0, writer.WrittenCount);
161+
162+
var span = writer.GetSpan(16);
163+
for (int i = 0; i < span.Length; i++)
164+
{
165+
span[i] = (byte)i;
166+
}
167+
writer.Advance(16);
168+
169+
writer.Clear();
170+
Assert.Equal(0, writer.WrittenCount);
171+
writer.Advance(16);
172+
var written = writer.WrittenSpan;
173+
for (int i = 0; i < written.Length; i++)
174+
{
175+
Assert.Equal(0, written[i]);
176+
}
177+
178+
179+
writer.Dispose();
180+
Assert.Throws<ObjectDisposedException>(() => writer.Clear());
181+
}
182+
183+
[Fact]
184+
public void Dispose()
185+
{
186+
var writer = new ArrayPoolBufferWriter<byte>();
187+
188+
189+
writer.Dispose();
190+
writer.Dispose();
191+
}
192+
193+
[Fact]
194+
public void GetMemory()
195+
{
196+
var writer = new ArrayPoolBufferWriter<byte>(64);
197+
198+
Assert.True(writer.GetMemory(16).Length >= 16);
199+
Assert.True(writer.GetMemory(128).Length >= 128);
200+
201+
Assert.Throws<ArgumentOutOfRangeException>(() => writer.GetMemory(-1));
202+
203+
204+
writer.Dispose();
205+
Assert.Throws<ObjectDisposedException>(() => writer.GetMemory(1));
206+
}
207+
208+
[Fact]
209+
public void GetSpan()
210+
{
211+
var writer = new ArrayPoolBufferWriter<byte>(64);
212+
213+
var span = writer.GetSpan(16);
214+
Assert.True(span.Length >= 16);
215+
216+
for (int i = 0; i < span.Length; i++)
217+
{
218+
span[i] = (byte)i;
219+
}
220+
writer.Advance(16);
221+
222+
span = writer.GetSpan(128);
223+
Assert.True(span.Length >= 128);
224+
for (int i = 0; i < span.Length; i++)
225+
{
226+
span[i] = (byte)i;
227+
}
228+
229+
230+
Assert.Throws<ArgumentOutOfRangeException>(() => writer.GetSpan(-1));
231+
232+
233+
writer.Dispose();
234+
Assert.Throws<ObjectDisposedException>(() => writer.GetSpan(1));
235+
}
236+
237+
[Fact]
238+
public void ResetWrittenCount()
239+
{
240+
var writer = new ArrayPoolBufferWriter<byte>(64);
241+
242+
writer.ResetWrittenCount();
243+
Assert.Equal(0, writer.WrittenCount);
244+
245+
var span = writer.GetSpan(16);
246+
for (int i = 0; i < span.Length; i++)
247+
{
248+
span[i] = (byte)i;
249+
}
250+
writer.Advance(16);
251+
252+
writer.ResetWrittenCount();
253+
Assert.Equal(0, writer.WrittenCount);
254+
writer.Advance(16);
255+
var written = writer.WrittenSpan;
256+
for (int i = 0; i < written.Length; i++)
257+
{
258+
Assert.Equal((byte)i, written[i]);
259+
}
260+
261+
262+
writer.Dispose();
263+
Assert.Throws<ObjectDisposedException>(() => writer.ResetWrittenCount());
264+
}
265+
}

0 commit comments

Comments
 (0)