efcore
efcore copied to clipboard
Compiled model performance trends
In investigating the compiled model changes in EF9, I noticed that things were taking quite a lot longer than expected, so I did some analysis.
The model here is the one used in the samples: https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Miscellaneous/CompiledModels
The first issue is that, for this sample model with 449 entity types, 6390 properties, and 720 relationships, the startup time is now worse when using a compiled model.
EF version | Normal (s) | Compiled model (s) |
---|---|---|
EF Core 6 | 3.71 | 0.93 |
EF Core 7 | 4.07 | 1.04 |
EF Core 8 | 4.52 | 3.37 |
EF Core 9 | 4.48 | 5.24 |
By startup time, I mean wall clock time to run the following:
using (var context = new BlogsContext())
{
entityCount = context.Model.GetEntityTypes().Count();
}
My suspicion that a lot of this is assembly loading and/or JIT, based on the increase in DLL size across releases:
EF version | Normal (MB) | Compiled model (MB) |
---|---|---|
EF Core 6 | 0.9 | 2 |
EF Core 7 | 0.9 | 2 |
EF Core 8 | 0.9 | 8 |
EF Core 9 | 0.9 | 20 |
Related to all this is that the time to run dotnet ef dbcontext optimize
on this same model has increased very dramatically in EF9.
EF version | Time to run dbcontext optimize (s) |
---|---|
EF Core 6 | 11 |
EF Core 7 | 11 |
EF Core 8 | 40 |
EF Core 9 | 107 |
Note: see profiling session done by @muzopraha in #33495:
This also shows regression for non-compiled models. Is a separate issue needed to track that?
@stevendarby yeah, that's true - we'll discuss this in the team as well.
@stevendarby @roji We discussed the regression in 8.0 at the time, and it was considered acceptable because if people had slow model building performance then they could use compiled models! Oh, the irony.
@ajcvickers Irony aside, it's also not really true in all cases - for example, if you use query filters?
@stevendarby Agreed.
Is it a reasonable workaround, for now, to compile the model with EF Core 7, but then upgrade packages and run with v8 or v9? It doesn't crash and burn immediately, but I'd like to know if that's a completely unsupported mix. Thanks.
Not sure how surprising this is, but when I encountered this issue, I discovered that AOT doesn't really improve the model init time of compiled models. (Leaving aside the fact queries won't run on AOT).
Is it a reasonable workaround, for now, to compile the model with EF Core 7, but then upgrade packages and run with v8 or v9? It doesn't crash and burn immediately, but I'd like to know if that's a completely unsupported mix. Thanks.
No, it's completely unsupported. A "better" workaround would be to use some post-processing script to remove the slow calls added in the newer code. Most of them will be just computed lazily when not using NativeAOT.
This is the proposed action plan to deal with this regression:
- [ ] By default generate a compiled model equivalent to what EF Core 6 would generate, with CLI switches that would add more code to the compiled model (For NativeAOT fully eager generation is required)
- [ ] #31370
- [ ] Look for opportunities to generate less code by leveraging defaults (in particular, for provider-specific configuration)
- [ ] Fully qualify (including the return type) the generated lambdas
- [ ] "Lift" reused lambdas, comparers and type mappings to a common location
- [ ] By default, don't use the backing field for non-virtual auto-props
- [ ] Inline property accessors to avoid lambdas with closures (this would require new API on
InternalEntityEntry
) - [ ] Don't generate lambda expressions for NativeAOT (#32717)