C# Linq到实体表达式/Func之间的性能差异
我刚刚测试了一个简单的查询,我正在以不同的方式访问它,但是每个查询的速度最多可以相差2秒。我希望有人能澄清为什么会这样。我的项目还处于非常早期的阶段,所以我想我应该确保在它变得太大之前我做得很好 诚然,我的测试风格并不完美,但我认为这已经足够好了 我使用的是一个通用存储库和UnitofWork,在这个C# Linq到实体表达式/Func之间的性能差异,c#,performance,linq,entity-framework,C#,Performance,Linq,Entity Framework,我刚刚测试了一个简单的查询,我正在以不同的方式访问它,但是每个查询的速度最多可以相差2秒。我希望有人能澄清为什么会这样。我的项目还处于非常早期的阶段,所以我想我应该确保在它变得太大之前我做得很好 诚然,我的测试风格并不完美,但我认为这已经足够好了 我使用的是一个通用存储库和UnitofWork,在这个while语句中,我点击了DB(本地机器上的sqlexpress)10000次。该表只有64条记录。测试在发布模式下运行 [TestMethod] public void MyTestMethod(
while
语句中,我点击了DB(本地机器上的sqlexpress)10000次。该表只有64条记录。测试在发布模式下运行
[TestMethod]
public void MyTestMethod()
{
using (var u = new UnitOfWork())
{
TestA(u);
TestB(u);
}
}
测试(功能):
Single()
func(IEnumerable)
withExpression
参数是IQueryable
上的扩展方法,由查询提供程序使用,如LINQ to实体。您提供的表达式树将转换为适当的SQL查询,并将其发送到DB,只将必要的行返回到应用程序
withFunc
参数是对IEnumerable
的一种扩展方法,LINQ使用它来处理对象,这意味着数据库中的所有记录都将被提取到应用程序内存中,然后元素将作为内存中的查询进行搜索,这是一种线性搜索
您肯定应该使用IQueryable
中的一个,因为它将更加高效(因为数据库已优化以执行查询)。withExpression
参数是IQueryable
上的一个扩展方法,由查询提供程序使用,如LINQ to实体。您提供的表达式树将转换为适当的SQL查询,并将其发送到DB,只将必要的行返回到应用程序
withFunc
参数是对IEnumerable
的一种扩展方法,LINQ使用它来处理对象,这意味着数据库中的所有记录都将被提取到应用程序内存中,然后元素将作为内存中的查询进行搜索,这是一种线性搜索
您肯定应该使用
IQueryable
中的一个,因为它将更有效(因为数据库已优化以执行查询)。我将列出一些测试,您可能希望帮助您缩小操作之间的差异
检查实际的SQL代码
打开查询的调试日志或在SSE日志上检查它。这一点很重要,因为EF引擎应该优化语句,您可以看到真正发送到DB的内容
正如您所说的,First
操作应该更快,因为有优化的SQL操作符。单个
应该较慢,因为它必须验证所有的值,并且会根据行的数量进行缩放
使用数据库上的真实SQL进行参考测试
一旦有了真正的SQL,您还可以直接检查数据库上经过的时间差。在DB上执行相同的C#测试,可能是一个Sotred过程,看看会发生什么
尝试内置LINQ进行比较
我不知道你是否已经做了测试,但是试着使用本地LINQ进行比较
我在这里使用LINQ做了很多测试,并且您提供的两个语句之间没有差异,所以它实际上可能是表达式。(我使用了SS CE btw)
另外,为了说明这一点,请remmember为涉及繁重操作的列创建索引;)
EF 6.1现在内置了此功能
[Index]
public String MyProperty{ get; set; }
让我知道它是否有用。我将列出一些测试,您可能希望帮助您缩小操作之间的差异 检查实际的SQL代码 打开查询的调试日志或在SSE日志上检查它。这一点很重要,因为EF引擎应该优化语句,您可以看到真正发送到DB的内容 正如您所说的,
First
操作应该更快,因为有优化的SQL操作符。单个
应该较慢,因为它必须验证所有的值,并且会根据行的数量进行缩放
使用数据库上的真实SQL进行参考测试
一旦有了真正的SQL,您还可以直接检查数据库上经过的时间差。在DB上执行相同的C#测试,可能是一个Sotred过程,看看会发生什么
尝试内置LINQ进行比较
我不知道你是否已经做了测试,但是试着使用本地LINQ进行比较
我在这里使用LINQ做了很多测试,并且您提供的两个语句之间没有差异,所以它实际上可能是表达式。(我使用了SS CE btw)
另外,为了说明这一点,请remmember为涉及繁重操作的列创建索引;)
EF 6.1现在内置了此功能
[Index]
public String MyProperty{ get; set; }
让我知道它是否有用。这不是答案,只是为了确保测试结果更可靠 尝试这样编写测试:
public long TestA()
{
using (var u = new UnitOfWork())
{
var s = Stopwatch.StartNew();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
return s.ElapsedMilliseconds;
}
}
public int TestA()
{
var gc0 = GC.CollectionCount(0);
using (var u = new UnitOfWork())
{
var s = Stopwatch.StartNew();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
}
return GC.CollectionCount(0) - gc0;
}
现在你的结果可能会更准确。现在让我们知道时间
现在,尝试如下更改您的测试:
public long TestA()
{
using (var u = new UnitOfWork())
{
var s = Stopwatch.StartNew();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
return s.ElapsedMilliseconds;
}
}
public int TestA()
{
var gc0 = GC.CollectionCount(0);
using (var u = new UnitOfWork())
{
var s = Stopwatch.StartNew();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
}
return GC.CollectionCount(0) - gc0;
}
public int TestA()
{
var gc0=GC.CollectionCount(0);
使用(var u=new UnitOfWork())
{
var s=Stopwatch.StartNew();
var x=0;
var repo=u.Repository();
var code=“ABCD”.First().ToString();
而(x<10000)
{
var testCase=repo.Single(w=>w.Code==Code&&w.CodeOrder==0);
x++;
}
s、 停止();
}
返回GC.CollectionCount(0)-gc0;
}
这应该确定执行了多少次第0代垃圾回收。这可能表明性能问题与测试有关,而与SQL无关
public long TestA()
{
using (var u = new UnitOfWork())
{
var s = Stopwatch.StartNew();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
return s.ElapsedMilliseconds;
}
}
[TestMethod]
public void MyTestMethod()
{
var dummyA = TestA();
var dummyB = TestB();
var realA = 0L;
var realB = 0L;
for (var i = 0; i < 10; i++)
{
realA += TestA();
realB += TestB();
}
Console.WriteLine("TESTA: " + realA.ToString());
Console.WriteLine("TESTB: " + realA.ToString());
}
public int TestA()
{
var gc0 = GC.CollectionCount(0);
using (var u = new UnitOfWork())
{
var s = Stopwatch.StartNew();
var x = 0;
var repo = u.Repository<MyEntity>();
var code = "ABCD".First().ToString();
while (x < 10000)
{
var testCase = repo.Single(w => w.Code == code && w.CodeOrder == 0).Name;
x++;
}
s.Stop();
}
return GC.CollectionCount(0) - gc0;
}