1

スレッドの健全性には優れていますが、非同期 I/O は常に同期 I/O よりも少し遅いと予想していました。私のテストでは、非同期 I/O が同期よりも高速な場合があることが証明されているようです。ここで何が欠けていますか?

[編集] 私の最初の測定値は間違っていました (コメントを無効にしないために削除しませんでした)。

タイミング ループを修正した場合の測定結果を次に示します。
反復ごとの ADO_DataReaderSync=4,17ms
ADO_DataReaderASyncReader per iteration=3,55ms (ExecuteReaderAsync のみ)
反復ごとの ADO_DataReaderASyncRead=11,28ms (ExecuteReaderAsync および ReadAsync)
FileIO_ReadToEndSync SmallFile per iteration=3,67ms
反復ごとの FileIO_ReadToEndAsync SmallFile=8,97ms
FileIO_ReadToEndSync LargeFile per iteration=266,34ms
FileIO_ReadToEndAsync LargeFile per iteration=322,05ms

バギー測定:

ADO_ReadSync の経過時間:00:00:00.0012249/反復 = 0,12249ms
ADO_ReadAsync 経過時間:00:00:00.0050702/反復 = 0,50702ms
ADO_DataReaderSync 経過時間:00:00:00.0090513/反復 = 0.90513ms
ADO_DataReaderASync 経過時間:00:00:00.0044125/反復 = 0,44125ms
FileIO_ReadSync LargeFile 経過時間:00:00:00.0655596/反復 = 6,55596ms
FileIO_ReadAsync LargeFile 経過時間:00:00:00.0003056/反復 = 0,03056ms
FileIO_ReadSync SmallFile 経過時間:00:00:00.0005619/反復 =0,05619ms
FileIO_ReadAsync SmallFile 経過時間: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

エンドモジュール

4

2 に答える 2

3

遅いの定義に依存します:-)

セットアップに時間がかかる別の実行スレッドで実行する必要があるため、非同期 I/O が遅くなる可能性があります。これはもちろん実装によって異なります。I/O 完了ポートを使用してスレッドを設定した場合など、非同期 I/O 用にスレッドが既に実行されている場合でも、スレッド間で情報を通信するのにかかる時間が要因になる場合があります。

ただし、 I/O が完了するのを待っている間に他のことを続けることができるということは、すべての操作にかかる合計時間が短縮される可能性があることを意味します。

そのため、I/O は遅くなる可能性がありますが、すべての操作にかかる全体的な時間は通常より長くなります。

async の方が速いと思われる場合、それは信じられないほど効率的な実装、情報のキャッシュ、またはその他の多数の可能性である可能性があります。

おそらく、何か他のことをしたいので(おそらくGUIスレッドの応答性を維持することを含む)、asyncを使用しています。もしそうなら、それがどれほど遅いかは実際には問題ではありません(理由の範囲内で)。

于 2012-09-22T12:01:22.453 に答える
2

タイミング ループは非同期操作が完了するのを待っているわけではないため、非同期操作を開始するのにかかる時間を計っているだけです。

マイクロベンチマークを使用して何かを「証明」することは非常に 困難です。

于 2012-09-22T12:29:55.823 に答える