2009/05/05

Streamクラスの計測

このところバイナリデータ あーんど C#いじりにハマりつつあるじょにです。

FileStreamクラスを使って、ぐるぐるバイナリ状態でファイルのI/Oをやっていて、体感として

「ファイルの読み込み・書き込みについてだけ考えたら、TextReaderクラスやTextWriterクラスよりもFileStreamを使った方が早いような気がする…」

と思ってちょっと調べてみると、StreamReader/StreamWriterクラスは、byteデータを扱うストリーム・オブジェクトクトをラップしているクラスでした。ああ、やっぱり。(;´Д`)

ということは、「FileStreamを使ってバイナリ状態のままデータを扱った方が、StreamReader/StreamWriterクラスを使うより、処理は早い(≒Text系のストリームでは、I/Oは指定した文字コードでの処理、内部処理はUnicodeのハズ。いちいち文字コードの変換をかけている分、コストがかかっているのではないか?)」──という推論の元に実験開始!

読み込み/書き出しのクラスをセットにして、それぞれ

  1. StreamReader/StreamWriterクラス
  2. FileStreamのReadByte/WriteByteメソッド
  3. FileStreamのRead/Writeメソッド
  4. System.IO.File.Copyメソッド

で計測してみました。

4.のCopyメソッドについては、当然、バイナリでコピーをやっていると思うので、その際の参考値。予測としては多分一番早い。

コピー元の実験材料は郵便局の郵便番号データをちょちょいと加工して1行を1002バイト(改行キー含むバイト数)、1ファイル全体で117MBくらいになったものです。で、FileStreamのReadクラスやWriteクラスのMaxのバッファは1002にしたので、StreamReader/StreamWriterクラスとI/Oの回数は同一のハズ。メモリの空き容量で差がでてこないように、直前でGC.Colloect();いれてマス。


結果は

  1. 08.5791859
  2. 11.1155169
  3. 05.7603782
  4. 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 件のコメント: