スレッドの健全性には優れていますが、非同期 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
エンドモジュール