Is StreamReader.ReadLineAsync much slower than ReadLine?
-
Monday, September 24, 2012 9:06 AM
I am trying to get a feeling for the performance-side of async I/O. I know that async I/O does not block the calling thread and allows me to do things in parallel.
In my test ReadLineAsync is way slower than ReadLine. Is this to be expected, or am I doing something wrong. In contrast to ReadLineAsync SqlDataReader.ExecuteReaderAsync seems to be faster than its synchronous pendant.
Here my test results and code:
_file1MB Sync 254,65114ms
_file1MB Async 420,0024ms
8500 rows ADO_DataReaderSync 9,64463ms
8500 rows ADO_DataReaderASync 7,95758msAsync Function Measure() As Task Dim iterations = 10 Dim stw As New Stopwatch stw.Restart() For i = 0 To iterations - 1 FileIO_ReadLineSync(_file10MB) Next stw.Stop() Console.WriteLine("{0} elapsed:{1} per iteration={2}ms", "_file1MB Sync", stw.Elapsed, stw.Elapsed.TotalMilliseconds / iterations) stw.Restart() For i = 0 To iterations - 1 Await FileIO_ReadLineAsync(_file10MB) Next stw.Stop() Console.WriteLine("{0} elapsed:{1} per iteration={2}ms", "_file1MB Async", stw.Elapsed, stw.Elapsed.TotalMilliseconds / iterations) stw.Restart() For i = 0 To iterations - 1 ADO_DataReaderSync() Next stw.Stop() Console.WriteLine("{0} elapsed:{1} per iteration={2}ms", "ADO_DataReaderSync Sync", stw.Elapsed, stw.Elapsed.TotalMilliseconds / iterations) stw.Restart() For i = 0 To iterations - 1 Await ADO_DataReaderASync() Next stw.Stop() Console.WriteLine("{0} elapsed:{1} per iteration={2}ms", "ADO_DataReaderASync Async", stw.Elapsed, stw.Elapsed.TotalMilliseconds / iterations) End Function Function FileIO_ReadLineSync(filePath As String) As Long Dim fileLength As Integer Using reader As New StreamReader(filePath) Dim line = reader.ReadLine While line IsNot Nothing fileLength += line.Length line = reader.ReadLine End While Return fileLength End Using End Function Async Function FileIO_ReadLineAsync(filePath As String) As Task(Of Long) Dim fileLength As Integer Using reader As New StreamReader(filePath) Dim line = Await reader.ReadLineAsync() While line IsNot Nothing fileLength += line.Length line = Await reader.ReadLineAsync() End While Return fileLength End Using End Function Function ADO_DataReaderSync() As List(Of Date) Using cnx As New SqlClient.SqlConnection(_connectionString) Dim cmd As New SqlCommand("SELECT * FROM [Purchasing].[PurchaseOrderDetail]", cnx) cnx.Open() Using rdr As SqlDataReader = cmd.ExecuteReader Dim productNames As New List(Of Date) While rdr.Read productNames.Add(rdr("DueDate")) End While Return productNames End Using End Using End Function Async Function ADO_DataReaderASync() As Task(Of List(Of Date)) Using cnx As New SqlClient.SqlConnection(_connectionString) Dim cmd As New SqlCommand("SELECT * FROM [Purchasing].[PurchaseOrderDetail]t", cnx) Await cnx.OpenAsync() Using rdr As SqlDataReader = Await cmd.ExecuteReaderAsync.ConfigureAwait(False) Dim productNames As New List(Of Date) While rdr.Read productNames.Add(rdr("DueDate")) End While Return productNames End Using End Using End Function
All Replies
-
Monday, September 24, 2012 4:34 PMModerator
In general, the "Async" methods will typically be slower than a synchronous version, especially if you're using the new await/async calls. There's a fair amount of overhead involved in making the call asynchronous and posting the results back to the calling method on the correct SynchronizationContext.
When you use ReadLineAsync on a file, in your test above, you're purely showing that extra overhead. You're not doing anything with the resulting data at all, so the extra timing is purely the wasted overhead to reschedule on the proper context. If you were doing some "real work" in between, it would likely be less noticeable or objectionable, especially since you'd get back the responsiveness you'd lose when using the non-async version.
(Note that your SQL comparison isn't exactly fair - to be the "same", you should make the read calls async, as well, by using ReadAsync - right now, you're connecting asynchronously, but still reading synchronously in both versions. That being said, I'm actually surprised it's faster - I suspect it's due to the fact that the first sync call happens first, so the initial [very slow] connection is added into the sync version. For better timings, you should call each method once prior to starting your stopwatches.)
In general, making methods asynchronous and using await/async isn't going to improve performance - in many (most?) cases, it'll hurt total perf., but that's not the goal. The goal is to remove latency. By making the calls asynchronous, you can stay off the UI thread, or allow more work to pump through a WCF service, etc., since you're not blocking and waiting on the results. This gives a perception of performance, as you're not "hanging", even if things actually take longer when measured by a clock.
Reed Copsey, Jr. - http://reedcopsey.com
If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".- Marked As Answer by pmeinl Tuesday, October 09, 2012 5:12 AM
-
Tuesday, September 25, 2012 4:58 AM
I know what we are gaining by using async. I am trying to get a feeling for the performance penalty of async methods because I am considering using async by default for certain application types if this penalty is small enough.
To enforce responsive UIs the WinRT designers found it acceptable to offer async-only methods. I will certainly use more async in all our GUIs and do consider using more async for better throughput in our services.
I generally favor simplicity and robustness and only tune for performance where necessary. In the past we used sync by default with the exception of calling some services and where platforms like the phone enforced async. We rarely tuned by using async. With the advantages of async and it now being quite easy to code and compose (using Await and TAP methods) I am wondering if we should use aync by default and only tune for performance by using sync when needed.
-
Wednesday, September 26, 2012 5:00 AM
I had assumed async to generally be a little slower than sync. Reading Using SqlDataReader’s new async methods in .Net 4.5 made me question this and in my sample code I show a constellation where it is actually faster.
AFAIK Windows file I/O internally is async. Looking at this naively it is not clear to me, why async file i/O in .NET should be slower than sync at all.
So let me rephrase my original question: Is the StreamReader ReadLine\ReadLineAsync performance penalty 250 vs 400ms typical? Or did I miss something in my sample code (like I could have forgotten to use useAsync in the FileStream constructor with the legacy.BeginRead).

