Skip to content

Commit

Permalink
Query: Perf: Large perf improvement in Include.
Browse files Browse the repository at this point in the history
- Eliminated dynamic delegate creations during Include query execution.

Fix #6623
  • Loading branch information
anpete committed Sep 30, 2016
1 parent 6c00355 commit d013493
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,9 @@ var shaper
methodCallExpression,
entityAccessor,
Expression.Constant(_navigationPath),
Expression.Constant(
_createRelatedEntitiesLoadersMethodInfo
.MakeGenericMethod(_queryCompilationContext.QueryMethodProvider.RelatedEntitiesLoaderType)
.Invoke(this, new object[] { _querySource, _navigationPath })),
(Expression)_createRelatedEntitiesLoadersMethodInfo
.MakeGenericMethod(_queryCompilationContext.QueryMethodProvider.RelatedEntitiesLoaderType)
.Invoke(this, new object[] { _querySource, _navigationPath }),
Expression.Constant(_querySourceRequiresTracking));
}
}
Expand Down Expand Up @@ -147,42 +146,55 @@ var shaper

var existingGroupJoinIncludeArgument = methodCallExpression.Arguments[groupJoinIncludeArgumentIndex];
var existingGroupJoinIncludeWithAccessor = existingGroupJoinIncludeArgument as MethodCallExpression;
var withAccessorMethodInfo = _queryCompilationContext.QueryMethodProvider.GroupJoinIncludeType
.GetTypeInfo().GetDeclaredMethod(nameof(GroupJoinInclude.WithEntityAccessor));

var existingGroupJoinIncludeExpression = existingGroupJoinIncludeWithAccessor != null
&& existingGroupJoinIncludeWithAccessor.Method == withAccessorMethodInfo
var withAccessorMethodInfo
= _queryCompilationContext.QueryMethodProvider.GroupJoinIncludeType
.GetTypeInfo()
.GetDeclaredMethod(nameof(GroupJoinInclude.WithEntityAccessor));

var existingGroupJoinIncludeExpression
= existingGroupJoinIncludeWithAccessor != null
&& existingGroupJoinIncludeWithAccessor.Method == withAccessorMethodInfo
? existingGroupJoinIncludeWithAccessor.Object
: existingGroupJoinIncludeArgument;

var relatedEntitiesLoaders
= Expression.Lambda<Func<object>>(
(Expression)_createRelatedEntitiesLoadersMethodInfo
.MakeGenericMethod(_queryCompilationContext.QueryMethodProvider.RelatedEntitiesLoaderType)
.Invoke(this, new object[]
{
_querySource,
_navigationPath
}))
.Compile()
.Invoke();

var groupJoinInclude
= _queryCompilationContext.QueryMethodProvider
.CreateGroupJoinInclude(
_navigationPath,
_querySourceRequiresTracking,
(existingGroupJoinIncludeExpression as ConstantExpression)?.Value,
_createRelatedEntitiesLoadersMethodInfo
.MakeGenericMethod(_queryCompilationContext.QueryMethodProvider.RelatedEntitiesLoaderType)
.Invoke(this, new object[]
{
_querySource,
_navigationPath
}));
relatedEntitiesLoaders);

