【发布时间】:2016-10-08 00:08:37
【问题描述】:
这篇 MSDN 文章列出了一系列提高实体框架性能的方法:
https://msdn.microsoft.com/en-us/data/hh949853.aspx
它的一个建议(4.3)是将非映射对象的属性转换为局部变量,以便EF可以缓存其内部查询计划。
听起来是个好主意。因此,我使用一个简单的查询对其进行了测试,该查询将查询中间接属性引用的 10,000 次迭代的性能与局部变量进行了比较。像这样:
[Fact]
public void TestQueryCaching()
{
const int iterations = 1000;
var quote = new Quote();
using (var ctx = new CoreContext())
{
quote.QuoteId = ctx.Quotes.First().Id;
}
double indirect = 0;
double direct = 0;
10.Times(it =>
{
indirect += PerformCoreDbTest(iterations, "IndirectValue", (ctx, i) =>
{
var dbQuote = ctx.Quotes.First(x => x.Id == quote.QuoteId);
}).TotalSeconds;
direct += PerformCoreDbTest(iterations, "DirectValue", (ctx, i) =>
{
var quoteId = quote.QuoteId;
var dbQuote = ctx.Quotes.First(x => x.Id == quoteId);
}).TotalSeconds;
});
_logger.Debug($"Indirect seconds: {indirect:0.00}, direct seconds:{direct:0.00}");
}
protected TimeSpan PerformCoreDbTest(int iterations, string descriptor, Action<ICoreContext, int> testAction)
{
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < iterations; i++)
{
using (var ctx = new CoreContext())
{
testAction(ctx, i);
}
}
sw.Stop();
_logger.DebugFormat("{0}: Took {1} milliseconds for {2} iterations",
descriptor, sw.Elapsed.TotalMilliseconds, iterations);
return sw.Elapsed;
}
但我没有看到任何真正的性能优势。在两台不同的机器上,这些是 5 次迭代的结果:
Machine1 - Indirect seconds: 9.06, direct seconds:9.36
Machine1 - Indirect seconds: 9.98, direct seconds:9.84
Machine2 - Indirect seconds: 22.41, direct seconds:20.38
Machine2 - Indirect seconds: 17.27, direct seconds:16.93
Machine2 - Indirect seconds: 16.35, direct seconds:16.32
使用局部变量 - MSDN 文章推荐的“直接”方法 - 可能稍微快一点(4/5 倍),但不是始终如一,也不是很多。
我在测试中做错了吗?还是影响真的很小以至于没有太大区别?还是 MSDN 文章基本上是错误的,并且这种引用对象的方式对查询缓存没有任何影响?
** 2016 年 10 月 9 日编辑 ** 我将查询修改为 (a) 使其更复杂,并且 (b) 每次都传入不同的 quoteId。我怀疑后者很重要,否则查询实际上会被缓存 - 因为没有任何参数。请参阅下面@raderick 的答案。
这是更复杂的测试:
[Fact]
public void TestQueryCaching()
{
const int iterations = 1000;
List<EFQuote> quotes;
using (var ctx = new CoreContext())
{
quotes = ctx.Quotes.Take(iterations).ToList();
}
double indirect = 0;
double direct = 0;
double iqueryable = 0;
10.Times(it =>
{
indirect += PerformCoreDbTest(iterations, "IndirectValue", (ctx, i) =>
{
var quote = quotes[i];
var dbQuote = ctx.Quotes
.Include(x => x.QuoteGroup.QuoteGroupElements.Select(e => e.DefaultElement.DefaultChoices))
.Include(x => x.QuoteElements.Select(e => e.DefaultElement.DefaultChoices))
.Include(x => x.QuotePackage)
.Include(x => x.QuoteDefinition)
.Include(x => x.QuoteLines)
.First(x => x.Id == quote.Id);
}).TotalSeconds;
direct += PerformCoreDbTest(iterations, "DirectValue", (ctx, i) =>
{
var quoteId = quotes[i].Id;
var dbQuote = ctx.Quotes
.Include(x => x.QuoteGroup.QuoteGroupElements.Select(e => e.DefaultElement.DefaultChoices))
.Include(x => x.QuoteElements.Select(e => e.DefaultElement.DefaultChoices))
.Include(x => x.QuotePackage)
.Include(x => x.QuoteDefinition)
.Include(x => x.QuoteLines)
.First(x => x.Id == quoteId);
}).TotalSeconds;
iqueryable += PerformCoreDbTest(iterations, "IQueryable", (ctx, i) =>
{
var quoteId = quotes[i].Id;
var dbQuote = ctx.Quotes
.Include(x => x.QuoteGroup.QuoteGroupElements.Select(e => e.DefaultElement.DefaultChoices))
.Include(x => x.QuoteElements.Select(e => e.DefaultElement.DefaultChoices))
.Include(x => x.QuotePackage)
.Include(x => x.QuoteDefinition)
.Include(x => x.QuoteLines)
.Where(x => x.Id == quoteId).First();
}).TotalSeconds;
});
_logger.Debug($"Indirect seconds: {indirect:0.00}, direct seconds:{direct:0.00}, iqueryable seconds:{iqueryable:0.00}");
}
结果(超过 10,000 次总迭代)与上面的 MSDN 文章描述的更相似:
Indirect seconds: 141.32, direct seconds:91.95, iqueryable seconds:93.96
【问题讨论】:
标签: c# performance entity-framework entity-framework-6