これは、私が掘り下げてきた個人的なプロジェクトです。基本的に、StreamReader を使用してテキスト ファイル (20 MB から約 1 GB まで) を解析します。パフォーマンスはかなりしっかりしていますが、それでも... バイナリで解析するとどうなるか知りたくてうずうずしています。誤解しないでください。私は時期尚早に最適化しているわけではありません。私は間違いなく、「見る」ためだけに意図的にマイクロ最適化しています。
そのため、バイト配列を使用してテキスト ファイルを読み込んでいます。調べてみると、新しい行は (Windows) 標準の CR/LF または CR または LF になる可能性があります...かなり面倒です。CR で Array.IndexOf を使用して、LF をスキップできるようにしたいと考えていました。代わりに、IndexOf と非常によく似たコードを書いていますが、いずれかをチェックし、必要に応じて配列を返しています。
要点: IndexOf と非常によく似たコードを使用しても、私のコードは依然として非常に遅くなります。800MB のファイルを使用して全体像を把握するには、次のようにします。
- IndexOf を使用して CR を探す: ~320mb/s
- StreamReader と ReadLine の使用: ~180mb/s
- for ループ複製 IndexOf: ~150mb/s
for ループ (~150mb/s) を含むコードは次のとおりです。
IEnumerator<byte[]> IEnumerable<byte[]>.GetEnumerator() {
using(FileStream fs = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, _bufferSize)) {
byte[] buffer = new byte[_bufferSize];
int bytesRead;
int overflowCount = 0;
while((bytesRead = fs.Read(buffer, overflowCount, buffer.Length - overflowCount)) > 0) {
int bufferLength = bytesRead + overflowCount;
int lastPos = 0;
for(int i = 0; i < bufferLength; i++) {
if(buffer[i] == 13 || buffer[i] == 10) {
int length = i - lastPos;
if(length > 0) {
byte[] line = new byte[length];
Array.Copy(buffer, lastPos, line, 0, length);
yield return line;
}
lastPos = i + 1;
}
}
if(lastPos > 0) {
overflowCount = bufferLength - lastPos;
Array.Copy(buffer, lastPos, buffer, 0, overflowCount);
}
}
}
}
これはより高速なコード ブロックです (~320mb/s):
while((bytesRead = fs.Read(buffer, overflowCount, buffer.Length - overflowCount)) > 0) {
int bufferLength = bytesRead + overflowCount;
int pos = 0;
int lastPos = 0;
while(pos < bufferLength && (pos = Array.IndexOf<byte>(buffer, 13, pos)) != -1) {
int length = pos - lastPos;
if(length > 0) {
byte[] line = new byte[length];
Array.Copy(buffer, lastPos, line, 0, length);
yield return line;
}
if(pos < bufferLength - 1 && buffer[pos + 1] == 10)
pos++;
lastPos = ++pos;
}
if(lastPos > 0) {
overflowCount = bufferLength - lastPos;
Array.Copy(buffer, lastPos, buffer, 0, overflowCount);
}
}
(いいえ、本番環境には対応していません。特定のケースでは爆発します。私はそれらのほとんどを無視するために 128kb サイズのバッファを使用しています。)
だから私の大きな質問は...なぜ Array.IndexOf はそれほど速く動作するのですか? 本質的に同じで、配列を歩く for ループです。mscorlib コードの実行方法について何かありますか? 上記のコードを実際に IndexOf を複製するように変更し、CR だけを探してから、IndexOf を使用する場合のように LF をスキップしても役に立ちません。エラー...私はさまざまな順列を経験してきましたが、おそらく私が見逃している明らかなバグがあるほど遅いですか?
ところで、ReadLine を調べたところ、if ブロックではなく switch ブロックを使用していることに気付きました...似たようなことをすると、奇妙なことに、パフォーマンスが約 15 mb/s 向上します。それはまた別の質問です (なぜ switch は if よりも速いのですか?) しかし、私はそれを見ていたことを指摘したいと思いました。
また、VS の外部でリリース ビルドをテストしているため、デバッグは行われません。