Skip to content

Commit d0d731d

Browse files
authored
Add MemoryStream benchmarks (#5140)
* Add MemoryStream benchmarks * Add Async benchmarks * Split benchmarks into two classes: chunked/regular
1 parent 95acb35 commit d0d731d

2 files changed

Lines changed: 231 additions & 0 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using BenchmarkDotNet.Attributes;
8+
using MicroBenchmarks;
9+
10+
namespace System.IO.Tests
11+
{
12+
/// <summary>
13+
/// Chunked / buffered operations. Fixed 64KB stream, chunk size controls call count.
14+
/// Read/Write variants loop over the stream in ChunkSize increments.
15+
/// CopyTo / CopyToAsync pass ChunkSize as the bufferSize parameter.
16+
/// </summary>
17+
[BenchmarkCategory(Categories.Libraries)]
18+
public class MemoryStreamChunkedTests
19+
{
20+
private const int StreamSize = 65536;
21+
22+
private MemoryStream _stream;
23+
private byte[] _buffer;
24+
25+
[Params(
26+
1, // 65536 calls, per-call overhead dominates
27+
4096)] // 16 calls, default StreamReader/BufferedStream buffer size
28+
public int ChunkSize { get; set; }
29+
30+
[GlobalSetup]
31+
public void Setup()
32+
{
33+
byte[] backing = new byte[StreamSize];
34+
new Random(42).NextBytes(backing);
35+
_buffer = new byte[ChunkSize];
36+
_stream = new MemoryStream(backing, writable: true);
37+
}
38+
39+
[GlobalCleanup]
40+
public void Cleanup()
41+
{
42+
_stream.Dispose();
43+
}
44+
45+
[Benchmark]
46+
[MemoryRandomization]
47+
public int ReadByteArray()
48+
{
49+
MemoryStream s = _stream;
50+
s.Position = 0;
51+
int count = 0;
52+
int n;
53+
while ((n = s.Read(_buffer, 0, _buffer.Length)) > 0)
54+
count += n;
55+
return count;
56+
}
57+
58+
[Benchmark]
59+
[MemoryRandomization]
60+
public int ReadSpan()
61+
{
62+
MemoryStream s = _stream;
63+
s.Position = 0;
64+
int count = 0;
65+
int n;
66+
while ((n = s.Read(_buffer.AsSpan())) > 0)
67+
count += n;
68+
return count;
69+
}
70+
71+
[Benchmark]
72+
[BenchmarkCategory(Categories.NoWASM)]
73+
[MemoryRandomization]
74+
public async Task<int> ReadAsyncMemory()
75+
{
76+
MemoryStream s = _stream;
77+
s.Position = 0;
78+
int count = 0;
79+
int n;
80+
while ((n = await s.ReadAsync(_buffer, CancellationToken.None)) > 0)
81+
count += n;
82+
return count;
83+
}
84+
85+
[Benchmark]
86+
[MemoryRandomization]
87+
public void WriteByteArray()
88+
{
89+
MemoryStream s = _stream;
90+
s.Position = 0;
91+
int remaining = StreamSize;
92+
while (remaining > 0)
93+
{
94+
int chunk = Math.Min(_buffer.Length, remaining);
95+
s.Write(_buffer, 0, chunk);
96+
remaining -= chunk;
97+
}
98+
}
99+
100+
[Benchmark]
101+
[MemoryRandomization]
102+
public void WriteSpan()
103+
{
104+
MemoryStream s = _stream;
105+
s.Position = 0;
106+
int remaining = StreamSize;
107+
while (remaining > 0)
108+
{
109+
int chunk = Math.Min(_buffer.Length, remaining);
110+
s.Write(_buffer.AsSpan(0, chunk));
111+
remaining -= chunk;
112+
}
113+
}
114+
115+
[Benchmark]
116+
[BenchmarkCategory(Categories.NoWASM)]
117+
[MemoryRandomization]
118+
public async Task WriteAsyncMemory()
119+
{
120+
MemoryStream s = _stream;
121+
s.Position = 0;
122+
int remaining = StreamSize;
123+
while (remaining > 0)
124+
{
125+
int chunk = Math.Min(_buffer.Length, remaining);
126+
await s.WriteAsync(_buffer.AsMemory(0, chunk), CancellationToken.None);
127+
remaining -= chunk;
128+
}
129+
}
130+
131+
[Benchmark]
132+
[MemoryRandomization]
133+
public void CopyToWithBufferSize()
134+
{
135+
MemoryStream s = _stream;
136+
s.Position = 0;
137+
s.CopyTo(Stream.Null, ChunkSize);
138+
}
139+
140+
[Benchmark]
141+
[BenchmarkCategory(Categories.NoWASM)]
142+
[MemoryRandomization]
143+
public async Task CopyToAsyncWithBufferSize()
144+
{
145+
MemoryStream s = _stream;
146+
s.Position = 0;
147+
await s.CopyToAsync(Stream.Null, ChunkSize);
148+
}
149+
}
150+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Threading.Tasks;
6+
using BenchmarkDotNet.Attributes;
7+
using MicroBenchmarks;
8+
9+
namespace System.IO.Tests
10+
{
11+
/// <summary>
12+
/// Operations that do not receive a user buffer. Backing size controls iteration count.
13+
/// ReadByte / WriteByte exercise per-byte overhead; CopyTo / CopyToAsync use the
14+
/// default buffer size overload.
15+
/// </summary>
16+
[BenchmarkCategory(Categories.Libraries)]
17+
public class MemoryStreamTests
18+
{
19+
private MemoryStream _stream;
20+
21+
[Params(
22+
1024, // small stream
23+
65536)] // large stream
24+
public int Size { get; set; }
25+
26+
[GlobalSetup]
27+
public void Setup()
28+
{
29+
byte[] backing = new byte[Size];
30+
new Random(42).NextBytes(backing);
31+
_stream = new MemoryStream(backing, writable: true);
32+
}
33+
34+
[GlobalCleanup]
35+
public void Cleanup()
36+
{
37+
_stream.Dispose();
38+
}
39+
40+
[Benchmark]
41+
[MemoryRandomization]
42+
public int ReadByte()
43+
{
44+
MemoryStream s = _stream;
45+
s.Position = 0;
46+
int count = 0;
47+
while (s.ReadByte() != -1)
48+
count++;
49+
return count;
50+
}
51+
52+
[Benchmark]
53+
[MemoryRandomization]
54+
public void WriteByte()
55+
{
56+
MemoryStream s = _stream;
57+
s.Position = 0;
58+
for (int i = 0; i < Size; i++)
59+
s.WriteByte((byte)i);
60+
}
61+
62+
[Benchmark]
63+
[MemoryRandomization]
64+
public void CopyTo()
65+
{
66+
MemoryStream s = _stream;
67+
s.Position = 0;
68+
s.CopyTo(Stream.Null);
69+
}
70+
71+
[Benchmark]
72+
[BenchmarkCategory(Categories.NoWASM)]
73+
[MemoryRandomization]
74+
public async Task CopyToAsync()
75+
{
76+
MemoryStream s = _stream;
77+
s.Position = 0;
78+
await s.CopyToAsync(Stream.Null);
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)