if (groupJoinInclude != null)
{
var groupJoinIncludeExpression = (Expression)Expression.Constant(groupJoinInclude);
var accessorLambda = shaper.GetAccessorExpression(_querySource) as LambdaExpression;

if (accessorLambda != null
&& accessorLambda.Parameters.Single().Type.GetTypeInfo().IsValueType)
{
groupJoinIncludeExpression = Expression.Call(
groupJoinIncludeExpression,
withAccessorMethodInfo,
shaper.GetAccessorExpression(_querySource));
groupJoinIncludeExpression
= Expression.Call(
groupJoinIncludeExpression,
withAccessorMethodInfo,
shaper.GetAccessorExpression(_querySource));
}

var newArguments = methodCallExpression.Arguments.ToList();

newArguments[groupJoinIncludeArgumentIndex] = groupJoinIncludeExpression;

return methodCallExpression.Update(methodCallExpression.Object, newArguments);
Expand All @@ -197,10 +209,12 @@ var groupJoinInclude
.GetDeclaredMethod(nameof(CreateRelatedEntitiesLoaders));

[UsedImplicitly]
private IReadOnlyList<Func<QueryContext, TRelatedEntitiesLoader>> CreateRelatedEntitiesLoaders<TRelatedEntitiesLoader>(
private NewArrayExpression CreateRelatedEntitiesLoaders<TRelatedEntitiesLoader>(
IQuerySource querySource, IEnumerable<INavigation> navigationPath)
{
var relatedEntitiesLoaders = new List<Func<QueryContext, TRelatedEntitiesLoader>>();
var queryContextParameter = Expression.Parameter(typeof(QueryContext));

var relatedEntitiesLoaders = new List<Expression<Func<QueryContext, TRelatedEntitiesLoader>>>();

var selectExpression
= _queryCompilationContext.FindSelectExpression(querySource);
Expand Down Expand Up @@ -284,17 +298,16 @@ var materializer

targetTableExpression = joinedTableExpression;

relatedEntitiesLoaders.Add(qc =>
(TRelatedEntitiesLoader)_queryCompilationContext.QueryMethodProvider
.CreateReferenceRelatedEntitiesLoaderMethod
.Invoke(
null,
new object[]
{
valueBufferOffset,
queryIndex,
materializer.Compile() // TODO: Used cached materializer?
}));
relatedEntitiesLoaders.Add(
Expression.Lambda<Func<QueryContext, TRelatedEntitiesLoader>>(
Expression.Call(
_queryCompilationContext.QueryMethodProvider
.CreateReferenceRelatedEntitiesLoaderMethod,
Expression.Constant(valueBufferOffset),
Expression.Constant(queryIndex),
materializer
),
queryContextParameter));
}
else
{
Expand Down Expand Up @@ -366,7 +379,9 @@ var subqueryTable

AddToPredicate(targetSelectExpression, existsPredicateExpression);

AddToPredicate(subqueryExpression, BuildJoinEqualityExpression(navigation, targetTableExpression, subqueryTable, querySource));
AddToPredicate(
subqueryExpression,
BuildJoinEqualityExpression(navigation, targetTableExpression, subqueryTable, querySource));

subqueryExpression.Predicate
= compositePredicateExpressionVisitor
Expand All @@ -379,8 +394,11 @@ var subqueryTable
foreach (var ordering in selectExpression.OrderBy)
{
// ReSharper disable once PossibleNullReferenceException
var principalKeyProperty = ((ordering.Expression as AliasExpression)?.Expression as ColumnExpression).Property;
var principalKeyProperty
= ((ordering.Expression as AliasExpression)?.Expression as ColumnExpression).Property;

var referencedForeignKeyProperty = pkPropertiesToFkPropertiesMap[principalKeyProperty];

targetSelectExpression
.AddToOrderBy(
_relationalAnnotationProvider.For(referencedForeignKeyProperty).ColumnName,
Expand Down Expand Up @@ -420,29 +438,36 @@ var innerJoinSelectExpression

selectExpression = targetSelectExpression;

relatedEntitiesLoaders.Add(qc =>
(TRelatedEntitiesLoader)_queryCompilationContext.QueryMethodProvider
.CreateCollectionRelatedEntitiesLoaderMethod
.Invoke(
null,
new object[]
{
qc,
relatedEntitiesLoaders.Add(
Expression.Lambda<Func<QueryContext, TRelatedEntitiesLoader>>(
Expression.Call(
_queryCompilationContext.QueryMethodProvider
.CreateCollectionRelatedEntitiesLoaderMethod,
queryContextParameter,
Expression.Constant(
_shaperCommandContextFactory.Create(() =>
_querySqlGeneratorFactory.CreateDefault(targetSelectExpression)),
queryIndex,
materializer.Compile() // TODO: Used cached materializer?
}));
_querySqlGeneratorFactory.CreateDefault(targetSelectExpression))),
Expression.Constant(queryIndex),
materializer
),
queryContextParameter));
}
}

return relatedEntitiesLoaders;
return Expression.NewArrayInit(
typeof(Func<QueryContext, TRelatedEntitiesLoader>),
relatedEntitiesLoaders);
}

private JoinExpressionBase AdjustJoinExpression(
SelectExpression selectExpression, JoinExpressionBase joinExpression)
{
var subquery = new SelectExpression(_querySqlGeneratorFactory, _queryCompilationContext) { Alias = joinExpression.Alias };
var subquery
= new SelectExpression(_querySqlGeneratorFactory, _queryCompilationContext)
{
Alias = joinExpression.Alias
};

// Don't create new alias when adding tables to subquery
subquery.AddTable(joinExpression.TableExpression, createUniqueAlias: false);
subquery.IsProjectStar = true;
Expand Down Expand Up @@ -612,11 +637,10 @@ private static IEnumerable<Expression> ExtractProjections(TableExpressionBase ta

if (selectExpression != null)
{
if (selectExpression.IsProjectStar)
{
return selectExpression.Tables.SelectMany(ExtractProjections);
}
return selectExpression.Projection.ToList();
return
selectExpression.IsProjectStar
? selectExpression.Tables.SelectMany(ExtractProjections)
: selectExpression.Projection.ToList();
}

var joinExpression = tableExpression as JoinExpressionBase;
Expand All @@ -633,12 +657,9 @@ private static void AddToPredicate(SelectExpression selectExpression, Expression
: Expression.AndAlso(selectExpression.Predicate, predicateToAdd);

private static bool IsOrderingOnNonPrincipalKeyProperties(
IEnumerable<Ordering> orderings, IReadOnlyList<IProperty> properties)
{
return
orderings
.Select(ordering => ((ordering.Expression as AliasExpression)?.Expression as ColumnExpression)?.Property)
.Any(property => !properties.Contains(property));
}
IEnumerable<Ordering> orderings, IReadOnlyList<IProperty> properties)
=> orderings
.Select(ordering => ((ordering.Expression as AliasExpression)?.Expression as ColumnExpression)?.Property)
.Any(property => !properties.Contains(property));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ public class QueryMethodProvider : IQueryMethodProvider
[UsedImplicitly]
// ReSharper disable once InconsistentNaming
private static IEnumerable<ValueBuffer> _Query(
QueryContext queryContext,
ShaperCommandContext shaperCommandContext,
QueryContext queryContext,
ShaperCommandContext shaperCommandContext,
int? queryIndex)
=> new QueryingEnumerable(
(RelationalQueryContext)queryContext,
shaperCommandContext,
queryIndex);
(RelationalQueryContext)queryContext,
shaperCommandContext,
queryIndex);

/// <summary>
/// Gets the get result method.
Expand Down Expand Up @@ -492,10 +492,10 @@ public virtual MethodInfo CreateCollectionRelatedEntitiesLoaderMethod
[UsedImplicitly]
// ReSharper disable once InconsistentNaming
private static IRelatedEntitiesLoader _CreateCollectionRelatedEntitiesLoader(
QueryContext queryContext,
ShaperCommandContext shaperCommandContext,
int queryIndex,
Func<ValueBuffer, object> materializer)
QueryContext queryContext,
ShaperCommandContext shaperCommandContext,
int queryIndex,
Func<ValueBuffer, object> materializer)
=> new CollectionRelatedEntitiesLoader(queryContext, shaperCommandContext, queryIndex, materializer);

private class CollectionRelatedEntitiesLoader : IRelatedEntitiesLoader
Expand All @@ -518,12 +518,9 @@ private class CollectionRelatedEntitiesLoader : IRelatedEntitiesLoader
}

