このところバイナリデータ あーんど C#いじりにハマりつつあるじょにです。
FileStreamクラスを使って、ぐるぐるバイナリ状態でファイルのI/Oをやっていて、体感として
「ファイルの読み込み・書き込みについてだけ考えたら、TextReaderクラスやTextWriterクラスよりもFileStreamを使った方が早いような気がする…」
と思ってちょっと調べてみると、StreamReader/StreamWriterクラスは、byteデータを扱うストリーム・オブジェクトクトをラップしているクラスでした。ああ、やっぱり。(;´Д`)
ということは、「FileStreamを使ってバイナリ状態のままデータを扱った方が、StreamReader/StreamWriterクラスを使うより、処理は早い(≒Text系のストリームでは、I/Oは指定した文字コードでの処理、内部処理はUnicodeのハズ。いちいち文字コードの変換をかけている分、コストがかかっているのではないか?)」──という推論の元に実験開始!
読み込み/書き出しのクラスをセットにして、それぞれ
- StreamReader/StreamWriterクラス
- FileStreamのReadByte/WriteByteメソッド
- FileStreamのRead/Writeメソッド
- System.IO.File.Copyメソッド
で計測してみました。
4.のCopyメソッドについては、当然、バイナリでコピーをやっていると思うので、その際の参考値。予測としては多分一番早い。
コピー元の実験材料は郵便局の郵便番号データをちょちょいと加工して1行を1002バイト(改行キー含むバイト数)、1ファイル全体で117MBくらいになったものです。で、FileStreamのReadクラスやWriteクラスのMaxのバッファは1002にしたので、StreamReader/StreamWriterクラスとI/Oの回数は同一のハズ。メモリの空き容量で差がでてこないように、直前でGC.Colloect();いれてマス。
結果は
- 08.5791859
- 11.1155169
- 05.7603782
- 03.8566569
という具合でした。2.の11秒はさすがに1バイトずつだとI/Oが多くてあまりよい数字はでてこない、ということになりますが。でもまぁI/Oの回数が多いわりには、意外ととStreamReader/StreamWriterクラスに迫っていますよね。最も外付けHDDやネットワーク越しに作業をしたり、ファイルサイズがも巨大であればあるほど、I/Oの問題はもっと顕著になってくるわけですけれどね。
ちなみに実験ソースはこんな感じ。
実験環境はVC#2008Express版(.Net Framework3.5)。
_InFilePath/_OutFilePathは実験クラスのメンバー変数です。
StreamReader/StreamWriterクラスの計測
//------------------------
Stopwatch sw = new Stopwatch();
sw.Reset();
sw.Start();
//------------------------
using (StreamReader _SReder = new StreamReader(_InFilePath, Encoding.GetEncoding(932)))
using (StreamWriter _SWriter = new StreamWriter(_OutFilePath, true, Encoding.GetEncoding(932)))
{
while (_SReder.Peek() != -1)
{
_SWriter.WriteLine(_SReder.ReadLine());
}
}
//------------------------
sw.Stop();
MessageBox.Show(sw.Elapsed.ToString());
//------------------------
FileStreamのReadByte/WriteByteメソッドの計測
//------------------------
Stopwatch sw = new Stopwatch();
sw.Reset();
sw.Start();
//------------------------
using (FileStream _FReder = new FileStream(_InFilePath, FileMode.Open, FileAccess.Read))
using (FileStream _FWriter = new FileStream(_OutFilePath, FileMode.Create, FileAccess.Write))
{
int _ReadByte = 0;
while (_ReadByte != -1)
{
_ReadByte = _FReder.ReadByte();
_FWriter.WriteByte((byte)_ReadByte);
}
}
//------------------------
sw.Stop();
MessageBox.Show(sw.Elapsed.ToString());
//------------------------
FileStreamのRead/Writeメソッドの計測
//------------------------
int _BufferSize = 1002;
//------------------------
Stopwatch sw = new Stopwatch();
sw.Reset();
sw.Start();
//------------------------
using (FileStream _FReder = new FileStream(_InFilePath, FileMode.Open, FileAccess.Read))
using (FileStream _FWriter = new FileStream(_OutFilePath, FileMode.Create, FileAccess.Write))
{
byte[] _ReadBuffer = new byte[_BufferSize];
int _ReadSize = _BufferSize;
while (_ReadSize != 0)
{
_ReadSize = _FReder.Read(_ReadBuffer, 0, _BufferSize);
_FWriter.Write(_ReadBuffer, 0, _ReadSize);
}
}
//------------------------
sw.Stop();
MessageBox.Show(sw.Elapsed.ToString());
//------------------------
0 件のコメント:
コメントを投稿