Skip to content

Commit 8b153c8

Browse files
authored
Merge pull request #180 from servicetitan/upstream/ImmutableExpression
Optimization: allocate immutable Expression objects statically;
2 parents a4034a0 + 02ede94 commit 8b153c8

14 files changed

Lines changed: 250 additions & 238 deletions

Orm/Xtensive.Orm/Linq/ConstantExtractor.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2020 Xtensive LLC.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Denis Krjuchkov
@@ -21,9 +21,10 @@ namespace Xtensive.Linq
2121
/// </summary>
2222
public sealed class ConstantExtractor : ExpressionVisitor
2323
{
24+
private static readonly ParameterExpression ConstantParameter = Expression.Parameter(WellKnownTypes.ObjectArray, "constants");
25+
2426
private readonly Func<ConstantExpression, bool> constantFilter;
2527
private readonly LambdaExpression lambda;
26-
private readonly ParameterExpression constantParameter;
2728
private List<object> constantValues;
2829

2930
/// <summary>
@@ -48,7 +49,7 @@ public LambdaExpression Process()
4849
if (constantValues != null)
4950
throw new InvalidOperationException();
5051
constantValues = new List<object>();
51-
var parameters = EnumerableUtils.One(constantParameter).Concat(lambda.Parameters).ToArray();
52+
var parameters = EnumerableUtils.One(ConstantParameter).Concat(lambda.Parameters).ToArray();
5253
var body = Visit(lambda.Body);
5354
// Preserve original delegate type because it may differ from types of parameters / return value
5455
return FastExpression.Lambda(FixDelegateType(lambda.Type), body, parameters);
@@ -60,7 +61,7 @@ protected override Expression VisitConstant(ConstantExpression c)
6061
if (!constantFilter.Invoke(c))
6162
return c;
6263
var result = Expression.Convert(
63-
Expression.ArrayIndex(constantParameter, Expression.Constant(constantValues.Count)), c.Type);
64+
Expression.ArrayIndex(ConstantParameter, Expression.Constant(constantValues.Count)), c.Type);
6465
constantValues.Add(c.Value);
6566
return result;
6667
}
@@ -70,7 +71,7 @@ protected override Expression VisitConstant(ConstantExpression c)
7071
private Type FixDelegateType(Type delegateType)
7172
{
7273
var signature = DelegateHelper.GetDelegateSignature(delegateType);
73-
return DelegateHelper.MakeDelegateType(signature.First, signature.Second.Prepend(constantParameter.Type));
74+
return DelegateHelper.MakeDelegateType(signature.First, signature.Second.Prepend(ConstantParameter.Type));
7475
}
7576

7677
private static bool DefaultConstantFilter(ConstantExpression constant)
@@ -106,7 +107,6 @@ public ConstantExtractor(LambdaExpression lambda, Func<ConstantExpression, bool>
106107
ArgumentValidator.EnsureArgumentNotNull(lambda, "lambda");
107108
this.lambda = lambda;
108109
this.constantFilter = constantFilter ?? DefaultConstantFilter;
109-
constantParameter = Expression.Parameter(WellKnownTypes.ObjectArray, "constants");
110110
}
111111
}
112112
}

