none
Is StreamReader.ReadLineAsync much slower than ReadLine?

    Question

  • 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,95758ms

       Async 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



    Monday, September 24, 2012 9:06 AM

Answers

  • 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
    Monday, September 24, 2012 4:34 PM
    Moderator

All replies

  • 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
    Monday, September 24, 2012 4:34 PM
    Moderator
  • 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.

    Tuesday, September 25, 2012 4:58 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).

    Wednesday, September 26, 2012 5:00 AM