Mi sono imbattuto in una situazione in cui ho un file piuttosto grande di cui ho bisogno per leggere i dati binari.BinaryReader più veloce (non sicuro) in .NET
Di conseguenza, mi sono reso conto che l'implementazione BinaryReader predefinita in .NET è piuttosto lenta. Su guardando con .NET Reflector mi sono imbattuto in questo:
public virtual int ReadInt32()
{
if (this.m_isMemoryStream)
{
MemoryStream stream = this.m_stream as MemoryStream;
return stream.InternalReadInt32();
}
this.FillBuffer(4);
return (((this.m_buffer[0] | (this.m_buffer[1] << 8)) | (this.m_buffer[2] << 0x10)) | (this.m_buffer[3] << 0x18));
}
Il che mi sembra estremamente inefficiente, pensando a come i computer sono stati progettati per funzionare con valori a 32 bit in quanto la CPU a 32 bit è stato inventato.
Così ho fatto il mio (non sicuro) di classe FastBinaryReader con codice come questo, invece:
public unsafe class FastBinaryReader :IDisposable
{
private static byte[] buffer = new byte[50];
//private Stream baseStream;
public Stream BaseStream { get; private set; }
public FastBinaryReader(Stream input)
{
BaseStream = input;
}
public int ReadInt32()
{
BaseStream.Read(buffer, 0, 4);
fixed (byte* numRef = &(buffer[0]))
{
return *(((int*)numRef));
}
}
...
}
che è molto più veloce - sono riuscito a radersi 5-7 secondi dal tempo impiegato per leggere un 500 MB file, ma è ancora piuttosto lento nel complesso (29 secondi inizialmente e ~ 22 secondi ora con il mio FastBinaryReader
).
È ancora un po 'sconcertante sul perché ci voglia così tanto tempo per leggere un file relativamente piccolo. Se copio il file da un disco a un altro ci vogliono solo un paio di secondi, quindi il throughput del disco non è un problema.
Ho inoltre inline il ReadInt32, ecc chiamate, e ho finito con questo codice:
using (var br = new FastBinaryReader(new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 0x10000, FileOptions.SequentialScan)))
while (br.BaseStream.Position < br.BaseStream.Length)
{
var doc = DocumentData.Deserialize(br);
docData[doc.InternalId] = doc;
}
}
public static DocumentData Deserialize(FastBinaryReader reader)
{
byte[] buffer = new byte[4 + 4 + 8 + 4 + 4 + 1 + 4];
reader.BaseStream.Read(buffer, 0, buffer.Length);
DocumentData data = new DocumentData();
fixed (byte* numRef = &(buffer[0]))
{
data.InternalId = *((int*)&(numRef[0]));
data.b = *((int*)&(numRef[4]));
data.c = *((long*)&(numRef[8]));
data.d = *((float*)&(numRef[16]));
data.e = *((float*)&(numRef[20]));
data.f = numRef[24];
data.g = *((int*)&(numRef[25]));
}
return data;
}
Eventuali ulteriori idee su come rendere questo ancora più veloce? Stavo pensando che forse potrei usare il marshalling per mappare l'intero file direttamente nella memoria sopra una struttura personalizzata, dato che i dati sono lineari, dimensioni fisse e sequenziali.
RISOLTO: Sono giunto alla conclusione che Buffering/BufferedStream di FileStream sono difettosi. Si prega di vedere la risposta accettata e la mia risposta (con la soluzione) di seguito.
Può essere utile: http://stackoverflow.com/questions/19558435/questo-è-il-best-buffer-size-when-using-binaryreader-to-read-big-files-1gb/19837238? noredirect = 1 # 19837238 –