Orm/Xtensive.Orm/Orm/Building/Builders/PartialIndexFilterBuilder.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2011-2020 Xtensive LLC.
1+
// Copyright (C) 2011-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Denis Krjuchkov
@@ -25,21 +25,21 @@ namespace Xtensive.Orm.Building.Builders
2525
{
2626
internal class PartialIndexFilterBuilder : ExpressionVisitor
2727
{
28+
private static readonly ParameterExpression Parameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "tuple");
29+
2830
private readonly TypeInfo declaringType;
2931
private readonly TypeInfo reflectedType;
3032
private readonly IndexInfo index;
31-
private readonly ParameterExpression parameter;
3233
private readonly List<FieldInfo> usedFields = new List<FieldInfo>();
3334
private readonly Dictionary<Expression, FieldInfo> entityAccessMap = new Dictionary<Expression, FieldInfo>();
3435

3536
public static void BuildFilter(IndexInfo index)
3637
{
3738
ArgumentValidator.EnsureArgumentNotNull(index, "index");
38-
var parameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "tuple");
39-
var builder = new PartialIndexFilterBuilder(index, parameter);
39+
var builder = new PartialIndexFilterBuilder(index);
4040
var body = builder.Visit(index.FilterExpression.Body);
4141
var filter = new PartialIndexFilterInfo {
42-
Expression = FastExpression.Lambda(body, parameter),
42+
Expression = FastExpression.Lambda(body, Parameter),
4343
Fields = builder.usedFields,
4444
};
4545
index.Filter = filter;
@@ -112,7 +112,7 @@ private Expression BuildFieldAccess(FieldInfo field, bool addNullability)
112112
var fieldIndex = usedFields.Count;
113113
var valueType = addNullability ? field.ValueType.ToNullable() : field.ValueType;
114114
usedFields.Add(field);
115-
return Expression.Call(parameter,
115+
return Expression.Call(Parameter,
116116
WellKnownMembers.Tuple.GenericAccessor.MakeGenericMethod(valueType),
117117
Expression.Constant(fieldIndex));
118118
}
@@ -174,10 +174,9 @@ private DomainBuilderException UnableToTranslate(Expression expression)
174174
return UnableToTranslate(expression, string.Format(Strings.ExpressionsOfTypeXAreNotSupported, expression.NodeType));
175175
}
176176

177-
private PartialIndexFilterBuilder(IndexInfo index, ParameterExpression parameter)
177+
private PartialIndexFilterBuilder(IndexInfo index)
178178
{
179179
this.index = index;
180-
this.parameter = parameter;
181180

182181
declaringType = index.DeclaringType;
183182
reflectedType = index.ReflectedType;

Orm/Xtensive.Orm/Orm/EntitySet{T}.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public class EntitySet<TItem> : EntitySetBase,
6262
IQueryable<TItem>
6363
where TItem : IEntity
6464
{
65+
private static readonly MemberExpression OwnerPropertyExpression = Expression.Property(Expression.Constant(ownerParameter), ownerParameter.GetType()
66+
.GetProperty("Value", WellKnownOrmTypes.Entity));
67+
6568
private Expression expression;
6669

6770
/// <summary>
@@ -230,9 +233,7 @@ protected sealed override Func<QueryEndpoint,Int64> GetItemCountQueryDelegate(Fi
230233

231234
private static IQueryable<TItem> GetItemsQuery(QueryEndpoint qe, FieldInfo field)
232235
{
233-
var owner = Expression.Property(Expression.Constant(ownerParameter), ownerParameter.GetType()
234-
.GetProperty("Value", WellKnownOrmTypes.Entity));
235-
var queryExpression = QueryHelper.CreateEntitySetQuery(owner, field);
236+
var queryExpression = QueryHelper.CreateEntitySetQuery(OwnerPropertyExpression, field);
236237
return qe.Provider.CreateQuery<TItem>(queryExpression);
237238
}
238239

Orm/Xtensive.Orm/Orm/Linq/Expressions/Visitors/IncludeFilterMappingGatherer.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2020 Xtensive LLC.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Gamzov
@@ -34,7 +34,7 @@ public MappingEntry(LambdaExpression calculatedColumn)
3434
}
3535
}
3636

37-
private readonly ParameterExpression calculatedColumnParameter;
37+
private static readonly ParameterExpression CalculatedColumnParameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "filteredRow");
3838

3939
private readonly Expression filterDataTuple;
4040
private readonly ApplyParameter filteredTuple;
@@ -85,7 +85,7 @@ protected override Expression VisitMemberAccess(MemberExpression m)
8585
}
8686

8787
if (target.NodeType == ExpressionType.Constant && ((ConstantExpression) target).Value == filteredTuple) {
88-
return calculatedColumnParameter;
88+
return CalculatedColumnParameter;
8989
}
9090
return base.VisitMemberAccess(m);
9191
}
@@ -96,14 +96,12 @@ private MappingEntry CreateMappingEntry(Expression expression)
9696
if (tupleAccess != null) {
9797
return new MappingEntry(tupleAccess.GetTupleAccessArgument());
9898
}
99-
expression = ExpressionReplacer.Replace(expression, filterDataTuple, calculatedColumnParameter);
100-
return new MappingEntry(FastExpression.Lambda(expression, calculatedColumnParameter));
99+
expression = ExpressionReplacer.Replace(expression, filterDataTuple, CalculatedColumnParameter);
100+
return new MappingEntry(FastExpression.Lambda(expression, CalculatedColumnParameter));
101101
}
102102

