我们目前正在尝试优化 Entity Framework 查询的性能。特别是,我们寻找减少 CPU 使用率的方法。
使用 dotTrace,我们分析了执行不同查询时消耗最多 CPU 时间的部分。请参阅下面的快照:
这个快照来自一个相当简单的查询,但它仍然显示了最耗时的操作:GetExecutionPlan()。更深入地研究这一点,可以看出在 ComputeHashValue() 方法中使用了很多时间,该方法为表达式树中的所有节点递归调用。
这篇博文指出
实体框架将遍历表达式树中的节点并创建一个散列,该散列成为用于将其放置在查询缓存中的键。
因此,哈希值似乎仅用作查询缓存的键。由于我们在查询中使用 IEnumerable.Contains(),EF 不会对它们进行缓存(请参阅此 MSDN 文章(第 3.2 和 4.1 章)。因此,我们禁用了查询计划缓存,如下所示:
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var objectSet = objectContext.CreateObjectSet<Customer>();
objectSet.EnablePlanCaching = false;
// use objectSet for queries..
我们希望不再调用 ComputeHashValue()。但是,dotTrace 显示的调用树没有变化,性能与启用查询计划缓存时相同。
禁用查询计划缓存时仍然需要 ComputeHashValue() 是否有原因?
对于我们更复杂的查询,对 ComputeHashValue() 的所有调用会占用查询执行所需的全部 CPU 时间的 70%,因此避免这些调用(如果不需要它们)会极大地影响我们的性能。