Skip to content

Commit b7a436f

Browse files
authored
Fix to dotnet#35212 - Query/Perf: Compile identifier lambdas passed to PopulateIncludeCollection rather than inline (dotnet#35217)
Fixes dotnet#35212 Port of dotnet#35213 **Description** In EF9 we changed the way we generate shapers in preparation for AOT scenarios. As part of these changes we started inlining some delegates passed to PopulateIncludeCollection (as well as couple other methods), rather than compiling them. For scenarios with large number of entities we see significant perf degradation when these delegates are inlined, as opposed to compiled (like we used to do in EF8). This change reverts to EF8 behavior. **Customer impact** Queries using collection navigation with significant amount of data suffer large performance degradation when compared with EF8. No good workaround. **How found** Multiple customer reports on 9.0.0. **Regression** Yes, from 8.0. Perf regression only, no functional regression here. **Testing** Ad-hoc perf testing with BenchmarkDotNet. Functional change already covered by numerous tests. **Risk** Low - essentially reverting back to EF8 behavior, quirk added. **Benchmarks** before the fix (but already with invoke fix and no interpretation) | Method | Async | Mean | Error | StdDev | Op/s | Gen0 | Gen1 | Allocated | |------------ |------ |---------:|--------:|---------:|------:|-----------:|----------:|----------:| | MultiInclue | False | 455.1 ms | 8.94 ms | 10.29 ms | 2.197 | 11000.0000 | 6000.0000 | 67.92 MB | | MultiInclue | True | 435.4 ms | 1.77 ms | 1.66 ms | 2.297 | 11000.0000 | 6000.0000 | 67.92 MB | after the fix: | Method | Async | Mean | Error | StdDev | Op/s | Gen0 | Gen1 | Allocated | |------------ |------ |---------:|--------:|--------:|------:|----------:|----------:|----------:| | MultiInclue | False | 363.3 ms | 6.72 ms | 6.29 ms | 2.752 | 9000.0000 | 3000.0000 | 58.51 MB | | MultiInclue | True | 351.9 ms | 2.08 ms | 1.74 ms | 2.842 | 9000.0000 | 3000.0000 | 58.51 MB | This gets us pretty close to the EF8 numbers which were: | Method | Async | Mean | Error | StdDev | Op/s | Gen0 | Gen1 | Allocated | |------------ |------ |---------:|--------:|--------:|------:|----------:|----------:|----------:| | MultiInclue | False | 297.1 ms | 1.47 ms | 1.30 ms | 3.365 | 8000.0000 | 6000.0000 | 52.4 MB | | MultiInclue | True | 290.2 ms | 3.76 ms | 3.52 ms | 3.446 | 8500.0000 | 6000.0000 | 52.4 MB |
1 parent 59e92ae commit b7a436f

File tree

1 file changed

+117
-14
lines changed

1 file changed

+117
-14
lines changed

src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs

Lines changed: 117 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public partial class RelationalShapedQueryCompilingExpressionVisitor
2424
/// </summary>
2525
public sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisitor
2626
{
27+
private static readonly bool UseOldBehavior35212 =
28+
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35212", out var enabled35212) && enabled35212;
29+
2730
/// <summary>
2831
/// Reading database values
2932
/// </summary>
@@ -858,16 +861,46 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
858861
QueryCompilationContext.QueryContextParameter,
859862
_dataReaderParameter);
860863

864+
var parentIdentifierExpression = UseOldBehavior35212
865+
? parentIdentifierLambda
866+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
867+
parentIdentifierLambda.Compile(),
868+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
869+
parentIdentifierLambda,
870+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
871+
"parentIdentifierLambda",
872+
typeof(Func<QueryContext, DbDataReader, object[]>));
873+
861874
var outerIdentifierLambda = Lambda(
862875
Visit(relationalCollectionShaperExpression.OuterIdentifier),
863876
QueryCompilationContext.QueryContextParameter,
864877
_dataReaderParameter);
865878

879+
var outerIdentifierExpression = UseOldBehavior35212
880+
? outerIdentifierLambda
881+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
882+
outerIdentifierLambda.Compile(),
883+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
884+
outerIdentifierLambda,
885+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
886+
"outerIdentifierLambda",
887+
typeof(Func<QueryContext, DbDataReader, object[]>));
888+
866889
var selfIdentifierLambda = Lambda(
867890
Visit(relationalCollectionShaperExpression.SelfIdentifier),
868891
QueryCompilationContext.QueryContextParameter,
869892
_dataReaderParameter);
870893