103103
private IncludeFilterMappingGatherer(Expression filterDataTuple, ApplyParameter filteredTuple, MappingEntry[] resultMapping)
104104
{
105-
calculatedColumnParameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "filteredRow");
106-
107105
this.filterDataTuple = filterDataTuple;
108106
this.filteredTuple = filteredTuple;
109107
this.resultMapping = resultMapping;

Orm/Xtensive.Orm/Orm/Linq/ItemToTupleConverter{TItem}.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2020 Xtensive LLC.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Gamzov
@@ -52,6 +52,9 @@ public void AddRange(IReadOnlyCollection<Type> newTypes)
5252
}
5353
}
5454

55+
private static readonly ParameterExpression ParamContext = Expression.Parameter(WellKnownOrmTypes.ParameterContext, "context");
56+
private static readonly MethodInfo SelectMethod = WellKnownMembers.Enumerable.Select.MakeGenericMethod(typeof(TItem), WellKnownOrmTypes.Tuple);
57+
5558
private readonly Func<ParameterContext, IEnumerable<TItem>> enumerableFunc;
5659
private readonly DomainModel model;
5760
private Func<TItem, Tuple> converter;
@@ -61,11 +64,9 @@ public void AddRange(IReadOnlyCollection<Type> newTypes)
6164

6265
public override Expression<Func<ParameterContext, IEnumerable<Tuple>>> GetEnumerable()
6366
{
64-
var paramContext = Expression.Parameter(WellKnownOrmTypes.ParameterContext, "context");
65-
var call = Expression.Call(Expression.Constant(enumerableFunc.Target), enumerableFunc.Method, paramContext);
66-
var selectMethod = WellKnownMembers.Enumerable.Select.MakeGenericMethod(typeof (TItem), WellKnownOrmTypes.Tuple);
67-
var select = Expression.Call(selectMethod, call, Expression.Constant(converter));
68-
return FastExpression.Lambda<Func<ParameterContext, IEnumerable<Tuple>>>(select, paramContext);
67+
var call = Expression.Call(Expression.Constant(enumerableFunc.Target), enumerableFunc.Method, ParamContext);
68+
var select = Expression.Call(SelectMethod, call, Expression.Constant(converter));
69+
return FastExpression.Lambda<Func<ParameterContext, IEnumerable<Tuple>>>(select, ParamContext);
6970
}
7071

7172

Orm/Xtensive.Orm/Orm/Linq/Materialization/ExpressionMaterializer.cs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ internal class ExpressionMaterializer : PersistentExpressionVisitor
3333
private static readonly MethodInfo GetParameterValueMethod;
3434
private static readonly PropertyInfo ParameterContextProperty;
3535
private static readonly MethodInfo GetTupleParameterValueMethod;
36+
private static readonly ParameterExpression TupleParameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "tuple");
37+
private static readonly ParameterExpression MaterializationContextParameter = Expression.Parameter(WellKnownOrmTypes.ItemMaterializationContext, "mc");
38+
private static readonly ConstantExpression TypeReferenceAccuracyConstantExpression = Expression.Constant(TypeReferenceAccuracy.BaseType);
3639

3740
private readonly TranslatorContext context;
38-
private readonly ParameterExpression tupleParameter;
3941
private readonly ParameterExpression itemMaterializationContextParameter;
4042
private readonly Dictionary<IEntityExpression, int> entityRegistry = new Dictionary<IEntityExpression, int>();
4143
private readonly HashSet<Parameter<Tuple>> tupleParameters;
@@ -52,19 +54,16 @@ internal class ExpressionMaterializer : PersistentExpressionVisitor
5254

5355
public static LambdaExpression MakeLambda(Expression expression, TranslatorContext context)
5456
{
55-
var tupleParameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "tuple");
56-
var visitor = new ExpressionMaterializer(tupleParameter, context, null, EnumerableUtils<Parameter<Tuple>>.Empty);
57+
var visitor = new ExpressionMaterializer(context, null, Enumerable.Empty<Parameter<Tuple>>());
5758
var processedExpression = OwnerRemover.RemoveOwner(expression);
58-
return FastExpression.Lambda(visitor.Visit(processedExpression), tupleParameter);
59+
return FastExpression.Lambda(visitor.Visit(processedExpression), TupleParameter);
5960
}
6061

