Skip to content

Commit 132e13f

Browse files
authored
Optimize AllocateParameterAndReplacer() (#378)
* Optimize `AllocateParameterAndReplacer()` * NoopReplacer * Memoize ClosureTypeInfo * ?. * Optimize IsClosure() * Increase cache size * Remove redundant closureType.DeclaringType null check
1 parent ce33c5e commit 132e13f

File tree

2 files changed

+36
-42
lines changed

2 files changed

+36
-42
lines changed

Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ internal record struct QueryKey(object Key, int MetadataToken, ModuleHandle Modu
1717

1818
internal class CompiledQueryRunner
1919
{
20+
private record struct ClosureTypeInfo(Type ParameterType, PropertyInfo ValueMemberInfo, FieldInfo[] Fields);
21+
22+
private static readonly ExtendedExpressionReplacer NoopReplacer = new(e => e);
23+
2024
private readonly Domain domain;
2125
private readonly Session session;
2226
private readonly QueryEndpoint endpoint;
@@ -152,47 +156,47 @@ private void AllocateParameterAndReplacer()
152156
{
153157
if (queryTarget == null) {
154158
queryParameter = null;
155-
queryParameterReplacer = new ExtendedExpressionReplacer(e => e);
159+
queryParameterReplacer = NoopReplacer;
156160
return;
157161
}
158162

159163
var closureType = queryTarget.GetType();
160-
var parameterType = WellKnownOrmTypes.ParameterOfT.CachedMakeGenericType(closureType);
161-
var valueMemberInfo = parameterType.GetProperty(nameof(Parameter<object>.Value), closureType);
162-
queryParameter = (Parameter) System.Activator.CreateInstance(parameterType, "pClosure");
164+
var info = Memoizer.Get(closureType, static ct => {
165+
var parameterType = WellKnownOrmTypes.ParameterOfT.CachedMakeGenericType(ct);
166+
return new ClosureTypeInfo(
167+
parameterType,
168+
parameterType.GetProperty(nameof(Parameter<object>.Value), ct),
169+
ct.IsClosure() ? ct.GetFields() : null
170+
);
171+
}, 10_000);
172+
MemberExpression closureAccessor = null;
173+
queryParameter = (Parameter) System.Activator.CreateInstance(info.ParameterType, "pClosure");
163174
queryParameterReplacer = new ExtendedExpressionReplacer(expression => {
164175
if (expression.NodeType == ExpressionType.Constant) {
165-
if ((expression as ConstantExpression).Value == null) {
166-
return null;
167-
}
168-
if (expression.Type.IsClosure()) {
169-
if (expression.Type==closureType) {
170-
return Expression.MakeMemberAccess(Expression.Constant(queryParameter, parameterType), valueMemberInfo);
171-
}
172-
else {
173-
throw new NotSupportedException(string.Format(
174-
Strings.ExExpressionDefinedOutsideOfCachingQueryClosure, expression));
175-
}
176+
if (((ConstantExpression)expression).Value is null) {
177+
return null;
176178
}
177179

178-
if (closureType.DeclaringType==null) {
179-
if (expression.Type.IsAssignableFrom(closureType))
180-
return Expression.MakeMemberAccess(Expression.Constant(queryParameter, parameterType), valueMemberInfo);
180+
var expressionType = expression.Type;
181+
if (expressionType.IsClosure()) {
182+
return expressionType == closureType
183+
? GetClosureAccessor()
184+
: throw new NotSupportedException(string.Format(Strings.ExExpressionDefinedOutsideOfCachingQueryClosure, expression));
181185
}
182-
else {
183-
if (expression.Type.IsAssignableFrom(closureType))
184-
return Expression.MakeMemberAccess(Expression.Constant(queryParameter, parameterType), valueMemberInfo);
185-
if (expression.Type.IsAssignableFrom(closureType.DeclaringType)) {
186-
var memberInfo = closureType.TryGetFieldInfoFromClosure(expression.Type);
187-
if (memberInfo != null)
188-
return Expression.MakeMemberAccess(
189-
Expression.MakeMemberAccess(Expression.Constant(queryParameter, parameterType), valueMemberInfo),
190-
memberInfo);
191-
}
186+
187+
if (expressionType.IsAssignableFrom(closureType))
188+
return GetClosureAccessor();
189+
if (expressionType.IsAssignableFrom(closureType.DeclaringType)
190+
&& info.Fields?.FirstOrDefault(field => field.FieldType == expressionType) is { } memberInfo) {
191+
return Expression.MakeMemberAccess(GetClosureAccessor(), memberInfo);
192192
}
193193
}
194+
194195
return null;
195196
});
197+
198+
MemberExpression GetClosureAccessor() =>
199+
closureAccessor ??= Expression.MakeMemberAccess(Expression.Constant(queryParameter, info.ParameterType), info.ValueMemberInfo);
196200
}
197201

198202
private ParameterizedQuery GetCachedQuery() =>

Orm/Xtensive.Orm/Reflection/TypeHelper.cs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,9 +1188,11 @@ public static bool IsAnonymous(this Type type)
11881188
/// </returns>
11891189
public static bool IsClosure(this Type type)
11901190
{
1191+
if (type.BaseType != WellKnownTypes.Object) {
1192+
return false;
1193+
}
11911194
var typeName = type.Name;
1192-
return type.BaseType == WellKnownTypes.Object
1193-
&& (typeName.StartsWith("<>", StringComparison.Ordinal) || typeName.StartsWith("VB$", StringComparison.Ordinal))
1195+
return (typeName.StartsWith("<>", StringComparison.Ordinal) || typeName.StartsWith("VB$", StringComparison.Ordinal))
11941196
&& typeName.IndexOf("DisplayClass", StringComparison.Ordinal) >= 0
11951197
&& type.IsDefined(CompilerGeneratedAttributeType, false);
11961198
}
@@ -1261,18 +1263,6 @@ internal static bool IsValueTuple(this Type type)
12611263

12621264
#region Private \ internal methods
12631265

1264-
/// <summary>
1265-
/// Gets information about field in closure.
1266-
/// </summary>
1267-
/// <param name="closureType">Closure type.</param>
1268-
/// <param name="fieldType">Type of field in closure.</param>
1269-
/// <returns>If field of <paramref name="fieldType"/> exists in closure then returns
1270-
/// <see cref="MemberInfo"/> of that field, otherwise, <see langword="null"/>.</returns>
1271-
internal static MemberInfo TryGetFieldInfoFromClosure(this Type closureType, Type fieldType) =>
1272-
closureType.IsClosure()
1273-
? closureType.GetFields().FirstOrDefault(field => field.FieldType == fieldType)
1274-
: null;
1275-
12761266
private static string TrimGenericSuffix(string @string)
12771267
{
12781268
var backtickPosition = @string.IndexOf('`');

0 commit comments

Comments
 (0)