【问题标题】:Is Async I/O generally a little slower than sync I/O?异步 I/O 通常比同步 I/O 慢一点吗?
【发布时间】:2012-09-14 15:25:14
【问题描述】:

虽然有利于线程卫生,但我希望异步 I/O 总是比同步 I/O 慢一点。我的测试似乎证明异步 I/O 有时比同步更快。 我在这里错过了什么?

[编辑] 我最初的测量结果是错误的(我没有删除它们以免使所做的 cmets 无效)。

以下是一些固定定时循环的测量结果: ADO_DataReaderSync 每次迭代=4,17ms ADO_DataReaderASyncReader 每次迭代=3,55ms(仅 ExecuteReaderAsync) ADO_DataReaderASyncRead 每次迭代=11,28ms(ExecuteReaderAsync 和 ReadAsync) FileIO_ReadToEndSync SmallFile 每次迭代=3,67ms FileIO_ReadToEndAsync SmallFile 每次迭代=8,97ms FileIO_ReadToEndSync LargeFile 每次迭代=266,34ms FileIO_ReadToEndAsync LargeFile 每次迭代=322,05ms

错误测量:

ADO_ReadSync elapsed:00:00:00.0012249 每次迭代=0,12249ms ADO_ReadAsync elapsed:00:00:00.0050702 每次迭代=0,50702ms ADO_DataReaderSync elapsed:00:00:00.0090513 每次迭代=0,90513ms ADO_DataReaderASync elapsed:00:00:00.0044125 每次迭代=0,44125ms FileIO_ReadSync LargeFile elapsed:00:00:00.0655596 每次迭代=6,55596ms FileIO_ReadAsync LargeFile elapsed:00:00:00.0003056 每次迭代=0,03056ms FileIO_ReadSync SmallFile elapsed:00:00:00.0005619 每次迭代=0,05619ms FileIO_ReadAsync SmallFile elapsed:00:00:00.0002955 每次迭代=0,02955ms

使用的测试代码:

Module Module1
Private Const _connectionString = "Data Source=zulu;Initial Catalog=AdventureWorks;Integrated Security=True"

Sub Main()
    DoTimed("ADO_ReadSync", Sub() ADO_ScalarSync(), 10)
    DoTimed("ADO_ReadAsync", Async Sub() Await ADO_ScalarAsync(), 10)
    DoTimed("ADO_DataReaderSync", Sub() ADO_DataReaderSync(), 10)
    DoTimed("ADO_DataReaderASync", Async Sub() Await ADO_DataReaderASync(), 10)

    Const filePathLargeFile = "O:\Temp\TestFiles\In\todo.txt"
    Const filePathSmallFile = "O:\Temp\TestFiles\In\eula.txt"
    DoTimed("FileIO_ReadSync LargeFile", Sub() FileIO_ReadSync(filePathLargeFile), 10)
    DoTimed("FileIO_ReadAsync LargeFile", Async Sub() Await FileIO_ReadAsync(filePathLargeFile), 10)
    DoTimed("FileIO_ReadSync SmallFile", Sub() FileIO_ReadSync(filePathSmallFile), 10)
    DoTimed("FileIO_ReadAsync SmallFile", Async Sub() Await FileIO_ReadAsync(filePathSmallFile), 10)

    Console.WriteLine("...")
    Console.ReadLine()
End Sub

Function ADO_ScalarSync() As Integer
    Using cnx As New SqlClient.SqlConnection(_connectionString)
        Dim cmd As New SqlCommand("SELECT COUNT(*) FROM Production.Product", cnx)
        cnx.Open()
        Return cmd.ExecuteScalar
    End Using
End Function

Async Function ADO_ScalarAsync() As Task(Of Integer)
    'Beginning in the .NET Framework 4.5 RC, these methods no longer require Asynchronous Processing=true in the connection string
    Using cnx As New SqlClient.SqlConnection(_connectionString)
        Dim cmd As New SqlCommand("SELECT COUNT(*) FROM Production.Product", cnx)
        cnx.Open()
        Return Await cmd.ExecuteScalarAsync
    End Using
End Function

Function ADO_DataReaderSync() As List(Of String)
    Using cnx As New SqlClient.SqlConnection(_connectionString)
        Dim cmd As New SqlCommand("SELECT * FROM Production.Product", cnx)
        cnx.Open()
        Using rdr As SqlDataReader = cmd.ExecuteReader
            Dim productNames As New List(Of String)
            While rdr.Read
                productNames.Add(rdr("Name"))
            End While
            Return productNames
        End Using
    End Using
End Function

