diff --git a/src/benchmarks/micro/libraries/System.IO/MemoryStreamChunkedTests.cs b/src/benchmarks/micro/libraries/System.IO/MemoryStreamChunkedTests.cs new file mode 100644 index 00000000000..46ec5b66949 --- /dev/null +++ b/src/benchmarks/micro/libraries/System.IO/MemoryStreamChunkedTests.cs @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using MicroBenchmarks; + +namespace System.IO.Tests +{ + /// + /// Chunked / buffered operations. Fixed 64KB stream, chunk size controls call count. + /// Read/Write variants loop over the stream in ChunkSize increments. + /// CopyTo / CopyToAsync pass ChunkSize as the bufferSize parameter. + /// + [BenchmarkCategory(Categories.Libraries)] + public class MemoryStreamChunkedTests + { + private const int StreamSize = 65536; + + private MemoryStream _stream; + private byte[] _buffer; + + [Params( + 1, // 65536 calls, per-call overhead dominates + 4096)] // 16 calls, default StreamReader/BufferedStream buffer size + public int ChunkSize { get; set; } + + [GlobalSetup] + public void Setup() + { + byte[] backing = new byte[StreamSize]; + new Random(42).NextBytes(backing); + _buffer = new byte[ChunkSize]; + _stream = new MemoryStream(backing, writable: true); + } + + [GlobalCleanup] + public void Cleanup() + { + _stream.Dispose(); + } + + [Benchmark] + [MemoryRandomization] + public int ReadByteArray() + { + MemoryStream s = _stream; + s.Position = 0; + int count = 0; + int n; + while ((n = s.Read(_buffer, 0, _buffer.Length)) > 0) + count += n; + return count; + } + + [Benchmark] + [MemoryRandomization] + public int ReadSpan() + { + MemoryStream s = _stream; + s.Position = 0; + int count = 0; + int n; + while ((n = s.Read(_buffer.AsSpan())) > 0) + count += n; + return count; + } + + [Benchmark] + [BenchmarkCategory(Categories.NoWASM)] + [MemoryRandomization] + public async Task ReadAsyncMemory() + { + MemoryStream s = _stream; + s.Position = 0; + int count = 0; + int n; + while ((n = await s.ReadAsync(_buffer, CancellationToken.None)) > 0) + count += n; + return count; + } + + [Benchmark] + [MemoryRandomization] + public void WriteByteArray() + { + MemoryStream s = _stream; + s.Position = 0; + int remaining = StreamSize; + while (remaining > 0) + { + int chunk = Math.Min(_buffer.Length, remaining); + s.Write(_buffer, 0, chunk); + remaining -= chunk; + } + } + + [Benchmark] + [MemoryRandomization] + public void WriteSpan() + { + MemoryStream s = _stream; + s.Position = 0; + int remaining = StreamSize; + while (remaining > 0) + { + int chunk = Math.Min(_buffer.Length, remaining); + s.Write(_buffer.AsSpan(0, chunk)); + remaining -= chunk; + } + } + + [Benchmark] + [BenchmarkCategory(Categories.NoWASM)] + [MemoryRandomization] + public async Task WriteAsyncMemory() + { + MemoryStream s = _stream; + s.Position = 0; + int remaining = StreamSize; + while (remaining > 0) + { + int chunk = Math.Min(_buffer.Length, remaining); + await s.WriteAsync(_buffer.AsMemory(0, chunk), CancellationToken.None); + remaining -= chunk; + } + } + + [Benchmark] + [MemoryRandomization] + public void CopyToWithBufferSize() + { + MemoryStream s = _stream; + s.Position = 0; + s.CopyTo(Stream.Null, ChunkSize); + } + + [Benchmark] + [BenchmarkCategory(Categories.NoWASM)] + [MemoryRandomization] + public async Task CopyToAsyncWithBufferSize() + { + MemoryStream s = _stream; + s.Position = 0; + await s.CopyToAsync(Stream.Null, ChunkSize); + } + } +} diff --git a/src/benchmarks/micro/libraries/System.IO/MemoryStreamTests.cs b/src/benchmarks/micro/libraries/System.IO/MemoryStreamTests.cs new file mode 100644 index 00000000000..24db4f653ee --- /dev/null +++ b/src/benchmarks/micro/libraries/System.IO/MemoryStreamTests.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using MicroBenchmarks; + +namespace System.IO.Tests +{ + /// + /// Operations that do not receive a user buffer. Backing size controls iteration count. + /// ReadByte / WriteByte exercise per-byte overhead; CopyTo / CopyToAsync use the + /// default buffer size overload. + /// + [BenchmarkCategory(Categories.Libraries)] + public class MemoryStreamTests + { + private MemoryStream _stream; + + [Params( + 1024, // small stream + 65536)] // large stream + public int Size { get; set; } + + [GlobalSetup] + public void Setup() + { + byte[] backing = new byte[Size]; + new Random(42).NextBytes(backing); + _stream = new MemoryStream(backing, writable: true); + } + + [GlobalCleanup] + public void Cleanup() + { + _stream.Dispose(); + } + + [Benchmark] + [MemoryRandomization] + public int ReadByte() + { + MemoryStream s = _stream; + s.Position = 0; + int count = 0; + while (s.ReadByte() != -1) + count++; + return count; + } + + [Benchmark] + [MemoryRandomization] + public void WriteByte() + { + MemoryStream s = _stream; + s.Position = 0; + for (int i = 0; i < Size; i++) + s.WriteByte((byte)i); + } + + [Benchmark] + [MemoryRandomization] + public void CopyTo() + { + MemoryStream s = _stream; + s.Position = 0; + s.CopyTo(Stream.Null); + } + + [Benchmark] + [BenchmarkCategory(Categories.NoWASM)] + [MemoryRandomization] + public async Task CopyToAsync() + { + MemoryStream s = _stream; + s.Position = 0; + await s.CopyToAsync(Stream.Null); + } + } +}