お久しぶりです、エンジニアのMasashiです。
今回はC#を扱う上で非常によく話題になりますDataTableからのデータ検索速度について検証を行ってみました。
DataTableから該当のデータを取得する方法は様々あり、
ソースコードが書かれた年代などで書き方も様々だと思います。
そこで、DataTableからデータを検索・取得する速度について検証を行ってみたいと思います。
今回の記事は下記記事の結果を元に検証方法を決定しています。
【C#】DataTableのデータアクセス速度比較
測定までの流れ
今回測定を行うのは、DataTableに格納されているデータから該当のデータを取得する際にかかった時間になります。
DataTableには、ID・Name・RandomNoが保持されており、RandomNoが8,000以上のデータを検索します。
データの取得方法については、DataTable.Select・Linqと以前の検証記事から速度の速かったDataColumnにIndexを使用する方法、
可読性を考慮して比較的使用されていると考えられるDataColumnでのアクセスになります。
測定環境
- Visual Studio 2015
対象データ
- 列数:3(ID,Name,RandomNo)
- 行数:1,000,000
- 乱数範囲:0~10,000
- 検索値:RandomNoが8,000以上のデータ
測定対象
- DataTable.Selectで取得
- Linqで取得
- DataColumnにIndexを使用して取得
- DataColumnでアクセスして取得
テストコード
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 |
using System; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Linq; using System.Text; namespace DtGetDataSpeed { internal class Program { // 検索する値 private const int Threshold = 8000; // DataTable行数 private const int RowCount = 1000000; // 乱数:最大値 private const int MaxNumber = 10000; // DataTableカラム名 private const string Id = "ID"; private const string Name = "Name"; private const string RandomNo = "RandomNo"; private static void Main() { var dt = MakeDataTable(); for (var i = 0; i < 10; i++) { Console.WriteLine("----------------"); DtSelect(dt); DtLinq(dt); DataColumnIndex(dt); DataColumn(dt); Console.WriteLine("----------------"); } Console.WriteLine("続行するには何かキーを押してください:"); Console.ReadKey(); } // DataTable.Selectで検索 private static void DtSelect(DataTable dt) { var stopwatch = new Stopwatch(); const string expression = "8000 <= [RandomNo]"; stopwatch.Start(); var searchResult = dt.Select(expression); stopwatch.Stop(); Console.WriteLine("検索条件該当データ数:" + searchResult.Length); Console.WriteLine("計測時間【DataTableSelectSpeed】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // Linqで検索 private static void DtLinq(DataTable dt) { var stopwatch = new Stopwatch(); stopwatch.Start(); var searchResult = dt.AsEnumerable().Where(row => int.Parse(row[RandomNo].ToString()) >= Threshold) .Select(row => row).ToArray(); stopwatch.Stop(); Console.WriteLine("計測時間【DataTableLinqSpeed】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // DataColumnのみIndexを使用してアクセス private static void DataColumnIndex(DataTable dt) { var stopwatch = new Stopwatch(); stopwatch.Start(); var searchResult = new List<DataRow>(); foreach (DataRow dr in dt.Rows) { if (int.Parse(dr[2].ToString()) >= Threshold) { searchResult.Add(dr); } } stopwatch.Stop(); Console.WriteLine("計測時間【DataColumnIndex】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // DataColumnでアクセス private static void DataColumn(DataTable dt) { var stopwatch = new Stopwatch(); stopwatch.Start(); var searchResult = new List<DataRow>(); foreach (DataRow dr in dt.Rows) { if (int.Parse(dr[RandomNo].ToString()) >= Threshold) { searchResult.Add(dr); } } stopwatch.Stop(); Console.WriteLine("計測時間【DataColumn】: " + stopwatch.Elapsed.TotalMilliseconds + "ms"); } // テストデータ作成 private static DataTable MakeDataTable() { var r = new Random(); var dt = new DataTable(); dt.Columns.Add(Id, typeof(int)); dt.Columns.Add(Name, typeof(string)); dt.Columns.Add(RandomNo, typeof(int)); for (var i = 0; i < RowCount; i++) { var dr = dt.NewRow(); dr[Id] = i; dr[Name] = Name + i; dr[RandomNo] = r.Next(0, MaxNumber); dt.Rows.Add(dr); } return dt; } } } |
測定結果
10回試行した結果の平均値が下記になっています。小数点第二位以下は四捨五入しています。
計測項目 | 計測時間(ms) |
---|---|
DataTable.Selectで取得 | 2411.8 |
Linqで取得 | 332.6 |
DataColumnにIndexを使用して取得 | 292.6 |
DataColumnでアクセスして取得 | 319.2 |
上記からDataTableで該当のデータ取得にはDataColumnにIndexを使用して取得が一番速い結果になりました。
前回の記事通りの結果といえるのではないでしょうか。
DataColumにIndexを使用するパターンとDataColumnでアクセスする方法では、
以前の記事同様にIndexを使用した手法のほうが速い結果になりました。
遅いと評判をよく耳にしていたDataTable.Selectですが、比較すると非常に遅い結果になっています。
これは、ReferenceSourceを見てみるとDataRow単位でデータを取り出し比較、条件に合致する場合は別の変数に確保しておき、
最終的に返却する変数に値を移し替えるなどの処理があり結果的に時間がかかっているようです。
Linqは即時実行させてみた結果、遅くもないけど速くもない結果になりました。
まとめ
今回はDataColumnにIndexを使用して取得する方法が一番データの検索・取得は速く行えました。
Linqが使われていない際によくみられるDataTableへのIndex・Columnを使ったアクセスは、
速度に大きな差があるわけではないので可読性を考慮してColumnでのアクセスでも問題ないかと考えられます。
今回はDataTable.Selectとそれ以外の手法で速度結果の差が大きく驚きました。
引き続きいろいろな速度調査を行っていきたいと思います。