Async Function ADO_DataReaderASync() As Task(Of List(Of String))
    Using cnx As New SqlClient.SqlConnection(_connectionString)
        'Await cnx.OpenAsync() 'I would only use .OpenAsync if the DB is commonly down and we would hang on the timeout
        Dim cmd As New SqlCommand("SELECT * FROM Production.Product", cnx)
        cnx.Open()
        Using rdr As SqlDataReader = Await cmd.ExecuteReaderAsync
            Dim productNames As New List(Of String)
            While rdr.Read
                productNames.Add(rdr("Name"))
            End While
            Return productNames
        End Using
    End Using
End Function

Function FileIO_ReadSync(filePath As String) As Long
    Using reader As New StreamReader(filePath)
        Dim fileString = reader.ReadToEnd
        Return fileString.Length
    End Using
End Function

Async Function FileIO_ReadAsync(filePath As String) As Task(Of Long)
    Using reader As New StreamReader(filePath)
        Dim fileString = Await reader.ReadToEndAsync().ConfigureAwait(False)
        Return fileString.Length
    End Using
End Function

结束模块

Public Module Timing
Function DoTimed(name As String, operation As Action, Optional iterations As Integer = 1) As TimeSpan
    operation() 'Warmup

    Dim stw = Stopwatch.StartNew
    DoIterate(operation, iterations)
    stw.Stop()
    Console.WriteLine("{0} elapsed:{1} per iteration={2}ms", name, stw.Elapsed, stw.Elapsed.TotalMilliseconds / iterations)
    Return stw.Elapsed
End Function

Sub DoIterate(action As Action, Optional iterations As Integer = 1)
    For i = 0 To iterations - 1
        action()
    Next
End Sub

结束模块

【问题讨论】:

  • 在您的测试中,异步 IO 的速度快。这让我怀疑这些测试。读取一个大文件的速度突然提高了 20 倍,你不会怀疑吗?
  • 您可能想在阅读斯蒂芬的回答后再试一次——这不是整个异步 IO 的计时...
  • 您可能也应该将测试循环设为异步方法,并且每次都等待结果。在 Main 方法中,您需要在该异步方法上 .Wait() (但只有一个等待整个应用程序,而不是每次迭代!)。
  • 即使没有测量,我的假设很明显,异步 I/O 总是比同步 I/O 慢一点,这是错误的。见Using SqlDataReader’s new async methods in .Net 4.5
  • 我修复了我的计时循环并添加了现在合理的新测量。现在异步 I/O 通常比同步慢一些。不过也有豁免。例如:在我的示例中使用 ADO.NET ExecuteReaderAsync 但不是 ReadAsync。

标签: .net asynchronous io


【解决方案1】:

取决于您对较慢的定义:-)

异步 ​​I/O 可能会更慢,因为它必须在单独的执行线程中运行,这需要时间来设置 - 这当然取决于实现。即使线程已经为异步 I/O 运行,例如当您使用 I/O 完成端口设置它们时,线程之间传递信息所花费的时间也可能是一个因素。

然而,您可以在等待 I/O 完成的同时继续做其他事情,这意味着所有操作所花费的总时间可能会更少.

因此,虽然 I/O 可能较慢,但所有操作所花费的总时间通常会更好。

在异步似乎更快的情况下,这可能是一种非常有效的实现、信息缓存或其他十几种可能性,其中一种可能是您的基准错误。

您可能正在使用异步,因为您想做其他事情(包括可能保持 GUI 线程响应)。如果是这样,它实际上有多慢(在合理范围内) 并不重要。

【讨论】:

【解决方案2】:

您的计时循环不会等待异步操作完成,因此它只是计时启动它们需要多长时间。

使用微基准测试的“证明”任何东西quitedifficult

【讨论】:

  • 显然,这是正确的答案。解释为什么读取文件的速度似乎快了 20 倍。
  • 同意。代码可以改成reader.ReadToEndAsync().Wait();但是,我不明白计时这个有什么意义......
  • 我的计时功能似乎有问题,我会检查一下。我想感受一下同步和异步 I/O 之间的性能差异。我通常会选择最简单的实现,并且只在必要时进行调整。如果 async 几乎与 sync 一样快(甚至更快)并且不再难以使用,则默认情况下可以使用 *Async 重载,并且在必要时仅使用同步重载。
  • @PeterMeinl:性能应该足够接近,一般不会成为问题。 调用代码最好地回答同步与异步的问题——它更自然地同步还是异步?如果同步,使用同步方法;如果是异步的,请使用异步方法。大多数代码自然是异步的,但也有例外(如 ASP.NET 请求访问运行在单个服务器上的数据库后端)。
  • 关于我的问题的背景,请参阅 [StreamReader.ReadLineAsync 比 ReadLine 慢得多吗?] (social.msdn.microsoft.com/Forums/en-US/parallelextensions/…)
猜你喜欢
  • 1970-01-01
  • 2013-06-19
  • 1970-01-01
  • 2019-07-02
  • 2011-03-14
  • 1970-01-01
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多