DataSet vs DataReader
某掲示板で.NET2.0のDataSet話題が少しだけ上がったので、気になって調べました。
DataSetとDataReaderはそれぞれ長所と短所があると思いますが、パフォーマンスってことでちょいといろいろやってみました。
テスト方法
テストに使用するデータベース
- SQL Server 2000 Developer Edition
- テーブルのフィールドは4つ(int(PK), varchar(50), decimal, char(10) )
- 総レコード数は2万
測定する対象オブジェクト/メソッド
- SqlDataAdapterオブジェクトの生成からFillメソッドが完了するまでの時間。
using(SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
SqlCommand command = con.CreateCommand();
command.CommandText = sqlQuery;
// 測定開始
SqlDataAdapter adapt = new SqlDataAdapter(command);
DataSet ds = new DataSet();
adapt.Fill(ds);
// 測定終了
}
- SqlCommand.ExecuteReaderメソッドからSqlDataReader.Closeメソッド完了までの時間。
using(SqlConnection con = new SqlConnection(connectionString))
{
con.Open();
SqlCommand command = con.CreateCommand();
command.CommandText = sqlQuery;
// 測定開始
SqlDataReader reader = command.ExecuteReader();
while(reader.Read())
{
}
reader.Close();
// 測定終了
}
測定方法
- 測定をそれぞれ7回行い、最大値と最小値を除く5回の平均を取得する。
- 測定する時間はDateTime.Ticksを使用する。1Ticksは100ナノ秒でTicksの1000万の位の十進数が秒に相当します(文献(1)のこの回りくどい表現は何なんでしょう…)。
測定環境
- DBサーバ
CPU PentiumM 1.8GHz
Memory 1GB
- クライアント
CPU Pentium4 2.66GHz (L1キャッシュとL2キャッシュはOFF)
Memory 1GB
クライアントの方がスペックがいいように見えますが、実はL1とL2キャッシュを切っているので、パフォーマンスは激悪です。(立ち上げるのに30分かかった(;´Д`)
L1とL2キャッシュを切った理由は簡単で、普通のPCでDataReaderなりDataAdapterを使用するなりした場合、高速過ぎて下記のSQLの実行時間t3が全体の大きな部分を占めてしまうことになってしまうのです。ぶっちゃけSQLの実行時間t3を短くできる手段は持ち合わせていないので、逆にt2の部分を相対的にでかくしてやろうってことにしただけです。
測定できる時間は下図のt1部分なので、t2>>t3となればt1≒t2となるはずなのです。
ということで、計測を行った結果が以下の通り。
結果 .NET 1.1
レコード数 |
DataReader |
DataSet |
100 |
156250 |
625000 |
200 |
312500 |
1281250 |
300 |
468750 |
2000000 |
400 |
531250 |
2500000 |
500 |
656250 |
3156250 |
大体5倍程度の差が出ていますね。ネットワークを監視してデータベースとクライアント間のネットワークを監視できればよかったんですが、如何せん劣悪なPCでEtherealを起動するわけにはいきません。ルータに監視用のインタフェースは無いし、バカハブも手元にはねっす。
とりあえず、.NET1.1と.NET2.0の(定性的な)比較はできますので、.NET2.0の方の計測もやってみます。
結果 .NET2.0
レコード数 |
DataReader |
DataSet |
100 |
156250 |
937500 |
200 |
281250 |
1843750 |
300 |
375000 |
2781250 |
400 |
500000 |
3750000 |
500 |
687500 |
4750000 |
差が7~8倍に増えてますね…DataReaderの結果が.NET 1.1と変わらないことを考慮すると、DataAdapterかDataSetが遅くなったんでしょう。かなりざっくりとした測定をしているので、あまりはっきりしたことは言えませんが、もしかしたらDataTableの機能を充実させた分パフォーマンスが落ちたのかもしれません。
あとは、.NET2.0ではDataReaderをDataTableへと変換することができるようになったので、DataReaderで読み込んだ後にDataTable.Loadを使用しDataReaderをDataTableに変換した結果も見てみたいと思います。
DataTable dt = new DataTable();
dt.Load(reader);
結果 .NET2.0 DataReader2DataTable
レコード数 |
DataReader2DataTable |
DataSet |
100 |
1968750 |
937500 |
200 |
3562500 |
1843750 |
300 |
4500000 |
2781250 |
400 |
6031250 |
3750000 |
500 |
7250000 |
4750000 |
うむ。遅い!DataSetを直接使用するよりも遅いですね。
DataSet vs DataReader グラフ
まとめ
文献(1)の様に30倍も差がある結果はでませんでしたが、やっぱり速度差はそれなりにあるみたいです。そんでもって.NET2.0になってちょびっと遅くなったと。
とはいえ、余程レコード数が多くならない限り処理に1秒以上かかることはあまりないですし、文献(2)や文献(3)で述べられてるように、開発効率などはDataSetを使用したほうがサクっとできる場合があります(Genericの登場でArrayListが大活躍するかもしれませんが…)。N階層アーキテクチャを採用した場合、各層を伝播するオブジェクトとしてDataReaderを使用するのは気持ち悪いし、そうするとO/Rマッパー、もしくはそれに近い処理が必要となってしまいます。
この辺はプロジェクトの規模や方針に合わせてやっていくしかなさそうですが、パフォーマンスを考えるうえではDataReaderい軍配が上がりそうです。
参考文献
(1) A Speed Freak's Guide to Retrieving Data in ADO.NET
(2) Why I Don't Use DataSets in My ASP.NET Applications
(3) More On Why I Don't Use DataSets in My ASP.NET Applications