61-
public static MaterializationInfo MakeMaterialization(ItemProjectorExpression projector, TranslatorContext context,
62+
public static MaterializationInfo MakeMaterialization(ItemProjectorExpression projector, TranslatorContext context,
6263
IEnumerable<Parameter<Tuple>> tupleParameters)
6364
{
64-
var tupleParameter = Expression.Parameter(WellKnownOrmTypes.Tuple, "tuple");
65-
var materializationContextParameter = Expression.Parameter(WellKnownOrmTypes.ItemMaterializationContext, "mc");
66-
var visitor = new ExpressionMaterializer(tupleParameter, context, materializationContextParameter, tupleParameters);
67-
var lambda = FastExpression.Lambda(visitor.Visit(projector.Item), tupleParameter, materializationContextParameter);
65+
var visitor = new ExpressionMaterializer(context, MaterializationContextParameter, tupleParameters);
66+
var lambda = FastExpression.Lambda(visitor.Visit(projector.Item), TupleParameter, MaterializationContextParameter);
6867
var count = visitor.entityRegistry.Count;
6968
return new MaterializationInfo(count, lambda);
7069
}
@@ -96,7 +95,7 @@ protected override Expression VisitMarker(MarkerExpression expression)
9695
if (itemMaterializationContextParameter == null)
9796
return processedTarget;
9897
var columns = ColumnGatherer.GetColumns(target, ColumnExtractionModes.Distinct | ColumnExtractionModes.Ordered).ToArray();
99-
var sequenceCheck = Expression.Call(MaterializationHelper.IsNullMethodInfo, tupleParameter, Expression.Constant(columns));
98+
var sequenceCheck = Expression.Call(MaterializationHelper.IsNullMethodInfo, TupleParameter, Expression.Constant(columns));
10099
var throwException = Expression.Convert(Expression.Call(MaterializationHelper.ThrowEmptySequenceExceptionMethodInfo), target.Type);
101100
return Expression.Condition(sequenceCheck, throwException, processedTarget);
102101
}
@@ -129,7 +128,7 @@ protected override Expression VisitGroupingExpression(GroupingExpression groupin
129128
Expression.Constant(projection),
130129
Expression.Constant(translatedQuery),
131130
Expression.Constant(parameterOfTuple),
132-
tupleParameter,
131+
TupleParameter,
133132
keyMaterializer,
134133
itemMaterializationContextParameter);
135134

@@ -154,7 +153,7 @@ protected override Expression VisitSubQueryExpression(SubQueryExpression subQuer
154153
Expression.Constant(projection),
155154
Expression.Constant(translatedQuery),
156155
Expression.Constant(parameterOfTuple),
157-
tupleParameter,
156+
TupleParameter,
158157
itemMaterializationContextParameter);
159158