public IEnumerable<EntityLoadInfo> Load(QueryContext queryContext, IIncludeKeyComparer keyComparer)
{
return
_includeCollectionIterator
.GetRelatedValues(keyComparer)
.Select(vr => new EntityLoadInfo(vr, _materializer));
}
=> _includeCollectionIterator
.GetRelatedValues(keyComparer)
.Select(vr => new EntityLoadInfo(vr, _materializer));

public void Dispose() => _includeCollectionIterator?.Dispose();
}
Expand All @@ -541,9 +538,9 @@ public IEnumerable<EntityLoadInfo> Load(QueryContext queryContext, IIncludeKeyCo
[UsedImplicitly]
// ReSharper disable once InconsistentNaming
private static IEnumerable<TElement> _InjectParameters<TElement>(
QueryContext queryContext,
IEnumerable<TElement> source,
string[] parameterNames,
QueryContext queryContext,
IEnumerable<TElement> source,
string[] parameterNames,
object[] parameterValues)
=> new ParameterInjector<TElement>(queryContext, source, parameterNames, parameterValues);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,34 @@ var customers
,
entityAccessor: default(System.Func`2[Specification.Tests.TestModels.Northwind.Customer,System.Object]),
navigationPath: INavigation[] { Customer.Orders, },
relatedEntitiesLoaderFactories: List<Func<QueryContext, IRelatedEntitiesLoader>>
{
System.Func`2[QueryContext,Internal.IRelatedEntitiesLoader],
}
relatedEntitiesLoaderFactories: new Func<QueryContext, IRelatedEntitiesLoader>[]{ (QueryContext ) => IRelatedEntitiesLoader _CreateCollectionRelatedEntitiesLoader(
queryContext: ,
shaperCommandContext: SelectExpression:
SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE EXISTS (
SELECT 1
FROM [Customers] AS [c]
WHERE [o].[CustomerID] = [c].[CustomerID])
ORDER BY [o].[CustomerID]
,
queryIndex: 1,
materializer: (ValueBuffer valueBuffer) =>
{
var3 = new Order()
var3.<OrderID>k__BackingField = try { (int) object valueBuffer.get_Item(0) } catch (Exception) { ... }
var3.<CustomerID>k__BackingField = try { (string) object valueBuffer.get_Item(1) } catch (Exception) { ... }
var3.<EmployeeID>k__BackingField = try { (Nullable<int>) object valueBuffer.get_Item(2) } catch (Exception) { ... }
var3.<OrderDate>k__BackingField = try { (Nullable<DateTime>) object valueBuffer.get_Item(3) } catch (Exception) { ... }
return instance
}
)
}
,
querySourceRequiresTracking: True
)",
)
Opening connection to database 'Northwind' on server '(localdb)\MSSQLLocalDB'.
Closing connection to database 'Northwind' on server '(localdb)\MSSQLLocalDB'.",
TestSqlLoggerFactory.Log.Replace(Environment.NewLine, FileLineEnding));
}
}
Expand Down

0 comments on commit d013493

Please sign in to comment.