894+
var selfIdentifierExpression = UseOldBehavior35212
895+
? selfIdentifierLambda
896+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
897+
selfIdentifierLambda.Compile(),
898+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
899+
selfIdentifierLambda,
900+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
901+
"selfIdentifierLambda",
902+
typeof(Func<QueryContext, DbDataReader, object[]>));
903+
871904
_inline = false;
872905

873906
_includeExpressions.Add(
@@ -878,8 +911,8 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
878911
_dataReaderParameter,
879912
_resultCoordinatorParameter,
880913
entity,
881-
parentIdentifierLambda,
882-
outerIdentifierLambda,
914+
parentIdentifierExpression,
915+
outerIdentifierExpression,
883916
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
884917
navigation,
885918
LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(navigation),
@@ -907,9 +940,9 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
907940
QueryCompilationContext.QueryContextParameter,
908941
_dataReaderParameter,
909942
_resultCoordinatorParameter,
910-
parentIdentifierLambda,
911-
outerIdentifierLambda,
912-
selfIdentifierLambda,
943+
parentIdentifierExpression,
944+
outerIdentifierExpression,
945+
selfIdentifierExpression,
913946
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
914947
relationalCollectionShaperExpression.ParentIdentifierValueComparers
915948
.Select(x => (Func<object, object, bool>)x.Equals).ToArray(),
@@ -982,6 +1015,16 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
9821015
QueryCompilationContext.QueryContextParameter,
9831016
_dataReaderParameter);
9841017

1018+
var parentIdentifierExpression = UseOldBehavior35212
1019+
? parentIdentifierLambda
1020+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
1021+
parentIdentifierLambda.Compile(),
1022+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
1023+
parentIdentifierLambda,
1024+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
1025+
"parentIdentifierLambda",
1026+
typeof(Func<QueryContext, DbDataReader, object[]>));
1027+
9851028
_inline = false;
9861029

9871030
innerProcessor._inline = true;
@@ -991,6 +1034,16 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
9911034
QueryCompilationContext.QueryContextParameter,
9921035
innerProcessor._dataReaderParameter);
9931036

1037+
var childIdentifierExpression = UseOldBehavior35212
1038+
? childIdentifierLambda
1039+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
1040+
childIdentifierLambda.Compile(),
1041+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
1042+
childIdentifierLambda,
1043+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
1044+
"childIdentifierLambda",
1045+
typeof(Func<QueryContext, DbDataReader, object[]>));
1046+
9941047
innerProcessor._inline = false;
9951048

9961049
_includeExpressions.Add(
@@ -1001,7 +1054,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
10011054
_dataReaderParameter,
10021055
_resultCoordinatorParameter,
10031056
entity,
1004-
parentIdentifierLambda,
1057+
parentIdentifierExpression,
10051058
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
10061059
navigation,
10071060
LiftableConstantExpressionHelpers.BuildNavigationAccessLambda(navigation),
@@ -1031,7 +1084,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
10311084
CreateReaderColumnsExpression(readerColumns, _parentVisitor.Dependencies.LiftableConstantFactory),
10321085
Constant(_detailedErrorsEnabled),
10331086
_resultCoordinatorParameter,
1034-
childIdentifierLambda,
1087+
childIdentifierExpression,
10351088
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
10361089
relationalSplitCollectionShaperExpression.IdentifierValueComparers
10371090
.Select(x => (Func<object, object, bool>)x.Equals).ToArray(),
@@ -1150,16 +1203,46 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
11501203
QueryCompilationContext.QueryContextParameter,
11511204
_dataReaderParameter);
11521205

1206+
var parentIdentifierExpression = UseOldBehavior35212
1207+
? parentIdentifierLambda
1208+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
1209+
parentIdentifierLambda.Compile(),
1210+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
1211+
parentIdentifierLambda,
1212+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
1213+
"parentIdentifierLambda",
1214+
typeof(Func<QueryContext, DbDataReader, object[]>));
1215+
11531216
var outerIdentifierLambda = Lambda(
11541217
Visit(relationalCollectionShaperExpression.OuterIdentifier),
11551218
QueryCompilationContext.QueryContextParameter,
11561219
_dataReaderParameter);
11571220

