C# StreamReader ReadToEndAsync Example (Performance)

Use the StreamReader ReadToEndAsync method with the async and await keywords.
StreamReader, ReadToEndAsync. With ReadToEndAsync on StreamReader, we can read in a file without blocking other code execution. But when can this feature lead to a performance gain?asyncStreamReader
In this test, we perform an expensive, CPU-bound computation while reading in a file. The same computation is done in the Main method.
Example code. Here we have 2 code paths: the first uses async and await. The second does not. The code does the same things, but the first iteration in the loop is asynchronous.

CreateHugeFile: This is needed for testing. Make sure you have a correct path here—you can change it as needed for your system.

Async: Consider ComputeDataFromFileAsync. It uses Task, async, await, and ReadToEndAsync on StreamReader.

And: Both Compute Data methods use calls to ComputeSum(). This causes extreme CPU load.

C# program that uses async, StreamReader ReadToEndAsync using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; class Program { const string _file = @"C:\programs\huge-file"; public static void Main() { // Huge file needed for testing. CreateHugeFile(); // Loop over tests. for (int i = 0; i <= 1; i++) { // Use async on first iteration only. bool useAsync = i == 0; Console.WriteLine("Use async: " + useAsync); var t1 = Stopwatch.StartNew(); if (useAsync) { // Use async code here. Task<int> task = ComputeDataFromFileAsync(); Console.WriteLine(ComputeSum()); task.Wait(); Console.WriteLine("Data: " + task.Result); } else { // Use synchronous code here. int result = ComputeDataFromFile(); Console.WriteLine(ComputeSum()); Console.WriteLine("Data: " + result); } Console.WriteLine("Elapsed: " + t1.ElapsedMilliseconds.ToString()); } } static void CreateHugeFile() { using (StreamWriter writer = new StreamWriter(_file)) { for (int i = 0; i < 10000; i++) { writer.WriteLine("Huge file line"); } } } static double ComputeSum(int lengthArgument = 60000) { // Does many computations based on argument. // ... Meant to be slow. double[] array = new double[lengthArgument]; for (int i = 0; i < array.Length; i++) { array[i] = i; } for (int z = 0; z < 100; z++) { for (int i = 0; i < array.Length; i++) { array[i] = (int)Math.Sqrt(array[i]) + (int)Math.Pow(array[i], 2) + 10; } } return array.Sum(); } static async Task<int> ComputeDataFromFileAsync() { int count = 0; using (StreamReader reader = new StreamReader(_file)) { string contents = await reader.ReadToEndAsync(); count += (int)ComputeSum(contents.Length % 60000); } return count; } static int ComputeDataFromFile() { int count = 0; using (StreamReader reader = new StreamReader(_file)) { string contents = reader.ReadToEnd(); count += (int)ComputeSum(contents.Length % 60000); } return count; } } Output Use async: True 2230342492166 Data: -2147483648 Elapsed: 522 Use async: False 2230342492166 Data: -2147483648 Elapsed: 805
Notes, ComputeSum. With ComputeSum, we have high CPU usage during file loads. And we call ComputeSum in main as well—this is where the performance is most affected by a sync code.

Results: With ReadToEndAsync, our program finishes in 522 ms. With ReadToEnd (no async) it finishes in 805 ms.

So: The async, await, Task, and ReadToEndAsync features lead to a significant speedup in the program.

Notes, results. The programs come to the same result—they do the same thing. But async gives us parallel processing, so the program finishes much faster.
A summary. With async, await, ReadToEndAsync and Task, we can achieve a big performance boost. The important thing is that excess CPU usage must be present for this to help.StreamReader ReadToEnd
© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to
Dot Net Perls