-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Looping DbSet.Local is slow in EF Core compared to EF 6 #14231
Comments
Note for triage: iteration over the local entities is slow, since it is a view over the state manager. @HjalteParner A workaround that makes the behavior more like EF6 is to convert the var local = context.Blogs.Local.ToObservableCollection();
foreach (var blog in local)
{
} This is essentially what EF6 does. It has the downside that the creating the |
Thanks you very much @ajcvickers for your reply. Looping the LocalView through the ObservableCollection definitely makes a big difference making EF Core almost on par with the EF6 implementation. Another thing I was wondering about is that setting the context AutoDetectChangesEnabled = false in my example seems to have no effect in EF Core while in EF 6 not setting this property to false an as such keeping the default true, makes EF 6 terrible slow. Can you explain this behavior as well? |
@HjalteParner See #14001 |
Thanks @ajcvickers, that explains the current behavior (or lack of behavior you might say) when AutoDetectChangesEnabled is set to true. |
Part of #8898 Fixes #14231 Investigation into DbSet.Local shows that: * Iteration over tracked entities had a lot of LINQ code; fixed by un-LINQing * Multiple composed iterators cause slow-down; fixed by giving LocalView it's only IStateManager method * Filtering causes slow-down; fixed by splitting storage into multiple dictionaries. Makes lookup for entity instance slightly slower. * Calculate Count lazily This means that DbSet.Local iteration is now about 3x faster, although it depends a lot on what is being tracked. Getting an ObservableCollection and then iterating over that _once_ is slower than just iterating over the entries once. Iterating over the ObservableCollection multiple times is still faster than iterating over DbSet.Local multiple times. This seems like a reasonable trade-off. If the application does heavy iteration, then creating a new collection for that seems reasonable. With regard to #8898, the layering we have still seems correct, so moving forward with the breaking changes there.
Part of #8898 Fixes #14231 Investigation into DbSet.Local shows that: * Iteration over tracked entities had a lot of LINQ code; fixed by un-LINQing * Multiple composed iterators cause slow-down; fixed by giving LocalView it's only IStateManager method * Filtering causes slow-down; fixed by splitting storage into multiple dictionaries. Makes lookup for entity instance slightly slower. * Calculate Count lazily This means that DbSet.Local iteration is now about 3x faster, although it depends a lot on what is being tracked. Getting an ObservableCollection and then iterating over that _once_ is slower than just iterating over the entries once. Iterating over the ObservableCollection multiple times is still faster than iterating over DbSet.Local multiple times. This seems like a reasonable trade-off. If the application does heavy iteration, then creating a new collection for that seems reasonable. With regard to #8898, the layering we have still seems correct, so moving forward with the breaking changes there.
Part of #8898 Fixes #14231 Investigation into DbSet.Local shows that: * Iteration over tracked entities had a lot of LINQ code; fixed by un-LINQing * Multiple composed iterators cause slow-down; fixed by giving LocalView it's only IStateManager method * Filtering causes slow-down; fixed by splitting storage into multiple dictionaries. Makes lookup for entity instance slightly slower. * Calculate Count lazily This means that DbSet.Local iteration is now about 3x faster, although it depends a lot on what is being tracked. Getting an ObservableCollection and then iterating over that _once_ is slower than just iterating over the entries once. Iterating over the ObservableCollection multiple times is still faster than iterating over DbSet.Local multiple times. This seems like a reasonable trade-off. If the application does heavy iteration, then creating a new collection for that seems reasonable. With regard to #8898, the layering we have still seems correct, so moving forward with the breaking changes there.
Part of #8898 Fixes #14231 Investigation into DbSet.Local shows that: * Iteration over tracked entities had a lot of LINQ code; fixed by un-LINQing * Multiple composed iterators cause slow-down; fixed by giving LocalView it's only IStateManager method * Filtering causes slow-down; fixed by splitting storage into multiple dictionaries. Makes lookup for entity instance slightly slower. * Calculate Count lazily This means that DbSet.Local iteration is now about 3x faster, although it depends a lot on what is being tracked. Getting an ObservableCollection and then iterating over that _once_ is slower than just iterating over the entries once. Iterating over the ObservableCollection multiple times is still faster than iterating over DbSet.Local multiple times. This seems like a reasonable trade-off. If the application does heavy iteration, then creating a new collection for that seems reasonable. With regard to #8898, the layering we have still seems correct, so moving forward with the breaking changes there.
Part of #8898 Fixes #14231 Investigation into DbSet.Local shows that: * Iteration over tracked entities had a lot of LINQ code; fixed by un-LINQing * Multiple composed iterators cause slow-down; fixed by giving LocalView it's only IStateManager method * Filtering causes slow-down; fixed by splitting storage into multiple dictionaries. Makes lookup for entity instance slightly slower. * Calculate Count lazily This means that DbSet.Local iteration is now about 3x faster, although it depends a lot on what is being tracked. Getting an ObservableCollection and then iterating over that _once_ is slower than just iterating over the entries once. Iterating over the ObservableCollection multiple times is still faster than iterating over DbSet.Local multiple times. This seems like a reasonable trade-off. If the application does heavy iteration, then creating a new collection for that seems reasonable. With regard to #8898, the layering we have still seems correct, so moving forward with the breaking changes there.
I don't have quality metrics on this, but that appears to be true for me as well. Moving from 2.2 to 3.1 and a stress test we have that executes with a warning benchmark of above 24 seconds (and passes in 2.2), Over 1000 entities being added onto the context, the execution of checking 7 local's with .Any() goes from 67ms to 720ms. The stress test processes 5000 in just this one spot, so the expected total processing time in 2.2 cannot possibly exceed an average of 4ms each, if my math is correct, so even the baseline execution time is slower by an order of magnitude, potentially two, as that 4ms is for the entirety of code execution. I also appear to have a 3-50 ms drift elsewhere, that's just All my other timing metrics are showing a delta between timings of 0 ms from initial to 1000th. EDIT: Looks like the problem might be https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#dbe Related/more precise/else https://entityframeworkcore.com/knowledge-base/61074160/dbset-tentity--local-any---performance-issue-when-upgraging-ef-core-from-2-2-6-to-3-1-3 |
Discussion continued on #21564 |
I have been queering EF 6 DbSet.Local with great success for a number of years. However doing the same in EF Core is a factor 6 slower which unfortunately is a showstopper for me as I'm dealing with a huge amount of data which again prevent me to make the shift from EF 6 to EF Core.
Please help me to solve this issue.
Below please find an example which can be run both in EF 6. and EF Core
private BlogContext _blogContext;
The text was updated successfully, but these errors were encountered: