Skip to content

Optimize ItemProjectorExpression #391

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

Merged
merged 1 commit into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions Orm/Xtensive.Orm/Linq/ExpressionWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -476,19 +476,20 @@ protected override InvocationExpression VisitInvocation(InvocationExpression i)
/// <inheritdoc/>
protected override LambdaExpression VisitLambda<T>(Expression<T> l)
{
if (l.Parameters.Count > 1) {
var lambdaParameters = l.Parameters;
if (lambdaParameters.Count > 1) {
Write("(");
for (int i = 0, n = l.Parameters.Count; i < n; i++) {
Write(l.Parameters[i].Name);
for (int i = 0, n = lambdaParameters.Count; i < n; i++) {
Write(lambdaParameters[i].Name);
if (i < n - 1) {
Write(", ");
}
}

Write(")");
}
else if (l.Parameters.Count == 1) {
Write(l.Parameters[0].Name);
else if (lambdaParameters.Count == 1) {
Write(lambdaParameters[0].Name);
}
else {
Write("()");
Expand Down Expand Up @@ -657,4 +658,4 @@ public ExpressionWriter(TextWriter writer, int indentSize)
this.indentSize = indentSize;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ protected override int VisitMemberAccess(MemberExpression m) =>

protected override int VisitLambda(LambdaExpression l)
{
parameters.AddRange(l.Parameters);
return HashCode.Combine(HashExpressionSequence(l.Parameters.Cast<Expression>()), Visit(l.Body));
var lambdaParameters = l.Parameters;
parameters.AddRange(lambdaParameters);
return HashCode.Combine(HashExpressionSequence(lambdaParameters), Visit(l.Body));
}

protected override int VisitNew(NewExpression n) =>
Expand Down
3 changes: 2 additions & 1 deletion Orm/Xtensive.Orm/Orm/Building/Builders/AttributeProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,8 @@ private LambdaExpression GetExpressionFromProvider(Type providerType, string pro
}

var expression = (LambdaExpression) method.Invoke(null, Array.Empty<object>());
if (expression.Parameters.Count != 1 || !expression.Parameters[0].Type.IsAssignableFrom(parameterType)) {
var expressionParameters = expression.Parameters;
if (expressionParameters.Count != 1 || !expressionParameters[0].Type.IsAssignableFrom(parameterType)) {
throw new DomainBuilderException(string.Format(
Strings.ExLambdaExpressionReturnedByXShouldTakeOneParameterOfTypeYOrAnyBaseTypeOfIt, memberName,
parameterType.FullName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
namespace Xtensive.Orm.Linq.Expressions
{
[Serializable]
internal enum ExtendedExpressionType
internal enum ExtendedExpressionType : short
{
Projection = 1000,
Key,
Expand All @@ -28,4 +28,4 @@ internal enum ExtendedExpressionType
Constructor,
FullText
}
}
}
43 changes: 34 additions & 9 deletions Orm/Xtensive.Orm/Orm/Linq/Expressions/ItemProjectorExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
// Created by: Alexis Kochetov
// Created: 2009.05.06

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Xtensive.Core;
using Xtensive.Orm.Linq.Expressions.Visitors;
Expand Down Expand Up @@ -53,19 +50,38 @@ public IReadOnlyList<ColNum> GetColumns(ColumnExtractionModes columnExtractionMo

public ItemProjectorExpression Remap(CompilableProvider dataSource, ColNum offset)
{
if (offset == 0) {
return new ItemProjectorExpression(Item, dataSource, Context, AggregateType);
var item = Item;
if (offset != 0) {
Dictionary<Expression, Expression> dict = null;
item = GenericExpressionVisitor<IMappedExpression>.Process(Item, mapped => {
if (dict == null) {
dict = new();
}
else {
dict.Clear();
}

return mapped.Remap(offset, dict);
});
}

var item = GenericExpressionVisitor<IMappedExpression>
.Process(Item, mapped => mapped.Remap(offset, new Dictionary<Expression, Expression>()));
return new ItemProjectorExpression(item, dataSource, Context, AggregateType);
}

public ItemProjectorExpression Remap(CompilableProvider dataSource, ColumnMap columnMap)
{
Dictionary<Expression, Expression> dict = null;
var item = GenericExpressionVisitor<IMappedExpression>
.Process(Item, mapped => mapped.Remap(columnMap, new Dictionary<Expression, Expression>()));
.Process(Item, mapped => {
if (dict == null) {
dict = new();
}
else {
dict.Clear();
}

return mapped.Remap(columnMap, dict);
});
return new ItemProjectorExpression(item, dataSource, Context, AggregateType);
}

Expand All @@ -83,8 +99,17 @@ public ItemProjectorExpression BindOuterParameter(ParameterExpression parameter)

public ItemProjectorExpression RemoveOuterParameter()
{
Dictionary<Expression, Expression> dict = null;
var item = GenericExpressionVisitor<IMappedExpression>
.Process(Item, mapped => mapped.RemoveOuterParameter(new Dictionary<Expression, Expression>()));
.Process(Item, mapped => {
if (dict == null) {
dict = new();
}
else {
dict.Clear();
}
return mapped.RemoveOuterParameter(dict);
});
return new ItemProjectorExpression(item, DataSource, Context, AggregateType);
}

Expand Down
7 changes: 4 additions & 3 deletions Orm/Xtensive.Orm/Orm/Linq/Rewriters/AggregateOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ public static List<LambdaExpression> Execute(LambdaExpression expression)
// i => i.Acitve ? i.Value : 0

var finder = new AggregateProjectionFinder();
finder.aggregatedSequence = expression.Parameters.Count==2
? expression.Parameters[1] // Result selector parameter in GroupBy
: expression.Parameters[0]; // Selector parameter in Select after GroupBy
var expressionParameters = expression.Parameters;
finder.aggregatedSequence = expressionParameters.Count==2
? expressionParameters[1] // Result selector parameter in GroupBy
: expressionParameters[0]; // Selector parameter in Select after GroupBy
finder.Visit(expression);
return finder.result;
}
Expand Down
22 changes: 13 additions & 9 deletions Orm/Xtensive.Orm/Orm/Linq/Translator.Queryable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1257,9 +1257,11 @@ private ProjectionExpression CombineProjections(ProjectionExpression outer, Proj
outer = new ProjectionExpression(outer.Type, outer.ItemProjector.Remap(recordQuery, 0), tupleParameterBindings);
inner = new ProjectionExpression(inner.Type, inner.ItemProjector.Remap(recordQuery, outerLength), tupleParameterBindings);

context.Bindings.PermanentAdd(resultSelector.Parameters[0], outer);
context.Bindings.PermanentAdd(resultSelector.Parameters[1], inner);
using (context.Bindings.LinkParameters(resultSelector.Parameters)) {
var contextBindings = context.Bindings;
var resultSelectorParameters = resultSelector.Parameters;
contextBindings.PermanentAdd(resultSelectorParameters[0], outer);
contextBindings.PermanentAdd(resultSelectorParameters[1], inner);
using (contextBindings.LinkParameters(resultSelectorParameters)) {
return BuildProjection(resultSelector);
}
}
Expand Down Expand Up @@ -1416,12 +1418,13 @@ private ProjectionExpression VisitSelectMany(Expression source, LambdaExpression
private ProjectionExpression VisitSelect(Expression expression, LambdaExpression le)
{
var sequence = VisitSequence(expression);
if (le.Parameters.Count == 2) {
var leParameters = le.Parameters;
if (leParameters.Count == 2) {
var indexProjection = GetIndexBinding(le, ref sequence);
context.Bindings.PermanentAdd(le.Parameters[1], indexProjection);
context.Bindings.PermanentAdd(leParameters[1], indexProjection);
}

context.Bindings.PermanentAdd(le.Parameters[0], sequence);
context.Bindings.PermanentAdd(leParameters[0], sequence);
var calculateExpressions = State.RequestCalculateExpressions || State.RequestCalculateExpressionsOnce;
using (CreateScope(new TranslatorState(State) {
CalculateExpressions = calculateExpressions,
Expand All @@ -1444,12 +1447,13 @@ private ProjectionExpression BuildProjection(LambdaExpression le)

private ProjectionExpression VisitWhere(Expression expression, LambdaExpression le)
{
var parameter = le.Parameters[0];
var leParameters = le.Parameters;
var parameter = leParameters[0];
var visitedSource = VisitSequence(expression);
var indexBinding = BindingCollection<ParameterExpression, ProjectionExpression>.BindingScope.Empty;
if (le.Parameters.Count == 2) {
if (leParameters.Count == 2) {
var indexProjection = GetIndexBinding(le, ref visitedSource);
indexBinding = context.Bindings.Add(le.Parameters[1], indexProjection);
indexBinding = context.Bindings.Add(leParameters[1], indexProjection);
}

using (indexBinding)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,16 +450,14 @@ protected override SqlExpression VisitLambda(LambdaExpression l)
{
if (activeParameters.Count>0)
throw new InvalidOperationException();
activeParameters.AddRange(l.Parameters);
for (int i = 0, count = l.Parameters.Count; i < count; i++) {
var p = l.Parameters[i];
var lParameters = l.Parameters;
activeParameters.AddRange(lParameters);
for (int i = 0, count = lParameters.Count; i < count; i++) {
var p = lParameters[i];
sourceMapping[p] = sourceColumns[i];
}
var body = Visit(l.Body);
var sqlContainer = body as SqlContainer;
if (sqlContainer is not null)
return TryUnwrapEnum(sqlContainer);
return body;
return body is SqlContainer sqlContainer ? TryUnwrapEnum(sqlContainer) : body;
}

protected override SqlExpression VisitNew(NewExpression n)
Expand Down
4 changes: 2 additions & 2 deletions Orm/Xtensive.Orm/Orm/Rse/AggregateType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Xtensive.Orm.Rse
/// <summary>
/// Defines the set of aggregate functions.
/// </summary>
public enum AggregateType
public enum AggregateType : byte
{
/// <summary>
/// Average of the values in a column.
Expand All @@ -36,4 +36,4 @@ public enum AggregateType
/// </summary>
Sum = 4
}
}
}
19 changes: 11 additions & 8 deletions Orm/Xtensive.Orm/Orm/Rse/Transformation/ColumnMappingInspector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,21 @@ internal protected override PredicateJoinProvider VisitPredicateJoin(PredicateJo
{
var (leftMapping, rightMapping) = SplitMappings(provider);

leftMapping.AddRange(mappingsGatherer.Gather(provider.Predicate,
provider.Predicate.Parameters[0]));
rightMapping.AddRange(mappingsGatherer.Gather(provider.Predicate,
provider.Predicate.Parameters[1]));
var providerPredicate = provider.Predicate;
var providerPredicateParameters = providerPredicate.Parameters;
leftMapping.AddRange(mappingsGatherer.Gather(providerPredicate,
providerPredicateParameters[0]));
rightMapping.AddRange(mappingsGatherer.Gather(providerPredicate,
providerPredicateParameters[1]));

var newLeftProvider = provider.Left;
var newRightProvider = provider.Right;
VisitJoin(ref leftMapping, ref newLeftProvider, ref rightMapping, ref newRightProvider, false);
mappings[provider] = MergeMappings(provider.Left, leftMapping, rightMapping);
var predicate = TranslateJoinPredicate(leftMapping, rightMapping, provider.Predicate);
var predicate = TranslateJoinPredicate(leftMapping, rightMapping, providerPredicate);

return newLeftProvider == provider.Left && newRightProvider == provider.Right
&& provider.Predicate == predicate
&& providerPredicate == predicate
? provider
: new PredicateJoinProvider(newLeftProvider, newRightProvider, (Expression<Func<Tuple, Tuple, bool>>) predicate, provider.JoinType);
}
Expand Down Expand Up @@ -508,10 +510,11 @@ private Expression TranslateLambda(IReadOnlyList<ColNum> colMap, LambdaExpressio
private Expression TranslateJoinPredicate(IReadOnlyList<ColNum> leftMapping,
IReadOnlyList<ColNum> rightMapping, Expression<Func<Tuple, Tuple, bool>> expression)
{
var expressionParameters = expression.Parameters;
var result = new TupleAccessRewriter(leftMapping, ResolveOuterMapping, true).Rewrite(expression,
expression.Parameters[0]);
expressionParameters[0]);
return new TupleAccessRewriter(rightMapping, ResolveOuterMapping, true).Rewrite(result,
expression.Parameters[1]);
expressionParameters[1]);
}

private void VisitJoin(
Expand Down