From ef923f6abe15940fcea7ac7373395491f0d68a65 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Oct 2025 17:34:16 +0000 Subject: [PATCH 1/7] Initial plan From b11ae722ae1737a0b4bd3b52173d98ebfafa545e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Oct 2025 18:10:31 +0000 Subject: [PATCH 2/7] Disable FileStream buffering when used with StreamReader/StreamWriter Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/libraries/System.Private.CoreLib/src/System/IO/File.cs | 4 ++-- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 2 +- .../System.Private.CoreLib/src/System/IO/StreamWriter.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index e7c4e948d6c3ce..24f592ba34f585 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -1077,7 +1077,7 @@ public static void Decrypt(string path) // we will have asynchronous file access faked by the thread pool. We want the real thing. private static StreamReader AsyncStreamReader(string path, Encoding encoding) => new StreamReader( - new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan), + new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, FileOptions.Asynchronous | FileOptions.SequentialScan), encoding, detectEncodingFromByteOrderMarks: true); public static Task ReadAllTextAsync(string path, CancellationToken cancellationToken = default) @@ -1339,7 +1339,7 @@ private static Task WriteAllLinesAsync(string path, IEnumerable contents try { writer = new StreamWriter( - new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous), + new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1, FileOptions.Asynchronous), encoding); } catch (Exception e) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index e06a2512264c8d..ab81d84bf3f87c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -232,7 +232,7 @@ private static FileStream ValidateArgsAndOpenPath(string path, int bufferSize) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bufferSize); } - return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, FileStream.DefaultBufferSize); + return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1); } public override void Close() diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index 4f370df7937b0f..67280cb381f82b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -189,7 +189,7 @@ private static FileStream ValidateArgsAndOpenPath(string path, bool append, int ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bufferSize); } - return new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, FileStream.DefaultBufferSize); + return new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1); } public override void Close() From d02f88715bb9fa1aa2cfb5256c0702bc0c0d4e96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Oct 2025 21:01:20 +0000 Subject: [PATCH 3/7] Apply FileStream buffering optimization to additional libraries Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../src/Microsoft/CSharp/CSharpCodeGenerator.cs | 4 ++-- .../src/System/CodeDom/Compiler/CodeCompiler.cs | 6 +++--- .../System.CodeDom/src/System/CodeDom/Compiler/Executor.cs | 2 +- .../src/System/ComponentModel/LicFileLicenseProvider.cs | 2 +- .../System.Management/src/System/Management/WMIGenerator.cs | 2 +- .../Net/NetworkInformation/StringParsingHelpers.Misc.cs | 2 +- .../System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs b/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs index c95e52baf5600a..990a10f7abe28f 100644 --- a/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs +++ b/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs @@ -3020,7 +3020,7 @@ private CompilerResults FromDomBatch(CompilerParameters options, CodeCompileUnit ResolveReferencedAssemblies(options, ea[i]); filenames[i] = options.TempFiles.AddExtension(i + FileExtension); - using (var fs = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var fs = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) { ((ICodeGenerator)this).GenerateCodeFromCompileUnit(ea[i], sw, _options); @@ -3055,7 +3055,7 @@ private static CompilerResults FromSourceBatch(CompilerParameters options, strin for (int i = 0; i < sources.Length; i++) { string name = options.TempFiles.AddExtension(i + FileExtension); - using (var fs = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var fs = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) using (var sw = new StreamWriter(fs, Encoding.UTF8)) { sw.Write(sources[i]); diff --git a/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/CodeCompiler.cs b/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/CodeCompiler.cs index 65300e8602eac8..da37bc5489de77 100644 --- a/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/CodeCompiler.cs +++ b/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/CodeCompiler.cs @@ -144,7 +144,7 @@ protected virtual CompilerResults FromDomBatch(CompilerParameters options, CodeC ResolveReferencedAssemblies(options, ea[i]); filenames[i] = options.TempFiles.AddExtension(i + FileExtension); - using (var fs = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var fs = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) using (var sw = new StreamWriter(fs, Encoding.UTF8)) { ((ICodeGenerator)this).GenerateCodeFromCompileUnit(ea[i], sw, Options); @@ -185,7 +185,7 @@ protected virtual string GetResponseFileCmdArgs(CompilerParameters options, stri { string responseFileName = options.TempFiles.AddExtension("cmdline"); - using (var fs = new FileStream(responseFileName, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var fs = new FileStream(responseFileName, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) using (var sw = new StreamWriter(fs, Encoding.UTF8)) { sw.Write(cmdArgs); @@ -205,7 +205,7 @@ protected virtual CompilerResults FromSourceBatch(CompilerParameters options, st for (int i = 0; i < sources.Length; i++) { string name = options.TempFiles.AddExtension(i + FileExtension); - using (var fs = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var fs = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) using (var sw = new StreamWriter(fs, Encoding.UTF8)) { sw.Write(sources[i]); diff --git a/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/Executor.cs b/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/Executor.cs index 4b2513a6d27520..c664e0e72d40f4 100644 --- a/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/Executor.cs +++ b/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/Executor.cs @@ -13,7 +13,7 @@ public static class Executor private const int ProcessTimeOut = 600000; private static FileStream CreateInheritedFile(string file) => - new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.Read | FileShare.Inheritable); + new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.Read | FileShare.Inheritable, bufferSize: 1); public static void ExecWait(string cmd, TempFileCollection tempFiles) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs index 573e97e6c7e320..f84b7ad82dc1fe 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs @@ -84,7 +84,7 @@ protected virtual string GetKey(Type type) Debug.WriteLine($"Looking for license in: {licenseFile}"); if (File.Exists(licenseFile)) { - Stream licStream = new FileStream(licenseFile, FileMode.Open, FileAccess.Read, FileShare.Read); + Stream licStream = new FileStream(licenseFile, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1); StreamReader sr = new StreamReader(licStream); string? s = sr.ReadLine(); sr.Close(); diff --git a/src/libraries/System.Management/src/System/Management/WMIGenerator.cs b/src/libraries/System.Management/src/System/Management/WMIGenerator.cs index 93f1186f680b32..d8749e509cfdef 100644 --- a/src/libraries/System.Management/src/System/Management/WMIGenerator.cs +++ b/src/libraries/System.Management/src/System/Management/WMIGenerator.cs @@ -182,7 +182,7 @@ public bool GenerateCode(CodeLanguage lang, string filePath, string netNamespace InitializeCodeGeneration(); //Now create the filestream (output file) - tw = new StreamWriter(new FileStream(filePath, FileMode.Create), System.Text.Encoding.UTF8); + tw = new StreamWriter(new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1), System.Text.Encoding.UTF8); return GenerateAndWriteCode(lang); diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs index 1d6c3f47e0838f..af3b2a384f3da2 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs @@ -41,7 +41,7 @@ internal static StreamReader OpenStreamReader(string filePath) { try { - return new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)); + return new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, useAsync: false)); } catch (Exception e) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs index d45397bb634f11..cad44c2ab2857c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs @@ -291,7 +291,7 @@ public XmlTextWriter(Stream w, Encoding? encoding) : this() // Creates an instance of the XmlTextWriter class using the specified file. public XmlTextWriter(string filename, Encoding? encoding) : this(new FileStream(filename, FileMode.Create, - FileAccess.Write, FileShare.Read), encoding) + FileAccess.Write, FileShare.Read, bufferSize: 1), encoding) { } From 2d54fffc2d87ab924667b9ca5a0454a41706322e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Oct 2025 21:26:28 +0000 Subject: [PATCH 4/7] Add BenchmarkDotNet performance test for FileStream buffering optimization Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../FileStreamBufferingBenchmark.cs | 173 ++++++++++++++++++ .../FileStreamBufferingBenchmark.csproj | 17 ++ .../FileStreamBufferingBenchmark/README.md | 70 +++++++ .../run-benchmark.cmd | 42 +++++ .../run-benchmark.sh | 35 ++++ 5 files changed, 337 insertions(+) create mode 100644 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.cs create mode 100644 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.csproj create mode 100644 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/README.md create mode 100644 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.cmd create mode 100755 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.sh diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.cs b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.cs new file mode 100644 index 00000000000000..25543035020d04 --- /dev/null +++ b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.cs @@ -0,0 +1,173 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +namespace System.IO.Tests.Performance +{ + /// + /// Benchmarks to validate FileStream buffering optimizations when used with StreamReader/StreamWriter. + /// These tests demonstrate the performance improvements from disabling FileStream internal buffering + /// when it's wrapped by StreamReader/StreamWriter (which provide their own buffering). + /// + [MemoryDiagnoser] + public class FileStreamBufferingBenchmark + { + private string _tempDir = null!; + private string _testFile = null!; + private string[] _testLines = null!; + private string _testContent = null!; + private const int LineCount = 1000; + private const int ContentSize = 50000; // ~50KB + + [GlobalSetup] + public void Setup() + { + _tempDir = Path.Combine(Path.GetTempPath(), "FileStreamBenchmark_" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempDir); + _testFile = Path.Combine(_tempDir, "test.txt"); + + // Generate test data + _testLines = new string[LineCount]; + StringBuilder contentBuilder = new StringBuilder(); + for (int i = 0; i < LineCount; i++) + { + string line = $"This is test line {i} with some content to make it more realistic"; + _testLines[i] = line; + contentBuilder.AppendLine(line); + } + _testContent = contentBuilder.ToString(); + + // Create initial file for read tests + File.WriteAllText(_testFile, _testContent); + } + + [GlobalCleanup] + public void Cleanup() + { + try + { + if (Directory.Exists(_tempDir)) + { + Directory.Delete(_tempDir, true); + } + } + catch { } + } + + // ==================== File.WriteAllLinesAsync ==================== + + [Benchmark] + public async Task File_WriteAllLinesAsync() + { + string path = Path.Combine(_tempDir, "write_lines.txt"); + await File.WriteAllLinesAsync(path, _testLines); + File.Delete(path); + } + + // ==================== File.AppendAllLinesAsync ==================== + + [Benchmark] + public async Task File_AppendAllLinesAsync() + { + string path = Path.Combine(_tempDir, "append_lines.txt"); + File.WriteAllText(path, "Initial content\n"); + await File.AppendAllLinesAsync(path, _testLines); + File.Delete(path); + } + + // ==================== File.ReadAllTextAsync ==================== + + [Benchmark] + public async Task File_ReadAllTextAsync() + { + return await File.ReadAllTextAsync(_testFile); + } + + // ==================== File.ReadAllLinesAsync ==================== + + [Benchmark] + public async Task File_ReadAllLinesAsync() + { + return await File.ReadAllLinesAsync(_testFile); + } + + // ==================== StreamReader(path) ==================== + + [Benchmark] + public async Task StreamReader_PathConstructor() + { + using (var reader = new StreamReader(_testFile)) + { + return await reader.ReadToEndAsync(); + } + } + + // ==================== StreamWriter(path) ==================== + + [Benchmark] + public async Task StreamWriter_PathConstructor() + { + string path = Path.Combine(_tempDir, "streamwriter.txt"); + using (var writer = new StreamWriter(path)) + { + await writer.WriteAsync(_testContent); + } + File.Delete(path); + } + + // ==================== FileInfo.OpenText ==================== + + [Benchmark] + public async Task FileInfo_OpenText() + { + var fileInfo = new FileInfo(_testFile); + using (var reader = fileInfo.OpenText()) + { + return await reader.ReadToEndAsync(); + } + } + + // ==================== FileInfo.CreateText ==================== + + [Benchmark] + public async Task FileInfo_CreateText() + { + string path = Path.Combine(_tempDir, "fileinfo_create.txt"); + var fileInfo = new FileInfo(path); + using (var writer = fileInfo.CreateText()) + { + await writer.WriteAsync(_testContent); + } + File.Delete(path); + } + + // ==================== FileInfo.AppendText ==================== + + [Benchmark] + public async Task FileInfo_AppendText() + { + string path = Path.Combine(_tempDir, "fileinfo_append.txt"); + File.WriteAllText(path, "Initial content\n"); + var fileInfo = new FileInfo(path); + using (var writer = fileInfo.AppendText()) + { + await writer.WriteAsync(_testContent); + } + File.Delete(path); + } + } + + public class Program + { + public static void Main(string[] args) + { + var summary = BenchmarkRunner.Run(); + } + } +} diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.csproj b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.csproj new file mode 100644 index 00000000000000..bda7298861ec5a --- /dev/null +++ b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.csproj @@ -0,0 +1,17 @@ + + + + $(NetCoreAppCurrent) + Exe + enable + true + + + + + + + + + + diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/README.md b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/README.md new file mode 100644 index 00000000000000..470d7af57cd4c6 --- /dev/null +++ b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/README.md @@ -0,0 +1,70 @@ +# FileStream Buffering Optimization Benchmark + +This benchmark validates the performance improvements from disabling FileStream internal buffering when wrapped by StreamReader/StreamWriter. + +## Background + +When a FileStream is wrapped by StreamReader or StreamWriter, having both layers of buffering active causes: +- Additional memory allocations for `BufferedFileStreamStrategy` +- GC pressure from finalizers +- Lock contention on every ReadAsync/WriteAsync +- Reduced throughput + +By setting `bufferSize: 1` on FileStream, we skip the BufferedFileStreamStrategy wrapper and let StreamReader/StreamWriter handle all buffering. + +## Running the Benchmark + +From the repository root: + +```bash +cd src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark +dotnet run -c Release +``` + +## What's Being Tested + +The benchmark tests these public APIs that benefit from the optimization: + +1. **File.WriteAllLinesAsync** - Writing lines to a file +2. **File.AppendAllLinesAsync** - Appending lines to a file +3. **File.ReadAllTextAsync** - Reading entire file content +4. **File.ReadAllLinesAsync** - Reading all lines from a file +5. **StreamReader(path)** - Path-taking StreamReader constructor +6. **StreamWriter(path)** - Path-taking StreamWriter constructor +7. **FileInfo.OpenText()** - Opening file for reading +8. **FileInfo.CreateText()** - Creating file for writing +9. **FileInfo.AppendText()** - Appending to file + +## Expected Results + +With the optimization (bufferSize: 1), you should see: +- **Lower memory allocations** (fewer bytes allocated, fewer Gen0/Gen1/Gen2 collections) +- **Faster execution times** (especially for larger files) +- **Better throughput** for both read and write operations + +The improvements are most noticeable when: +- Working with files larger than the buffer size (4KB) +- Performing many small I/O operations +- Running under memory pressure + +## Baseline Comparison + +To compare before/after, you would need to: +1. Checkout the commit before the changes +2. Run the benchmark and save results +3. Checkout the commit with changes +4. Run the benchmark again +5. Use BenchmarkDotNet's comparison tools to analyze the difference + +Example: +```bash +# Before changes +git checkout +dotnet run -c Release --exporters json --artifacts ./before + +# After changes +git checkout +dotnet run -c Release --exporters json --artifacts ./after + +# Compare results manually or use BenchmarkDotNet comparison features +``` diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.cmd b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.cmd new file mode 100644 index 00000000000000..52d503cae098cd --- /dev/null +++ b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.cmd @@ -0,0 +1,42 @@ +@echo off +REM Script to run FileStream buffering optimization benchmarks + +setlocal + +REM Change to the directory containing this script +cd /d "%~dp0" + +REM Get the repository root (5 levels up from this script) +set REPO_ROOT=%~dp0..\..\..\..\.. +pushd %REPO_ROOT% +set REPO_ROOT=%CD% +popd + +REM Set up the dotnet path +set PATH=%REPO_ROOT%\.dotnet;%PATH% + +echo Running FileStream Buffering Optimization Benchmark... +echo Repository root: %REPO_ROOT% +dotnet --version +echo. + +REM Build in Release mode +echo Building benchmark... +dotnet build -c Release + +if errorlevel 1 ( + echo Build failed! + exit /b 1 +) + +echo. +echo Running benchmark (this may take several minutes)... +echo. + +REM Run the benchmark +dotnet run -c Release --no-build + +echo. +echo Benchmark complete! + +endlocal diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.sh b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.sh new file mode 100755 index 00000000000000..09f07af9fe4571 --- /dev/null +++ b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Script to run FileStream buffering optimization benchmarks + +# Change to the directory containing this script +cd "$(dirname "$0")" + +# Get the repository root (5 levels up from this script) +REPO_ROOT="$(cd ../../../../.. && pwd)" + +# Set up the dotnet path +export PATH="$REPO_ROOT/.dotnet:$PATH" + +echo "Running FileStream Buffering Optimization Benchmark..." +echo "Repository root: $REPO_ROOT" +echo "Dotnet version: $(dotnet --version)" +echo "" + +# Build in Release mode +echo "Building benchmark..." +dotnet build -c Release + +if [ $? -ne 0 ]; then + echo "Build failed!" + exit 1 +fi + +echo "" +echo "Running benchmark (this may take several minutes)..." +echo "" + +# Run the benchmark +dotnet run -c Release --no-build + +echo "" +echo "Benchmark complete!" From 9c1c78c815de5d4adf4b93dc117147459c2fa43a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Oct 2025 21:56:40 +0000 Subject: [PATCH 5/7] Remove benchmark files (performance test completed and results documented) Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../FileStreamBufferingBenchmark.cs | 173 ------------------ .../FileStreamBufferingBenchmark.csproj | 17 -- .../FileStreamBufferingBenchmark/README.md | 70 ------- .../run-benchmark.cmd | 42 ----- .../run-benchmark.sh | 35 ---- 5 files changed, 337 deletions(-) delete mode 100644 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.cs delete mode 100644 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.csproj delete mode 100644 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/README.md delete mode 100644 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.cmd delete mode 100755 src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.sh diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.cs b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.cs deleted file mode 100644 index 25543035020d04..00000000000000 --- a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.cs +++ /dev/null @@ -1,173 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; - -namespace System.IO.Tests.Performance -{ - /// - /// Benchmarks to validate FileStream buffering optimizations when used with StreamReader/StreamWriter. - /// These tests demonstrate the performance improvements from disabling FileStream internal buffering - /// when it's wrapped by StreamReader/StreamWriter (which provide their own buffering). - /// - [MemoryDiagnoser] - public class FileStreamBufferingBenchmark - { - private string _tempDir = null!; - private string _testFile = null!; - private string[] _testLines = null!; - private string _testContent = null!; - private const int LineCount = 1000; - private const int ContentSize = 50000; // ~50KB - - [GlobalSetup] - public void Setup() - { - _tempDir = Path.Combine(Path.GetTempPath(), "FileStreamBenchmark_" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(_tempDir); - _testFile = Path.Combine(_tempDir, "test.txt"); - - // Generate test data - _testLines = new string[LineCount]; - StringBuilder contentBuilder = new StringBuilder(); - for (int i = 0; i < LineCount; i++) - { - string line = $"This is test line {i} with some content to make it more realistic"; - _testLines[i] = line; - contentBuilder.AppendLine(line); - } - _testContent = contentBuilder.ToString(); - - // Create initial file for read tests - File.WriteAllText(_testFile, _testContent); - } - - [GlobalCleanup] - public void Cleanup() - { - try - { - if (Directory.Exists(_tempDir)) - { - Directory.Delete(_tempDir, true); - } - } - catch { } - } - - // ==================== File.WriteAllLinesAsync ==================== - - [Benchmark] - public async Task File_WriteAllLinesAsync() - { - string path = Path.Combine(_tempDir, "write_lines.txt"); - await File.WriteAllLinesAsync(path, _testLines); - File.Delete(path); - } - - // ==================== File.AppendAllLinesAsync ==================== - - [Benchmark] - public async Task File_AppendAllLinesAsync() - { - string path = Path.Combine(_tempDir, "append_lines.txt"); - File.WriteAllText(path, "Initial content\n"); - await File.AppendAllLinesAsync(path, _testLines); - File.Delete(path); - } - - // ==================== File.ReadAllTextAsync ==================== - - [Benchmark] - public async Task File_ReadAllTextAsync() - { - return await File.ReadAllTextAsync(_testFile); - } - - // ==================== File.ReadAllLinesAsync ==================== - - [Benchmark] - public async Task File_ReadAllLinesAsync() - { - return await File.ReadAllLinesAsync(_testFile); - } - - // ==================== StreamReader(path) ==================== - - [Benchmark] - public async Task StreamReader_PathConstructor() - { - using (var reader = new StreamReader(_testFile)) - { - return await reader.ReadToEndAsync(); - } - } - - // ==================== StreamWriter(path) ==================== - - [Benchmark] - public async Task StreamWriter_PathConstructor() - { - string path = Path.Combine(_tempDir, "streamwriter.txt"); - using (var writer = new StreamWriter(path)) - { - await writer.WriteAsync(_testContent); - } - File.Delete(path); - } - - // ==================== FileInfo.OpenText ==================== - - [Benchmark] - public async Task FileInfo_OpenText() - { - var fileInfo = new FileInfo(_testFile); - using (var reader = fileInfo.OpenText()) - { - return await reader.ReadToEndAsync(); - } - } - - // ==================== FileInfo.CreateText ==================== - - [Benchmark] - public async Task FileInfo_CreateText() - { - string path = Path.Combine(_tempDir, "fileinfo_create.txt"); - var fileInfo = new FileInfo(path); - using (var writer = fileInfo.CreateText()) - { - await writer.WriteAsync(_testContent); - } - File.Delete(path); - } - - // ==================== FileInfo.AppendText ==================== - - [Benchmark] - public async Task FileInfo_AppendText() - { - string path = Path.Combine(_tempDir, "fileinfo_append.txt"); - File.WriteAllText(path, "Initial content\n"); - var fileInfo = new FileInfo(path); - using (var writer = fileInfo.AppendText()) - { - await writer.WriteAsync(_testContent); - } - File.Delete(path); - } - } - - public class Program - { - public static void Main(string[] args) - { - var summary = BenchmarkRunner.Run(); - } - } -} diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.csproj b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.csproj deleted file mode 100644 index bda7298861ec5a..00000000000000 --- a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/FileStreamBufferingBenchmark.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - $(NetCoreAppCurrent) - Exe - enable - true - - - - - - - - - - diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/README.md b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/README.md deleted file mode 100644 index 470d7af57cd4c6..00000000000000 --- a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# FileStream Buffering Optimization Benchmark - -This benchmark validates the performance improvements from disabling FileStream internal buffering when wrapped by StreamReader/StreamWriter. - -## Background - -When a FileStream is wrapped by StreamReader or StreamWriter, having both layers of buffering active causes: -- Additional memory allocations for `BufferedFileStreamStrategy` -- GC pressure from finalizers -- Lock contention on every ReadAsync/WriteAsync -- Reduced throughput - -By setting `bufferSize: 1` on FileStream, we skip the BufferedFileStreamStrategy wrapper and let StreamReader/StreamWriter handle all buffering. - -## Running the Benchmark - -From the repository root: - -```bash -cd src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark -dotnet run -c Release -``` - -## What's Being Tested - -The benchmark tests these public APIs that benefit from the optimization: - -1. **File.WriteAllLinesAsync** - Writing lines to a file -2. **File.AppendAllLinesAsync** - Appending lines to a file -3. **File.ReadAllTextAsync** - Reading entire file content -4. **File.ReadAllLinesAsync** - Reading all lines from a file -5. **StreamReader(path)** - Path-taking StreamReader constructor -6. **StreamWriter(path)** - Path-taking StreamWriter constructor -7. **FileInfo.OpenText()** - Opening file for reading -8. **FileInfo.CreateText()** - Creating file for writing -9. **FileInfo.AppendText()** - Appending to file - -## Expected Results - -With the optimization (bufferSize: 1), you should see: -- **Lower memory allocations** (fewer bytes allocated, fewer Gen0/Gen1/Gen2 collections) -- **Faster execution times** (especially for larger files) -- **Better throughput** for both read and write operations - -The improvements are most noticeable when: -- Working with files larger than the buffer size (4KB) -- Performing many small I/O operations -- Running under memory pressure - -## Baseline Comparison - -To compare before/after, you would need to: -1. Checkout the commit before the changes -2. Run the benchmark and save results -3. Checkout the commit with changes -4. Run the benchmark again -5. Use BenchmarkDotNet's comparison tools to analyze the difference - -Example: -```bash -# Before changes -git checkout -dotnet run -c Release --exporters json --artifacts ./before - -# After changes -git checkout -dotnet run -c Release --exporters json --artifacts ./after - -# Compare results manually or use BenchmarkDotNet comparison features -``` diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.cmd b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.cmd deleted file mode 100644 index 52d503cae098cd..00000000000000 --- a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.cmd +++ /dev/null @@ -1,42 +0,0 @@ -@echo off -REM Script to run FileStream buffering optimization benchmarks - -setlocal - -REM Change to the directory containing this script -cd /d "%~dp0" - -REM Get the repository root (5 levels up from this script) -set REPO_ROOT=%~dp0..\..\..\..\.. -pushd %REPO_ROOT% -set REPO_ROOT=%CD% -popd - -REM Set up the dotnet path -set PATH=%REPO_ROOT%\.dotnet;%PATH% - -echo Running FileStream Buffering Optimization Benchmark... -echo Repository root: %REPO_ROOT% -dotnet --version -echo. - -REM Build in Release mode -echo Building benchmark... -dotnet build -c Release - -if errorlevel 1 ( - echo Build failed! - exit /b 1 -) - -echo. -echo Running benchmark (this may take several minutes)... -echo. - -REM Run the benchmark -dotnet run -c Release --no-build - -echo. -echo Benchmark complete! - -endlocal diff --git a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.sh b/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.sh deleted file mode 100755 index 09f07af9fe4571..00000000000000 --- a/src/libraries/System.Runtime/tests/PerformanceTests/FileStreamBufferingBenchmark/run-benchmark.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# Script to run FileStream buffering optimization benchmarks - -# Change to the directory containing this script -cd "$(dirname "$0")" - -# Get the repository root (5 levels up from this script) -REPO_ROOT="$(cd ../../../../.. && pwd)" - -# Set up the dotnet path -export PATH="$REPO_ROOT/.dotnet:$PATH" - -echo "Running FileStream Buffering Optimization Benchmark..." -echo "Repository root: $REPO_ROOT" -echo "Dotnet version: $(dotnet --version)" -echo "" - -# Build in Release mode -echo "Building benchmark..." -dotnet build -c Release - -if [ $? -ne 0 ]; then - echo "Build failed!" - exit 1 -fi - -echo "" -echo "Running benchmark (this may take several minutes)..." -echo "" - -# Run the benchmark -dotnet run -c Release --no-build - -echo "" -echo "Benchmark complete!" From 3b51fa8a0158b9c1fa932b4c9b13b38a8cd32896 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Oct 2025 02:30:07 +0000 Subject: [PATCH 6/7] Revert inappropriate buffering changes, keep only AsyncStreamReader optimization Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../src/Microsoft/CSharp/CSharpCodeGenerator.cs | 4 ++-- .../src/System/CodeDom/Compiler/CodeCompiler.cs | 6 +++--- .../System.CodeDom/src/System/CodeDom/Compiler/Executor.cs | 2 +- .../src/System/ComponentModel/LicFileLicenseProvider.cs | 2 +- .../System.Management/src/System/Management/WMIGenerator.cs | 2 +- .../Net/NetworkInformation/StringParsingHelpers.Misc.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/IO/File.cs | 2 +- .../System.Private.CoreLib/src/System/IO/StreamReader.cs | 2 +- .../System.Private.CoreLib/src/System/IO/StreamWriter.cs | 2 +- .../System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs b/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs index 990a10f7abe28f..c95e52baf5600a 100644 --- a/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs +++ b/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs @@ -3020,7 +3020,7 @@ private CompilerResults FromDomBatch(CompilerParameters options, CodeCompileUnit ResolveReferencedAssemblies(options, ea[i]); filenames[i] = options.TempFiles.AddExtension(i + FileExtension); - using (var fs = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) + using (var fs = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read)) using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) { ((ICodeGenerator)this).GenerateCodeFromCompileUnit(ea[i], sw, _options); @@ -3055,7 +3055,7 @@ private static CompilerResults FromSourceBatch(CompilerParameters options, strin for (int i = 0; i < sources.Length; i++) { string name = options.TempFiles.AddExtension(i + FileExtension); - using (var fs = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) + using (var fs = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var sw = new StreamWriter(fs, Encoding.UTF8)) { sw.Write(sources[i]); diff --git a/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/CodeCompiler.cs b/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/CodeCompiler.cs index da37bc5489de77..65300e8602eac8 100644 --- a/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/CodeCompiler.cs +++ b/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/CodeCompiler.cs @@ -144,7 +144,7 @@ protected virtual CompilerResults FromDomBatch(CompilerParameters options, CodeC ResolveReferencedAssemblies(options, ea[i]); filenames[i] = options.TempFiles.AddExtension(i + FileExtension); - using (var fs = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) + using (var fs = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read)) using (var sw = new StreamWriter(fs, Encoding.UTF8)) { ((ICodeGenerator)this).GenerateCodeFromCompileUnit(ea[i], sw, Options); @@ -185,7 +185,7 @@ protected virtual string GetResponseFileCmdArgs(CompilerParameters options, stri { string responseFileName = options.TempFiles.AddExtension("cmdline"); - using (var fs = new FileStream(responseFileName, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) + using (var fs = new FileStream(responseFileName, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var sw = new StreamWriter(fs, Encoding.UTF8)) { sw.Write(cmdArgs); @@ -205,7 +205,7 @@ protected virtual CompilerResults FromSourceBatch(CompilerParameters options, st for (int i = 0; i < sources.Length; i++) { string name = options.TempFiles.AddExtension(i + FileExtension); - using (var fs = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1)) + using (var fs = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var sw = new StreamWriter(fs, Encoding.UTF8)) { sw.Write(sources[i]); diff --git a/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/Executor.cs b/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/Executor.cs index c664e0e72d40f4..4b2513a6d27520 100644 --- a/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/Executor.cs +++ b/src/libraries/System.CodeDom/src/System/CodeDom/Compiler/Executor.cs @@ -13,7 +13,7 @@ public static class Executor private const int ProcessTimeOut = 600000; private static FileStream CreateInheritedFile(string file) => - new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.Read | FileShare.Inheritable, bufferSize: 1); + new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.Read | FileShare.Inheritable); public static void ExecWait(string cmd, TempFileCollection tempFiles) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs index f84b7ad82dc1fe..573e97e6c7e320 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs @@ -84,7 +84,7 @@ protected virtual string GetKey(Type type) Debug.WriteLine($"Looking for license in: {licenseFile}"); if (File.Exists(licenseFile)) { - Stream licStream = new FileStream(licenseFile, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1); + Stream licStream = new FileStream(licenseFile, FileMode.Open, FileAccess.Read, FileShare.Read); StreamReader sr = new StreamReader(licStream); string? s = sr.ReadLine(); sr.Close(); diff --git a/src/libraries/System.Management/src/System/Management/WMIGenerator.cs b/src/libraries/System.Management/src/System/Management/WMIGenerator.cs index d8749e509cfdef..93f1186f680b32 100644 --- a/src/libraries/System.Management/src/System/Management/WMIGenerator.cs +++ b/src/libraries/System.Management/src/System/Management/WMIGenerator.cs @@ -182,7 +182,7 @@ public bool GenerateCode(CodeLanguage lang, string filePath, string netNamespace InitializeCodeGeneration(); //Now create the filestream (output file) - tw = new StreamWriter(new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1), System.Text.Encoding.UTF8); + tw = new StreamWriter(new FileStream(filePath, FileMode.Create), System.Text.Encoding.UTF8); return GenerateAndWriteCode(lang); diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs index af3b2a384f3da2..1d6c3f47e0838f 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs @@ -41,7 +41,7 @@ internal static StreamReader OpenStreamReader(string filePath) { try { - return new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, useAsync: false)); + return new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 0x1000, useAsync: false)); } catch (Exception e) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 24f592ba34f585..e8e0d49f447512 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -1339,7 +1339,7 @@ private static Task WriteAllLinesAsync(string path, IEnumerable contents try { writer = new StreamWriter( - new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1, FileOptions.Asynchronous), + new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous), encoding); } catch (Exception e) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index ab81d84bf3f87c..e06a2512264c8d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -232,7 +232,7 @@ private static FileStream ValidateArgsAndOpenPath(string path, int bufferSize) ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bufferSize); } - return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1); + return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, FileStream.DefaultBufferSize); } public override void Close() diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index 67280cb381f82b..4f370df7937b0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -189,7 +189,7 @@ private static FileStream ValidateArgsAndOpenPath(string path, bool append, int ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bufferSize); } - return new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 1); + return new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, FileStream.DefaultBufferSize); } public override void Close() diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs index cad44c2ab2857c..d45397bb634f11 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs @@ -291,7 +291,7 @@ public XmlTextWriter(Stream w, Encoding? encoding) : this() // Creates an instance of the XmlTextWriter class using the specified file. public XmlTextWriter(string filename, Encoding? encoding) : this(new FileStream(filename, FileMode.Create, - FileAccess.Write, FileShare.Read, bufferSize: 1), encoding) + FileAccess.Write, FileShare.Read), encoding) { } From 271f4eb5570ede0e6b2e6fe1c7e137611b26c8a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:11:26 +0000 Subject: [PATCH 7/7] Revert AsyncStreamReader change - not all code paths use large buffers Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- src/libraries/System.Private.CoreLib/src/System/IO/File.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index e8e0d49f447512..e7c4e948d6c3ce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -1077,7 +1077,7 @@ public static void Decrypt(string path) // we will have asynchronous file access faked by the thread pool. We want the real thing. private static StreamReader AsyncStreamReader(string path, Encoding encoding) => new StreamReader( - new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, FileOptions.Asynchronous | FileOptions.SequentialScan), + new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan), encoding, detectEncodingFromByteOrderMarks: true); public static Task ReadAllTextAsync(string path, CancellationToken cancellationToken = default)