Skip to content

Commit 26ce1d9

Browse files
Fastest way to read a text file: retarget net10.0, add async reads, re-run benchmark
- Both projects net7.0 -> net10.0; Nullable enabled on the sample project. - BenchmarkDotNet 0.13.7 -> 0.15.8. - Test packages to current: Microsoft.NET.Test.Sdk 18.9.0, xunit 2.9.3, xunit.runner.visualstudio 4.0.0, coverlet.collector 10.0.1. This clears NU1903 (Newtonsoft.Json 9.0.1 came in transitively through the old test SDK). - Primary constructor on WaysToReadATextFileInCsharp. - UseBufferedStreamObjectWithNoFileStreamBuffer moves to the FileStreamOptions overload. FileStreamOptions.BufferSize documents "0 or 1 means that buffering should be disabled"; the five-argument FileStream constructor documents its bufferSize as greater than zero and throws on zero. Same behaviour, but the section now stands on the API whose docs state what it teaches. - Three async reads with matching benchmarks and tests: UseFileReadAllTextAsync, UseFileReadLinesAsync, UseStreamReaderReadToEndAsync. The StreamReader one is built over a FileStream with FileOptions.Asynchronous rather than the path-taking constructor, which does not set that flag and so fakes async file access on the thread pool. - Benchmark file resolved with AppContext.BaseDirectory and copied to the output directory, replacing a six-level Directory.GetParent walk tied to one layout.
1 parent a59a6f6 commit 26ce1d9

5 files changed

Lines changed: 90 additions & 21 deletions

File tree

‎files-csharp/FastestWayToReadATextFileInCsharp/FastestWayToReadATextFileInCsharp/FastestWayToReadATextFileInCsharp.csproj‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net7.0</TargetFramework>
5+
<TargetFramework>net10.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
7-
<Nullable>disable</Nullable>
7+
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="BenchmarkDotNet" Version="0.13.7" />
11+
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<Content Include="BenchmarkTextFile.txt">
16+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
17+
</Content>
1218
</ItemGroup>
1319

1420
</Project>

‎files-csharp/FastestWayToReadATextFileInCsharp/FastestWayToReadATextFileInCsharp/WaysToReadATextFileInCsharp.cs‎

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,9 @@
33

44
namespace FastestWayToReadATextFileInCsharp;
55

