¿Comparación de rendimiento de IEnumerable y evento de subida para cada elemento en la fuente?
Quiero leer un archivo binario grande que contenga millones de registros y quiero obtener algunos informes para los registros. yo sueloBinaryReader
para leer (que creo que tiene el mejor rendimiento en lectores) y convertir los bytes de lectura al modelo de datos. Debido al conteo de registros, pasar el modelo a la capa de informe es otro problema: prefiero usarIEnumerable
tener funcionalidades y funcionalidades de LINQ al desarrollar los informes.
Aquí está la clase de datos de muestra:
Public Class MyData
Public A1 As UInt64
Public A2 As UInt64
Public A3 As Byte
Public A4 As UInt16
Public A5 As UInt64
End Class
Utilicé este sub para crear el archivo:
Sub CreateSampleFile()
Using streamWriter As New FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Write)
For i As Integer = 1 To 1000
For j As Integer = 1 To 1000
For k = 1 To 30
Dim item As New MyData With {.A1 = i, .A2 = j, .A3 = k, .A4 = j, .A5 = i * j}
Dim bytes() As Byte = BitConverter.GetBytes(item.A1).Concat(BitConverter.GetBytes(item.A2)).Concat({item.A3}).Concat(BitConverter.GetBytes(item.A4)).Concat(BitConverter.GetBytes(item.A5)).ToArray
streamWriter.Write(bytes, 0, bytes.Length)
Next
Next
Next
End Using
End Sub
Y aquí está mi clase de lector:
Imports System.IO
Public Class FileReader
Public Const BUFFER_LENGTH As Long = 4096 * 256 * 27
Public Const MY_DATA_LENGTH As Long = 27
Private _buffer(BUFFER_LENGTH - 1) As Byte
Private _streamWriter As FileStream
Public Event OnByteRead(sender As FileReader, bytes() As Byte, index As Long)
Public Sub StartReadBinary(fileName As String)
Dim currentBufferReadCount As Long = 0
Using fileStream As New FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)
Using streamReader As New BinaryReader(fileStream)
currentBufferReadCount = streamReader.Read(Me._buffer, 0, Me._buffer.Length)
While currentBufferReadCount > 0
For i As Integer = 0 To currentBufferReadCount - 1 Step MY_DATA_LENGTH
RaiseEvent OnByteRead(Me, Me._buffer, i)
Next
currentBufferReadCount = streamReader.Read(Me._buffer, 0, Me._buffer.Length)
End While
End Using
End Using
End Sub
Public Iterator Function GetAll(fileName As String) As IEnumerable(Of MyData)
Dim currentBufferReadCount As Long = 0
Using fileStream As New FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)
Using streamReader As New BinaryReader(fileStream)
currentBufferReadCount = streamReader.Read(Me._buffer, 0, Me._buffer.Length)
While currentBufferReadCount > 0
For i As Integer = 0 To currentBufferReadCount - 1 Step MY_DATA_LENGTH
Yield GetInstance(_buffer, i)
Next
currentBufferReadCount = streamReader.Read(Me._buffer, 0, Me._buffer.Length)
End While
End Using
End Using
End Function
Public Function GetInstance(bytes() As Byte, index As Long) As MyData
Return New MyData With {.A1 = BitConverter.ToUInt64(bytes, index), .A2 = BitConverter.ToUInt64(bytes, index + 8), .A3 = bytes(index + 16), .A4 = BitConverter.ToUInt16(bytes, index + 17), .A5 = BitConverter.ToUInt64(bytes, index + 19)}
End Function
End Class
Estaba pensando en elIEnumerable
rendimiento, así que traté de usar tantoGetAll
método comoIEnumerable
y el evento de aumento para cada registro que se lee desde el archivo. Aquí está el módulo de prueba:
Imports System.IO
Module Module1
Private fileName As String = "MyData.dat"
Private readerJustTraverse As New FileReader
Private WithEvents readerWithoutInstance As New FileReader
Private WithEvents readerWithInstance As New FileReader
Private readerIEnumerable As New FileReader
Sub Main()
Dim s As New Stopwatch
s.Start()
readerJustTraverse.StartReadBinary(fileName)
s.Stop()
Console.WriteLine("Read bytes: {0}", s.ElapsedMilliseconds)
s.Restart()
readerWithoutInstance.StartReadBinary(fileName)
s.Stop()
Console.WriteLine("Read bytes, raise event: {0}", s.ElapsedMilliseconds)
s.Restart()
readerWithInstance.StartReadBinary(fileName)
s.Stop()
Console.WriteLine("Read bytes, raise event, get instance: {0}", s.ElapsedMilliseconds)
s.Restart()
For Each item In readerIenumerable.GetAll(fileName)
Next
Console.WriteLine("Read bytes, get instance, return yield: {0}", s.ElapsedMilliseconds)
s.Stop()
Console.ReadLine()
End Sub
Private Sub readerWithInstance_OnByteRead(sender As FileReader, bytes() As Byte, index As Long) Handles readerWithInstance.OnByteRead
Dim item As MyData = sender.GetInstance(bytes, index)
End Sub
Private Sub readerWithoutInstance_OnByteRead(sender As FileReader, bytes() As Byte, index As Long) Handles readerWithoutInstance.OnByteRead
'do nothing
End Sub
End Module
Lo que me pregunto es el tiempo transcurrido para cada proceso, aquí está el resultado de la prueba (probado en ASUS Ultrabook - Zenbook Core i7):
Bytes de lectura: 384 (¡sin tocar los bytes de lectura!)
Leer bytes, levantar evento: 583
Leer bytes, generar evento, obtener instancia: 3923
Leer bytes, obtener instancia, rendimiento de retorno: 4917
Muestra que leer un archivo como un byte es increíblemente rápido, y convertir bytes al modelo es lento. También aumentar el evento en lugar de obtener el resultado de IEnumerable, es un 25% más rápido.
La iteración en IEnumerable es realmente tiene este costo de rendimiento o me perdi algo?