EntityFrameworkとSQL発行の速度比較 |
「O/R マッパーであるEntityFrameworkを使用すると
もろもろのオーバーヘッドが発生して
直接SQLを発行するより遅くなるだろうなぁ」
と漠然と考えていましたが
具体的な速度比較をしていませんでした。
今更な感はありますが、比較してみました。
検証環境
VisualStudio2010 Express
WindowsForms(C#)
EntityFramework6.1
SQLServer2008 R2 Express
CPU 1.3GHz
メモリ 4GB
Windows7 Home Premium(64bit)
注意点
残念な環境でテストを行っています。
また、実装方法に間違いや偏りがあるかもしれません。
あくまでも参考程度としてください。
検証方法
3階層のテーブルを連結して検索します。
Parents
>Children
>GrandChildren
Parentsテーブル(100件)
Id | int |
Childrenテーブル(3 * 100件)
Id | int |
ParentID | int |
GrandChildrenテーブル(10 * 3 * 100件)
Id | int |
Name | nvarchar(max) |
ChildID | int |
Parents.Idを検索条件に指定します。
検証結果
パフォーマンス比較するには厳しい環境での検証となりますが
SQL発行と比べると平均で
EntityFrameworkは4.5倍、
Joinを使用して一括で取得するSQLを発行する場合でも2倍弱の
処理時間がかかりました。
※EntityFrameworkの初回実行時の処理時間は除外しています。
検証結果の詳細は次のとおりです。
検索条件固定 (ID=2) | EntityFramework | EntityFramework (JOIN使用) | ADO.NET |
1回目 | 14.839 | 14.119 | 0.42 |
2回目 | 0.1 | 0.05 | 0.0031 |
3回目 | 0.05 | 0.02 | 0.0031 |
4回目 | 0.04 | 0.02 | 0.0027 |
5回目 | 0.038 | 0.02 | 0.0187 |
6回目 | 0.038 | 0.02 | 0.0026 |
7回目 | 0.0398 | 0.02 | 0.017 |
8回目 | 0.0381 | 0.02 | 0.0028 |
9回目 | 0.0398 | 0.02 | 0.048 |
10回目 | 0.05 | 0.01 | 0.0027 |
検索条件可変 | EntityFramework | EntityFramework (JOIN使用) | ADO.NET |
1回目 | 14.9 | 14.85 | 0.635 |
2回目 | 0.046 | 0.026 | 0.021 |
3回目 | 0.039 | 0.023 | 0.003 |
4回目 | 0.047 | 0.018 | 0.004 |
5回目 | 0.039 | 0.021 | 0.002 |
6回目 | 0.052 | 0.0189 | 0.05 |
7回目 | 0.038 | 0.136 | 0.003 |
8回目 | 0.088 | 0.0184 | 0.063 |
9回目 | 0.37 | 0.024 | 0.004 |
10回目 | 0.0387 | 0.018 | 0.012 |
検証コード
検証に使ったソースコードは下記のとおりです。
○EntityFramework用のクラス
/// <summary>
/// 親エンティティ
/// </summary>
public class Parent
{
public Parent()
{
this.Children = new HashSet<Child>();
}
[Key]
public int Id { set; get; }
public virtual ICollection<Child> Children { set; get; }
}
/// <summary>
/// 子エンティティ
/// </summary>
public class Child
{
public Child()
{
this.GrandChildren = new HashSet<GrandChild>();
}
[Key]
public int Id { set; get; }
[ForeignKey("Parent")]
public int ParentID { get; set; }
public virtual Parent Parent { get; set; }
public virtual ICollection<GrandChild> GrandChildren { set; get; }
}
/// <summary>
/// 孫エンティティ
/// </summary>
public class GrandChild
{
[Key]
public int Id { set; get; }
public string Name { set; get; }
[ForeignKey("Child")]
public int ChildID { get; set; }
public virtual Child Child { get; set; }
}
/// <summary>
/// データコンテキスト
/// </summary>
public class DataContext : DbContext
{
public DataContext()
{
this.Database.Initialize(false);
}
public DbSet<Parent> Prarents { set; get; }
public DbSet<Child> Children { set; get; }
public DbSet<GrandChild> GrandChildren { set; get; }
}
○画面表示用クラス
/// <summary>
/// 画面表示用クラス
/// </summary>
public class ViewModel
{
public int ParentId { set; get; }
public int ChildId { set; get; }
public int GrandChildId { set; get; }
public string Name { set; get; }
}
○テストデータ作成処理
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DataContext>());
using (var context = new DataContext())
{
for (var parentIndex = 0; parentIndex < 100; parentIndex++)
{
var parent = new Parent();
for (var childtIndex = 0; childtIndex < 3; childtIndex++)
{
var child = new Child();
for (var grandChildtIndex = 0; grandChildtIndex < 10; grandChildtIndex++)
{
var grandChildt = new GrandChild();
grandChildt.Name = "gc" + grandChildtIndex.ToString();
child.GrandChildren.Add(grandChildt);
}
parent.Children.Add(child);
}
context.Prarents.Add(parent);
}
context.SaveChanges();
}
○SQL発行処理
var id = int.Parse(this.txtID.Text);
var viewModels = new List<ViewModel>();
var sw = new Stopwatch();
sw.Start();
{
var connectionString =
ConfigurationManager.ConnectionStrings["sqlserver"].ConnectionString;
var sql =
@"select
Parents.Id as ParentId,
Children.Id as ChildId,
GrandChilds.Id as GrandChildId,
GrandChilds.Name
from Parents
left join Children on Parents.Id = Children.ParentID
left join GrandChilds on Children.Id = GrandChilds.ChildID
where Parents.Id=@id";
var adapter = new SqlDataAdapter(sql, connectionString);
adapter.SelectCommand.Parameters.Add(new SqlParameter("@id", id));
var dataTable = new DataTable();
adapter.Fill(dataTable);
foreach (DataRow row in dataTable.Rows)
{
var viewModel = new ViewModel();
viewModel.ParentId = (int)row["ParentId"];
viewModel.ChildId = (int)row["ChildId"];
viewModel.GrandChildId = (int)row["GrandChildId"];
viewModel.Name = row["Name"].ToString();
viewModels.Add(viewModel);
}
}
sw.Stop();
○EntityFramework
var id = int.Parse(this.txtID.Text);
var viewModels = new List<ViewModel>();
var sw = new Stopwatch();
sw.Start();
using (var context = new DataContext())
{
var parents = context.Prarents.Where(parent => parent.Id == id);
foreach (var parent in parents)
{
foreach (var child in parent.Children)
{
foreach (var grandChild in child.GrandChildren)
{
var viewModel = new ViewModel();
viewModel.ParentId = parent.Id;
viewModel.ChildId = child.Id;
viewModel.GrandChildId = grandChild.Id;
viewModel.Name = grandChild.Name;
viewModels.Add(viewModel);
}
}
}
}
sw.Stop();
○EntityFramework(Join使用)
var id = int.Parse(this.txtID.Text);
var viewModels = new List<ViewModel>();
var sw = new Stopwatch();
sw.Start();
using (var context = new DataContext())
{
var joinQuery = context.Prarents.Where(p=>p.Id==id)
.Join(context.Children, p => p.Id, c => c.ParentID, (p, c) =>
new
{
PrentId = p.Id,
ChildId = c.Id
})
.Join(context.GrandChildren, pc => pc.ChildId, gc => gc.ChildID, (pc, gc) =>
new
{
pc.PrentId,
pc.ChildId,
GrandChildId = gc.Id,
gc.Name
});
foreach (var item in joinQuery)
{
var viewModel = new ViewModel();
viewModel.ParentId = item.PrentId;
viewModel.ChildId = item.ChildId;
viewModel.GrandChildId = item.GrandChildId;
viewModel.Name = item.Name;
viewModels.Add(viewModel);
}
}
sw.Stop();
処理速度だけで言えば、SQL発行よりも遅いのですが
LINQを駆使することで、同等の速度とも行かなくても
実用に耐えられるパフォーマンスを得られることができるようです。
参考になれば幸いです。
よかったらクリックしてください。
にほんブログ村