お久しぶりです、エンジニアのMasashiです。
今回は、ファイルの内容を読み取る際の速度について検証を行ってみます。
ファイルの内容を読み取る際に使用できる関数はいくつかあり、
取得したいデータの形(文字列または配列等)を意識して使用する関数を変えているかと思います。
下記では、データの形は気にせずにファイルの内容を読み取る際の速度に違いがあるのか検証を行ってみます。
測定までの流れ
今回測定を行うのは、ファイルに乱数を100,000行記載を行いファイルの内容を読み込むまでに要した時間になります。
比較対象としてStreamReaderを使用して一括で読み込む場合と1行ずつ読み込む場合、
FileクラスのReadAllText、ReadAllLinesを使用した場合の4つについてtxt形式とcsv形式それぞれで速度を検証しています。
測定環境
- Visual Studio 2015
対象データ
- テキストに書き込む数値の数:100,000
- テキストに書き込む数値の内容:0~99,999までの乱数
測定対象
- txt形式のファイルにStreamReader.ReadToEndを使用して一括読み込み
- txt形式のファイルにStreamReader.ReadLineを使用して1行ずつ読み込み
- txt形式のファイルにFile.ReadAllTextを使用して読み込み
- txt形式のファイルにFile.ReadAllLinesを使用して読み込み
- csv形式のファイルにStreamReader.ReadToEndを使用して一括読み込み
- csv形式のファイルにStreamReader.ReadLineを使用して1行ずつ読み込み
- csv形式のファイルにFile.ReadAllTextを使用して読み込み
- csv形式のファイルにFile.ReadAllLinesを使用して読み込み
テストコード
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
using System; using System.Diagnostics; using System.IO; using System.Text; namespace FileReadingSpeed { internal class Program { // 乱数:最大値 private const int MaxValue = 100000; // データ数 private const int DataCount = 100000; // 読み込み先のテキストファイル private const string FilePathTxt = @"C:\test\test.txt"; // 読み込み先のCSVファイル private const string FilePathCsv = @"C:\test\test.csv"; // 文字コード private static readonly Encoding Enc = Encoding.GetEncoding("shift_jis"); private static void Main() { CreateTestData(); for (var i = 0; i < 10; i++) { Console.WriteLine("----------------"); StreamReader(); ReadEachLine(); ReadAllText(); ReadAllLines(); StreamReaderCsv(); ReadEachLineCsv(); ReadAllTextCsv(); ReadAllLinesCsv(); Console.WriteLine("----------------"); } Console.WriteLine("続行するには何かキーを押してください:"); Console.ReadKey(); } // txt形式のファイルにStreamReader.ReadToEndを使用して一括読み込み private static void StreamReader() { var stopwatch = new Stopwatch(); stopwatch.Start(); var readToEnd = string.Empty; using (var sr = new StreamReader(FilePathTxt, Enc)) { readToEnd = sr.ReadToEnd(); } stopwatch.Stop(); Console.WriteLine("計測時間【StreamReader】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // txt形式のファイルにStreamReader.ReadLineを使用して1行ずつ読み込み private static void ReadEachLine() { var stopwatch = new Stopwatch(); stopwatch.Start(); var readToEnd = new StringBuilder(); using (var sr = new StreamReader(FilePathTxt, Enc)) { while (sr.Peek() > -1) { readToEnd.Append(sr.ReadLine()); } } stopwatch.Stop(); Console.WriteLine("計測時間【ReadEachLine】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // txt形式のファイルにFile.ReadAllTextを使用して読み込み private static void ReadAllText() { var stopwatch = new Stopwatch(); stopwatch.Start(); var readToEnd = File.ReadAllText(FilePathTxt, Enc); stopwatch.Stop(); Console.WriteLine("計測時間【ReadAllText】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // txt形式のファイルにFile.ReadAllLinesを使用して読み込み private static void ReadAllLines() { var stopwatch = new Stopwatch(); stopwatch.Start(); var readToEnd = File.ReadAllLines(FilePathTxt, Enc); stopwatch.Stop(); Console.WriteLine("計測時間【ReadAllLines】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // csv形式のファイルにStreamReader.ReadToEndを使用して一括読み込み private static void StreamReaderCsv() { var stopwatch = new Stopwatch(); stopwatch.Start(); var readToEnd = string.Empty; using (var sr = new StreamReader(FilePathCsv, Enc)) { readToEnd = sr.ReadToEnd(); } stopwatch.Stop(); Console.WriteLine("計測時間【StreamReaderCsv】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // csv形式のファイルにStreamReader.ReadLineを使用して1行ずつ読み込み private static void ReadEachLineCsv() { var stopwatch = new Stopwatch(); stopwatch.Start(); var readToEnd = new StringBuilder(); using (var sr = new StreamReader(FilePathCsv, Enc)) { while (sr.Peek() > -1) { readToEnd.Append(sr.ReadLine()); } } stopwatch.Stop(); Console.WriteLine("計測時間【ReadEachLineCsv】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // csv形式のファイルにFile.ReadAllTextを使用して読み込み private static void ReadAllTextCsv() { var stopwatch = new Stopwatch(); stopwatch.Start(); var readToEnd = File.ReadAllText(FilePathCsv, Enc); stopwatch.Stop(); Console.WriteLine("計測時間【ReadAllTextCsv】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // csv形式のファイルにFile.ReadAllLinesを使用して読み込み private static void ReadAllLinesCsv() { var stopwatch = new Stopwatch(); stopwatch.Start(); var readToEnd = File.ReadAllLines(FilePathCsv, Enc); stopwatch.Stop(); Console.WriteLine("計測時間【ReadAllLinesCsv】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // 数字データをテキストファイルに書き込む private static void CreateTestData() { var r = new Random(); var sb = new StringBuilder(); for (var i = 1; i <= DataCount; i++) { // 乱数を結合 sb.Append(r.Next(0, MaxValue)); sb.Append(Environment.NewLine); } //ファイル書き込み File.WriteAllText(FilePathTxt, sb.ToString(), Enc); File.WriteAllText(FilePathCsv, sb.ToString(), Enc); } } } |
ファイル読み込みの測定結果
10回試行した結果の平均値が下記になっています。小数点第2位で四捨五入しています。
計測項目 | 計測時間(ms) |
---|---|
txt形式のファイルにStreamReader.ReadToEndを使用して一括読み込み | 5.8 |
txt形式のファイルにStreamReader.ReadLineを使用して1行ずつ読み込み | 10.0 |
txt形式のファイルにFile.ReadAllTextを使用して読み込み | 3.1 |
txt形式のファイルにFile.ReadAllLinesを使用して読み込み | 11.4 |
csv形式のファイルにStreamReader.ReadToEndを使用して一括読み込み | 5.1 |
csv形式のファイルにStreamReader.ReadLineを使用して1行ずつ読み込み | 10.8 |
csv形式のファイルにFile.ReadAllTextを使用して読み込み | 4.7 |
csv形式のファイルにFile.ReadAllLinesを使用して読み込み | 11.8 |
上記の結果を比較してみるとファイル形式の違いによる速度差については見られませんでした。
次に使用している関数別にみてみると一番速い形は、File.ReadAllTextを使用したパターンになりました。
StreamReader.ReadToEndと返り値の型や処理の流れは同一だと考えていましたが、
txt形式・csv形式どちらでもFile.ReadAllTextを使用した方が速いため、速度差の原因がどこにあるのか調査してみたいと思います。
StreamReader.ReadLineを使用して1行ずつ読み込むパターンは上記のパターンと比べると1行ずつ読み込む方法のため時間がかかる結果になりました。
File.ReadAllLinesは返り値が文字列の配列形式であるため、配列形式に合わせて格納する時間が処理時間として出ていると考えられます。
まとめ
ファイル読み込みの速度では、File.ReadAllTextが一番速い結果となりました。
結果を文字列形式で受け取れれば構わない場合は、File.ReadAllTextを使用することが一番いいと考えられます。
また、File.ReadAllLinesに関しては時間を比較した場合遅いですが、結果を配列で返却してくれるため、
データの加工が行いやすいメリットがあります。
処理時間もそこまで大きな差ではないため、返り値の型に応じて使用する関数を柔軟に変えて問題ないと思います。
この記事が少しでも皆さんのお役に立てば幸いです。