6-
public class WaysToReadATextFileInCsharp
6+
public class WaysToReadATextFileInCsharp(string sampleFilePath)
77
{
8-
private readonly string _sampleFilePath;
9-
10-
public WaysToReadATextFileInCsharp(string sampleFilePath)
11-
{
12-
_sampleFilePath = sampleFilePath;
13-
}
8+
private readonly string _sampleFilePath = sampleFilePath;
149

1510
public string UseFileReadAllLines()
1611
{
@@ -132,11 +127,13 @@ public string UseBufferedStreamObjectWithNoFileStreamBuffer()
132127
{
133128
var stringBuilder = new StringBuilder();
134129

135-
using var fileStream = new FileStream(_sampleFilePath,
136-
FileMode.Open,
137-
FileAccess.Read,
138-
FileShare.Read,
139-
0);
130+
using var fileStream = new FileStream(_sampleFilePath, new FileStreamOptions
131+
{
132+
Mode = FileMode.Open,
133+
Access = FileAccess.Read,
134+
Share = FileShare.Read,
135+
BufferSize = 0
136+
});
140137
using var bufferedStream = new BufferedStream(fileStream);
141138
using var streamReader = new StreamReader(bufferedStream);
142139

@@ -148,4 +145,34 @@ public string UseBufferedStreamObjectWithNoFileStreamBuffer()
148145

149146
return stringBuilder.ToString();
150147
}
148+
149+
public async Task<string> UseFileReadAllTextAsync()
150+
=> await File.ReadAllTextAsync(_sampleFilePath);
151+
152+
public async Task<string> UseFileReadLinesAsync()
153+
{
154+
var stringBuilder = new StringBuilder();
155+
156+
await foreach (var line in File.ReadLinesAsync(_sampleFilePath))
157+
{
158+
stringBuilder.AppendLine(line);
159+
}
160+
stringBuilder.Length -= Environment.NewLine.Length;
161+
162+
return stringBuilder.ToString();
163+
}
164+
165+
public async Task<string> UseStreamReaderReadToEndAsync()
166+
{
167+
using var fileStream = new FileStream(_sampleFilePath, new FileStreamOptions
168+
{
169+
Mode = FileMode.Open,
170+
Access = FileAccess.Read,
171+
Share = FileShare.Read,
172+
Options = FileOptions.Asynchronous | FileOptions.SequentialScan
173+
});
174+
using var streamReader = new StreamReader(fileStream);
175+
176+
return await streamReader.ReadToEndAsync();
177+
}
151178
}

‎files-csharp/FastestWayToReadATextFileInCsharp/FastestWayToReadATextFileInCsharp/WaysToReadATextFileInCsharpBenchmark.cs‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace FastestWayToReadATextFileInCsharp;
1010
public class WaysToReadATextFileInCsharpBenchmark
1111
{
1212
private static readonly string SampleFilePath
13-
= Path.Combine(Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.Parent.Parent.Parent.FullName, "BenchmarkTextFile.txt");
13+
= Path.Combine(AppContext.BaseDirectory, "BenchmarkTextFile.txt");
1414

1515
private readonly WaysToReadATextFileInCsharp _textFileReader = new(SampleFilePath);
1616

@@ -53,4 +53,16 @@ public string BufferedStreamObject()
5353
[Benchmark]
5454
public string BufferedStreamWithNoFileStreamBuffer()
5555
=> _textFileReader.UseBufferedStreamObjectWithNoFileStreamBuffer();
56+
57+
[Benchmark]
58+
public async Task<string> FileReadAllTextAsync()
59+
=> await _textFileReader.UseFileReadAllTextAsync();
60+
61+
[Benchmark]
62+
public async Task<string> FileReadLinesAsync()
63+
=> await _textFileReader.UseFileReadLinesAsync();
64+
65+
[Benchmark]
66+
public async Task<string> StreamReaderReadToEndAsync()
67+
=> await _textFileReader.UseStreamReaderReadToEndAsync();
5668
}

‎files-csharp/FastestWayToReadATextFileInCsharp/Tests/Tests.csproj‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net7.0</TargetFramework>
4+
<TargetFramework>net10.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77

88
<IsPackable>false</IsPackable>
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
13-
<PackageReference Include="xunit" Version="2.4.2" />
14-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
13+
<PackageReference Include="xunit" Version="2.9.3" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
1515
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1616
<PrivateAssets>all</PrivateAssets>
1717
</PackageReference>
18-
<PackageReference Include="coverlet.collector" Version="3.1.2">
18+
<PackageReference Include="coverlet.collector" Version="10.0.1">
1919
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2020
<PrivateAssets>all</PrivateAssets>
2121
</PackageReference>

‎files-csharp/FastestWayToReadATextFileInCsharp/Tests/WaysToReadATextFileInCsharpTests.cs‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,30 @@ public void WhenReadingATextFileWithBufferedStreamObjectWithFileStreamBufferDisa
100100
Assert.Equal(ExpectedText, result);
101101
}
102102

103+
[Fact]
104+
public async Task WhenReadingATextFileWithFileReadAllTextAsync_ThenReturnsExpectedText()
105+
{
106+
var result = await _textFileReader.UseFileReadAllTextAsync();
107+
108+
Assert.Equal(ExpectedText, result);
109+
}
110+
111+
[Fact]
112+
public async Task WhenReadingATextFileWithFileReadLinesAsync_ThenReturnsExpectedText()
113+
{
114+
var result = await _textFileReader.UseFileReadLinesAsync();
115+
116+
Assert.Equal(ExpectedText, result);
117+
}
118+
119+
[Fact]
120+
public async Task WhenReadingATextFileWithStreamReaderReadToEndAsync_ThenReturnsExpectedText()
121+
{
122+
var result = await _textFileReader.UseStreamReaderReadToEndAsync();
123+
124+
Assert.Equal(ExpectedText, result);
125+
}
126+
103127
public void Dispose()
104128
{
105129
if (File.Exists(TempFilePath))

0 commit comments

Comments
 (0)