160159
return Expression.Convert(resultExpression, subQueryExpression.Type);
@@ -184,15 +183,15 @@ private TranslatedQuery PrepareSubqueryParameters(SubQueryExpression subQueryExp
184183
var itemProjector = new ItemProjectorExpression(newItemProjectorBody, newDataSource, subQueryExpression.ProjectionExpression.ItemProjector.Context);
185184
parameterOfTuple = context.GetTupleParameter(subQueryExpression.OuterParameter);
186185

187-
// 2. Add only parameter<tuple>. Tuple value will be assigned
186+
// 2. Add only parameter<tuple>. Tuple value will be assigned
188187
// at the moment of materialization in SubQuery constructor
189188
projection = new ProjectionExpression(
190189
subQueryExpression.ProjectionExpression.Type,
191190
itemProjector,
192191
subQueryExpression.ProjectionExpression.TupleParameterBindings,
193192
subQueryExpression.ProjectionExpression.ResultAccessMethod);
194193

195-
// 3. Make translation
194+
// 3. Make translation
196195
elementType = subQueryExpression.ProjectionExpression.ItemProjector.Item.Type;
197196
var translateMethod = Translator.TranslateMethod;
198197
return (TranslatedQuery) translateMethod.Invoke(
@@ -278,8 +277,8 @@ protected override Expression VisitConstructorExpression(ConstructorExpression e
278277

279278
var realBindings = expression.NativeBindings;
280279

281-
return expression.NativeBindings.Count == 0
282-
? newExpression
280+
return expression.NativeBindings.Count == 0
281+
? newExpression
283282
: (Expression) Expression.MemberInit(newExpression, expression
284283
.NativeBindings
285284
.Where(item => Translator.FilterBindings(item.Key, item.Key.Name, item.Value.Type))
@@ -330,7 +329,7 @@ protected override Expression VisitKeyExpression(KeyExpression expression)
330329
Expression.Field(itemMaterializationContextParameter, ItemMaterializationContext.SessionFieldInfo),
331330
WellKnownMembers.SessionNodeId),
332331
Expression.Constant(expression.EntityType),
333-
Expression.Constant(TypeReferenceAccuracy.BaseType),
332+
TypeReferenceAccuracyConstantExpression,
334333
tupleExpression);
335334
}
336335

@@ -507,7 +506,7 @@ private Expression MaterializeThroughOwner(Expression target, Expression tuple,
507506
private Expression GetTupleExpression(ParameterizedExpression expression)
508507
{
509508
if (expression.OuterParameter == null)
510-
return tupleParameter;
509+
return TupleParameter;
511510

512511
var parameterOfTuple = context.GetTupleParameter(expression.OuterParameter);
513512
if (tupleParameters.Contains(parameterOfTuple)) {
@@ -525,7 +524,7 @@ private Expression GetTupleExpression(ParameterizedExpression expression)
525524
return Expression.Property(applyParameterExpression, WellKnownMembers.ApplyParameterValue);
526525
}
527526

528-
return tupleParameter;
527+
return TupleParameter;
529528
}
530529

531530
private static Tuple BuildPersistentTuple(Tuple tuple, Tuple tuplePrototype, int[] mapping)
@@ -541,14 +540,12 @@ private static Tuple BuildPersistentTuple(Tuple tuple, Tuple tuplePrototype, int
541540

542541
// Constructors
543542

544-
private ExpressionMaterializer(ParameterExpression
545-
tupleParameter,
543+
private ExpressionMaterializer(
546544
TranslatorContext context,
547545
ParameterExpression itemMaterializationContextParameter,
548546
IEnumerable<Parameter<Tuple>> tupleParameters)
549547
{
550548
this.itemMaterializationContextParameter = itemMaterializationContextParameter;
551-
this.tupleParameter = tupleParameter;
552549
this.context = context;
553550
this.tupleParameters = new HashSet<Parameter<Tuple>>(tupleParameters);
554551
}

Orm/Xtensive.Orm/Orm/Linq/ParameterAccessorFactory.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2020 Xtensive LLC.
1+
// Copyright (C) 2020-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44

@@ -18,6 +18,8 @@ internal static class ParameterAccessorFactory
1818
private static readonly MethodInfo GetParameterValueMethod =
1919
WellKnownOrmTypes.ParameterContext.GetMethod(nameof(ParameterContext.GetValue));
2020

21+
private static readonly ParameterExpression ParameterContextArgument = Expression.Parameter(WellKnownOrmTypes.ParameterContext, "context");
22+
2123
private class ParameterAccessorFactoryImpl<T>: ExpressionVisitor
2224
{
2325
private readonly ParameterExpression parameterContextArgument;
@@ -55,8 +57,7 @@ public ParameterAccessorFactoryImpl(ParameterExpression parameterContextArgument
5557

5658
public static Expression<Func<ParameterContext, T>> CreateAccessorExpression<T>(Expression parameterExpression)
5759
{
58-
var parameterContextArgument = Expression.Parameter(WellKnownOrmTypes.ParameterContext, "context");
59-
return new ParameterAccessorFactoryImpl<T>(parameterContextArgument).BindToParameterContext(parameterExpression);
60+
return new ParameterAccessorFactoryImpl<T>(ParameterContextArgument).BindToParameterContext(parameterExpression);
6061
}
6162
}
6263
}

0 commit comments

Comments
 (0)