2019/03/28 コメントでご指摘いただいた部分を再計測、修正
お久しぶりです、エンジニアのMasashiです。
今回は特定のディレクトリ内に含まれているファイルの一覧を取得する際の速度比較について検証を行ってみました。
C#でファイル検索を行う際は、Directory.GetFilesメソッド、.NET Framework 4.0以降であれば
Directory.EnumerateFilesメソッドのどちらかを利用することが多いかと思います。
今回Directory.GetFilesメソッドとDirectory.EnumerateFilesメソッドではファイルの一覧を取得する際に
どの程度速度差が生まれるのかを検証してみたいと思います。
測定までの流れ
フォルダ内のファイルをサブフォルダまで含んだ状態で検索した結果を2つのメソッドで測定します。
検索のオプションにtxt形式のファイルを検索するように指定したものと検索ファイル対象に指定がないものの
2種類を用意しオプションの違いで速度差が生まれるかも測定してみます。
測定環境
- Visual Studio 2015
対象データ
- フォルダ直下ファイル数:10,000
- サブフォルダ数:1,000
- サブフォルダ内のファイル数:1,000
- 総ファイル数:1,010,000
測定対象
- Directory.GetFilesメソッド、オプションにtxt形式を指定
- Directory.GetFilesメソッド、オプション指定なし
- Directory.EnumerateFilesメソッド、オプションにtxt形式を指定
- Directory.EnumerateFilesメソッド、オプションに指定なし
テストコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; namespace FileListSpeedComparison { internal class Program { // 文字コード private const string Encode = "shift_jis"; // 格納フォルダパス private const string FolderPass = @"D:\test"; // 拡張子 private const string Extension = ".txt"; // テストデータ件数 private const int DataCount = 10000; // テストデータ件数(サブフォルダ数) private const int FolderCount = 1000; // テストデータ件数(サブフォルダ内ファイル数) private const int FileCountInFolder = 1000; private static void Main() { MakeFiles(); for (var i = 0; i < 10; i++) { Console.WriteLine("----------------"); DirectoryInfoExtensionSpeed(); DirectoryInfoAllExtensionSpeed(); EnumerateFilesExtensionSpeed(); EnumerateFilesAllExtensionSpeed(); Console.WriteLine("----------------"); } Console.WriteLine("続行するには何かキーを押してください:"); Console.ReadKey(); } // 検索対象に.txtを指定して検索(GetFilesを使用) private static void DirectoryInfoExtensionSpeed() { var result = new List<int>(); var stopwatch = new Stopwatch(); stopwatch.Start(); var di = new DirectoryInfo(FolderPass); var files = di.GetFiles("*.txt", SearchOption.AllDirectories); stopwatch.Stop(); Console.WriteLine("計測時間【DirectoryInfoExtensionSpeed】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // 検索対象に指定なしで検索(GetFilesを使用) private static void DirectoryInfoAllExtensionSpeed() { var stopwatch = new Stopwatch(); stopwatch.Start(); var di = new DirectoryInfo(FolderPass); var files = di.GetFiles("*", SearchOption.AllDirectories); stopwatch.Stop(); Console.WriteLine("計測時間【DirectoryInfoAllExtensionSpeed】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // 検索対象に.txtを指定して検索(EnumerateFilesを使用) private static void EnumerateFilesExtensionSpeed() { var stopwatch = new Stopwatch(); stopwatch.Start(); var di = new DirectoryInfo(FolderPass); var files = di.EnumerateFiles("*.txt", SearchOption.AllDirectories).toList(); stopwatch.Stop(); Console.WriteLine("計測時間【EnumerateFilesExtensionSpeed】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // 検索対象に指定なしで検索(EnumerateFilesを使用) private static void EnumerateFilesAllExtensionSpeed() { var stopwatch = new Stopwatch(); stopwatch.Start(); var di = new DirectoryInfo(FolderPass); var files = di.EnumerateFiles("*", SearchOption.AllDirectories).toList(); stopwatch.Stop(); Console.WriteLine("計測時間【EnumerateFilesAllExtensionSpeed】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // テストデータ作成 private static void MakeFiles() { // 文字コードの設定 var enc = Encoding.GetEncoding(Encode); // サブフォルダの作成 for (var i = 0; i < FolderCount; i++) { var folderName = $"{i:0000}"; if (!Directory.Exists(Path.Combine(FolderPass, folderName))) { Directory.CreateDirectory(Path.Combine(FolderPass, folderName)); // サブフォルダ内のテキストファイル作成 for (var j = 0; j < FileCountInFolder; j++) { var filePath = Path.Combine(FolderPass, folderName, $"{j:0000}" + Extension); File.WriteAllText(filePath, $"{j:0000}", enc); } } } // フォルダ内のテキストファイル作成 for (var i = 0; i < DataCount; i++) { // ファイル名、書き込むデータ共に0埋め5桁 var filePath = Path.Combine(FolderPass, $"{i:00000}" + Extension); File.WriteAllText(filePath, $"{i:00000}", enc); } } } } |
測定結果
10回試行した結果の平均値が下記になっています。
計測項目 | 計測時間(ms) |
---|---|
Directory.GetFilesメソッド、オプションにtxt形式を指定 | 4725.975 |
Directory.GetFilesメソッド、オプション指定なし | 4660.758 |
Directory.EnumerateFilesメソッド、オプションにtxt形式を指定 | 3952.278 |
Directory.EnumerateFilesメソッド、オプションに指定なし | 3171.094 |
上記からDirectory.GetFilesメソッドとDirectory.EnumerateFilesメソッドでのファイル取得結果には
1秒程度ですが、速度差が生まれることが測定できました。
オプションの違いで比較した場合、オプション指定なしのほうが
ファイル一覧を取得するのに若干ですが速度が速くなることがわかります。
これは、オプションが指定されている場合、*.txtに該当する名前のファイルか、txt拡張子のファイルかを判定しているために
発生する時間が速度差として生まれていると考えられます。
まとめ
今回Directory.GetFilesメソッドとDirectory.EnumerateFilesメソッドでの
ファイル取得速度について比較を行ってみましたが、はっきりと大きな速度差を見つけることはできませんでしたが、若干Directory.EnumerateFilesが速い結果となりました。
現行の環境では、ほとんどが.NET Framework 4.0以降を使用していると思いますので、
基本的にはDirectory.EnumerateFilesメソッドを使用するのが最善だと考えられます。
またオプションの有無については微々たる差ではあるので、特に気にする必要はないかと思いますが、
1形式のファイルしかフォルダ内に存在しない時のファイル名を取得したいかつ、速度を少しでも向上させたい場合は、
オプションをワイルドカードにしてもらえれば少し速度が向上することがわかりました。
普段何気なく使用している関数ですが、意外なところに違いがあったりして計測してみると
興味深い結果が得られることがあります。
これを機にC#でいろいろな性能比較を行っていただければ幸いです。