C# StreamReader ReadToEndAsync Example (Performance)Use the StreamReader ReadToEndAsync method with the async and await keywords.
dot net perls

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?



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; } }
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-2021 sam allen. send bug reports to info@dotnetperls.com.