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クラスの計測

  1. //------------------------  
  2. Stopwatch sw = new Stopwatch();  
  3. sw.Reset();  
  4. sw.Start();  
  5. //------------------------  
  6. using (StreamReader _SReder = new StreamReader(_InFilePath, Encoding.GetEncoding(932)))  
  7. using (StreamWriter _SWriter = new StreamWriter(_OutFilePath, true, Encoding.GetEncoding(932)))  
  8. {  
  9.     while (_SReder.Peek() != -1)  
  10.     {  
  11.         _SWriter.WriteLine(_SReder.ReadLine());  
  12.     }  
  13. }  
  14. //------------------------  
  15. sw.Stop();  
  16. MessageBox.Show(sw.Elapsed.ToString());  
  17. //------------------------  

FileStreamのReadByte/WriteByteメソッドの計測

  1. //------------------------  
  2. Stopwatch sw = new Stopwatch();  
  3. sw.Reset();  
  4. sw.Start();  
  5. //------------------------  
  6. using (FileStream _FReder = new FileStream(_InFilePath, FileMode.Open, FileAccess.Read))  
  7. using (FileStream _FWriter = new FileStream(_OutFilePath, FileMode.Create, FileAccess.Write))  
  8. {  
  9.     int _ReadByte = 0;  
  10.     while (_ReadByte != -1)  
  11.     {  
  12.         _ReadByte = _FReder.ReadByte();  
  13.         _FWriter.WriteByte((byte)_ReadByte);  
  14.     }  
  15. }  
  16. //------------------------  
  17. sw.Stop();  
  18. MessageBox.Show(sw.Elapsed.ToString());  
  19. //------------------------  

FileStreamのRead/Writeメソッドの計測

  1. //------------------------  
  2. int _BufferSize = 1002;  
  3. //------------------------  
  4. Stopwatch sw = new Stopwatch();  
  5. sw.Reset();  
  6. sw.Start();  
  7. //------------------------  
  8. using (FileStream _FReder = new FileStream(_InFilePath, FileMode.Open, FileAccess.Read))  
  9. using (FileStream _FWriter = new FileStream(_OutFilePath, FileMode.Create, FileAccess.Write))  
  10. {  
  11.     byte[] _ReadBuffer = new byte[_BufferSize];  
  12.     int _ReadSize = _BufferSize;  
  13.     while (_ReadSize != 0)  
  14.     {  
  15.         _ReadSize = _FReder.Read(_ReadBuffer, 0, _BufferSize);  
  16.         _FWriter.Write(_ReadBuffer, 0, _ReadSize);  
  17.     }  
  18. }  
  19. //------------------------  
  20. sw.Stop();  
  21. MessageBox.Show(sw.Elapsed.ToString());  
  22. //------------------------  

0 件のコメント: