我们目前正在尝试优化实体框架查询的性能。特别是,我们寻找降低CPU使用率的方法。
使用dotTrace,我们分析了执行不同查询时CPU时间最多的成本。请参阅下面的快照:
这个快照来自一个相当简单的查询,但它仍然显示哪个是最耗时的操作:GetExecutionPlan()。进一步深入研究,可以看出,ComputeHashValue()方法使用了很多时间,该方法以递归方式为表达式树中的所有节点调用。
实体框架将遍历表达式树中的节点 创建一个哈希,它成为用于将其置于查询中的键 高速缓存中。
因此,似乎哈希值仅用作查询缓存的键。由于我们在查询中使用IEnumerable.Contains(),因此EF不会对它们进行处理(请参阅this MSDN article (chapters 3.2 & 4.1)。因此,我们禁用了查询计划缓存,如下所示:
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
var objectSet = objectContext.CreateObjectSet<Customer>();
objectSet.EnablePlanCaching = false;
// use objectSet for queries..
我们希望不再调用ComputeHashValue()。但是,dotTrace显示的Call Tree没有变化,性能与启用查询计划缓存的效果相同。
在禁用查询计划缓存时,是否还需要ComputeHashValue()?
对于我们更复杂的查询,对ComputeHashValue()的所有调用占用查询执行所需的整个CPU时间的70%,因此避免这些调用(如果不需要它们)将大大影响我们的性能。
答案 0 :(得分:0)
不幸的是,这并不是实体框架的实施方式。我看了一下源代码,我的理解是因为它正在编译ExecutionPlan,它还会计算它的HashValue。
这是因为如果启用EnablePlanCaching
并且找不到缓存查询,则可以根据此ComputedValue将其添加到缓存管理器。
以下是处理此逻辑的类的链接: EntitySqlQueryState