efcore: Slow compilation when using many .ThenInclude()'s
In EFCore3 Preview 7 onwards, there is a significant slow down when I have a query with many .ThenInclude()s, e.g.
new WhyYouSoSlowDb(options)
.Blog
.Include(x => x.Posts)
.ThenInclude(x => x.PostInstances)
.ThenInclude(x => x.Comments)
.ThenInclude(x => x.AcquiredComments)
.ThenInclude(x => x.Tag_AcquiredComments)
.ThenInclude(x => x.Tag)
.FirstOrDefault();
The above takes 20 seconds to run on an empty database. On EFCore3 Preview 5, it takes 1.5 seconds to run. (Preview 6 throws an exception.)
Steps to reproduce
Repo available here. Note that some amount of complexity is required to reproduce this issue - if you delete the 5 unreferenced properties from AcquiredCommentEntity, it will take 10 seconds to run.
Further technical details
EF Core version: EFCore3 Preview 7 and 8 Database Provider: Microsoft.EntityFrameworkCore.SqlServer Operating system: Windows 10 IDE: Visual Studio 2019 Preview
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 30 (19 by maintainers)
Commits related to this issue
- SelectExpression.VisitChildren optimizations Do not allocate and copy the different components if visitation hasn't changed anything. Affects #17455 — committed to dotnet/efcore by roji 5 years ago
- SelectExpression.VisitChildren optimizations Do not allocate and copy the different components if visitation hasn't changed anything. Affects #17455 — committed to dotnet/efcore by roji 5 years ago
- SelectExpression.VisitChildren optimizations Do not allocate and copy the different components if visitation hasn't changed anything. Affects #17455 — committed to dotnet/efcore by roji 5 years ago
thanks a lot @roji , I solved my problem and now I know how to better approach queries with multiple join in sql with Ef
@roji Thanks for the clarification. I noticed the compilation is faster (my integration tests and app looked hung before the query actually fired prior to your fix). I had a feeling in the back of my mind this was related to the significant change you mentioned, but I wasn’t entirely sure yet if this issue’s resolution would help.
I think you are correct though, I noticed the SQL being emitted by EF with 2.2.6 is being broken up into small queries versus 3.0 where it’s one gargantuan query of joins.
Will this be fixed for 3.0 release? Because I run projects that do 20-30 includes and if that is NOT fixed then basically the performance regression makes this update a total no go. I can not move queries from significantly sub 1 second EFCore handling to MINUTES.
Also re SQL: not meeting the bar is run - up to the point people actually have to DEBUG complex expression trees and the SQL looks like a nightmare compared to the clean SQL that was usus earlier.
When both of them points to same instance, is the case when SelectExpression did not re-generate. Sure, it is not that bug and it would still cause performance issue. But the only way you can keep same instance after re-generation is not visit the same SelectExpression multiple times, (no matter where it appears so it can expand more than projection too). And that solves the perf issue already. #17337 already fixes perf issue and I don’t see a reason to track it separately.
Few observations:
Preview8+
The 2nd query is 7x time slower in SqlServer. (according to analyze plan).
My belief is #15892 will get us as close as we can get. Apart from that, there is no extra work we are doing which is not by design.