1221+
var outerIdentifierExpression = UseOldBehavior35212
1222+
? outerIdentifierLambda
1223+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
1224+
outerIdentifierLambda.Compile(),
1225+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
1226+
outerIdentifierLambda,
1227+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
1228+
"outerIdentifierLambda",
1229+
typeof(Func<QueryContext, DbDataReader, object[]>));
1230+
11581231
var selfIdentifierLambda = Lambda(
11591232
Visit(relationalCollectionShaperExpression.SelfIdentifier),
11601233
QueryCompilationContext.QueryContextParameter,
11611234
_dataReaderParameter);
11621235

1236+
var selfIdentifierExpression = UseOldBehavior35212
1237+
? selfIdentifierLambda
1238+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
1239+
selfIdentifierLambda.Compile(),
1240+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
1241+
selfIdentifierLambda,
1242+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
1243+
"selfIdentifierLambda",
1244+
typeof(Func<QueryContext, DbDataReader, object[]>));
1245+
11631246
_inline = false;
11641247

11651248
var collectionParameter = Parameter(relationalCollectionShaperExpression.Type);
@@ -1173,8 +1256,8 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
11731256
QueryCompilationContext.QueryContextParameter,
11741257
_dataReaderParameter,
11751258
_resultCoordinatorParameter,
1176-
parentIdentifierLambda,
1177-
outerIdentifierLambda,
1259+
parentIdentifierExpression,
1260+
outerIdentifierExpression,
11781261
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
11791262
collectionAccessor,
11801263
LiftableConstantExpressionHelpers.BuildClrCollectionAccessorLambda(navigation),
@@ -1195,9 +1278,9 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
11951278
QueryCompilationContext.QueryContextParameter,
11961279
_dataReaderParameter,
11971280
_resultCoordinatorParameter,
1198-
parentIdentifierLambda,
1199-
outerIdentifierLambda,
1200-
selfIdentifierLambda,
1281+
parentIdentifierExpression,
1282+
outerIdentifierExpression,
1283+
selfIdentifierExpression,
12011284
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
12021285
relationalCollectionShaperExpression.ParentIdentifierValueComparers
12031286
.Select(x => (Func<object, object, bool>)x.Equals).ToArray(),
@@ -1267,6 +1350,16 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
12671350
QueryCompilationContext.QueryContextParameter,
12681351
_dataReaderParameter);
12691352

1353+
var parentIdentifierExpression = UseOldBehavior35212
1354+
? parentIdentifierLambda
1355+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
1356+
parentIdentifierLambda.Compile(),
1357+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
1358+
parentIdentifierLambda,
1359+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
1360+
"parentIdentifierLambda",
1361+
typeof(Func<QueryContext, DbDataReader, object[]>));
1362+
12701363
_inline = false;
12711364

12721365
innerProcessor._inline = true;
@@ -1276,6 +1369,16 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
12761369
QueryCompilationContext.QueryContextParameter,
12771370
innerProcessor._dataReaderParameter);
12781371

1372+
var childIdentifierExpression = UseOldBehavior35212
1373+
? childIdentifierLambda
1374+
: _parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
1375+
childIdentifierLambda.Compile(),
1376+
Lambda<Func<MaterializerLiftableConstantContext, object>>(
1377+
childIdentifierLambda,
1378+
Parameter(typeof(MaterializerLiftableConstantContext), "_")),
1379+
"childIdentifierLambda",
1380+
typeof(Func<QueryContext, DbDataReader, object[]>));
1381+
12791382
innerProcessor._inline = false;
12801383

12811384
var collectionParameter = Parameter(collectionType);
@@ -1290,7 +1393,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
12901393
QueryCompilationContext.QueryContextParameter,
12911394
_dataReaderParameter,
12921395
_resultCoordinatorParameter,
1293-
parentIdentifierLambda,
1396+
parentIdentifierExpression,
12941397
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
12951398
collectionAccessor,
12961399
LiftableConstantExpressionHelpers.BuildClrCollectionAccessorLambda(navigation),
@@ -1315,7 +1418,7 @@ when GetProjectionIndex(collectionResultExpression.ProjectionBindingExpression)
13151418
CreateReaderColumnsExpression(readerColumns, _parentVisitor.Dependencies.LiftableConstantFactory),
13161419
Constant(_detailedErrorsEnabled),
13171420
_resultCoordinatorParameter,
1318-
childIdentifierLambda,
1421+
childIdentifierExpression,
13191422
_parentVisitor.Dependencies.LiftableConstantFactory.CreateLiftableConstant(
13201423
relationalSplitCollectionShaperExpression.IdentifierValueComparers
13211424
.Select(x => (Func<object, object, bool>)x.Equals).ToArray(),

0 commit comments

Comments
 (0)