Skip to content

Commit 77e8b0a

Browse files
authored
PERF-392: Avoid closure allocation in .GetCompiler() (#419)
* PERF-392: Avoid closure allocation in `.GetCompiler()` * allow null compiler * Fix NonPublicGetterTest * Remove public
1 parent 49c16f6 commit 77e8b0a

File tree

5 files changed

+44
-38
lines changed

5 files changed

+44
-38
lines changed

Orm/Xtensive.Orm.Tests/Linq/MemberCompilerProviderTest.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public partial class MemberCompilerProviderTest
2020
{
2121
private readonly string[] dummy = new string[10];
2222

23-
private static Func<string, string[],string> GetCompilerForMethod(
23+
private static IMemberCompilerProvider<string>.BoundCompiler GetCompilerForMethod(
2424
IMemberCompilerProvider<string> provider, Type source, string methodName)
2525
{
2626
if (source.IsGenericTypeDefinition)
@@ -31,23 +31,23 @@ private static Func<string, string[],string> GetCompilerForMethod(
3131
if (mi.IsGenericMethodDefinition)
3232
mi = mi.MakeGenericMethod(typeof(object));
3333
var result = provider.GetCompiler(mi);
34-
Assert.IsNotNull(result);
34+
Assert.IsFalse(result.IsNull);
3535
return result;
3636
}
3737

38-
private static Func<string, string[], string> GetCompilerForCtor(
38+
private static IMemberCompilerProvider<string>.BoundCompiler GetCompilerForCtor(
3939
IMemberCompilerProvider<string> provider, Type source)
4040
{
4141
if (source.IsGenericTypeDefinition)
4242
source = source.MakeGenericType(typeof(object));
4343

4444
var ci = source.GetConstructors().First();
4545
var result = provider.GetCompiler(ci);
46-
Assert.IsNotNull(result);
46+
Assert.IsFalse(result.IsNull);
4747
return result;
4848
}
4949

50-
private static Func<string, string[], string> GetCompilerForField(
50+
private static IMemberCompilerProvider<string>.BoundCompiler GetCompilerForField(
5151
IMemberCompilerProvider<string> provider, Type source, string fieldName)
5252
{
5353
if (source.IsGenericTypeDefinition)
@@ -56,7 +56,7 @@ private static Func<string, string[], string> GetCompilerForField(
5656
var fi = source.GetField(fieldName);
5757
Assert.IsNotNull(fi);
5858
var result = provider.GetCompiler(fi);
59-
Assert.IsNotNull(result);
59+
Assert.IsFalse(result.IsNull);
6060
return result;
6161
}
6262

@@ -71,7 +71,7 @@ public void MethodsTest()
7171
foreach (string s2 in new[]{"Generic", "NonGeneric"}) {
7272
string method = s1 + s2 + "Method";
7373
var d = GetCompilerForMethod(provider, t, method);
74-
Assert.AreEqual(t.Name + "." + method, d(null, dummy));
74+
Assert.AreEqual(t.Name + "." + method, d.Invoke(null, dummy));
7575
}
7676
}
7777

@@ -86,7 +86,7 @@ public void PropertiesTest()
8686
foreach (string s2 in new[] { "InstanceProperty", "StaticProperty", "Item" }) {
8787
string method = s1 + s2;
8888
var d = GetCompilerForMethod(provider, t, method);
89-
Assert.AreEqual(t.Name + "." + method, d(null, dummy));
89+
Assert.AreEqual(t.Name + "." + method, d.Invoke(null, dummy));
9090
}
9191
}
9292

@@ -99,7 +99,7 @@ public void FieldsTest()
9999
foreach (var t in new[]{typeof(NonGenericTarget), typeof(GenericTarget<>)})
100100
foreach (string s in new[] {"InstanceField", "StaticField"}) {
101101
var d = GetCompilerForField(provider, t, s);
102-
Assert.AreEqual(t.Name + "." + s, d(null, dummy));
102+
Assert.AreEqual(t.Name + "." + s, d.Invoke(null, dummy));
103103
}
104104
}
105105

@@ -110,7 +110,7 @@ public void CtorsTest()
110110
provider.RegisterCompilers(typeof(CtorCompiler));
111111
foreach (var t in new[]{typeof(NonGenericTarget), typeof(GenericTarget<>)}) {
112112
var d = GetCompilerForCtor(provider, t);
113-
Assert.AreEqual(t.Name + Reflection.WellKnown.CtorName, d(null, dummy));
113+
Assert.AreEqual(t.Name + Reflection.WellKnown.CtorName, d.Invoke(null, dummy));
114114
}
115115
}
116116

@@ -128,8 +128,8 @@ public void GenericFindTest()
128128
.MakeGenericMethod(typeof(string));
129129

130130
var d = provider.GetCompiler(mi);
131-
Assert.IsNotNull(d);
132-
Assert.AreEqual("OK", d(null, dummy));
131+
Assert.IsFalse(d.IsNull);
132+
Assert.AreEqual("OK", d.Invoke(null, dummy));
133133
}
134134

135135
[Test]
@@ -160,7 +160,7 @@ public void ConflictKeepOldTest()
160160
provider.RegisterCompilers(typeof(ConflictCompiler1));
161161
provider.RegisterCompilers(typeof(ConflictCompiler2), ConflictHandlingMethod.KeepOld);
162162
var d = GetCompilerForMethod(provider, typeof(ConflictTarget), "ConflictMethod");
163-
Assert.AreEqual("Compiler1", d(null, dummy));
163+
Assert.AreEqual("Compiler1", d.Invoke(null, dummy));
164164
}
165165

166166
[Test]
@@ -170,7 +170,7 @@ public void ConflictOverwriteTest()
170170
provider.RegisterCompilers(typeof(ConflictCompiler1));
171171
provider.RegisterCompilers(typeof(ConflictCompiler2), ConflictHandlingMethod.Overwrite);
172172
var d = GetCompilerForMethod(provider, typeof(ConflictTarget), "ConflictMethod");
173-
Assert.AreEqual("Compiler2", d(null, dummy));
173+
Assert.AreEqual("Compiler2", d.Invoke(null, dummy));
174174
}
175175

176176
[Test]
@@ -189,7 +189,7 @@ public void NonPublicGetterTest()
189189
.GetProperty("InternalProperty", BindingFlags.Instance | BindingFlags.NonPublic);
190190
Assert.IsNotNull(property);
191191
var result = provider.GetCompiler(property);
192-
Assert.IsNull(result);
192+
Assert.IsTrue(result.IsNull);
193193
}
194194

195195
[Test]

Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/Interfaces/IMemberCompilerProvider{T}.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
// Created by: Denis Krjuchkov
55
// Created: 2009.02.09
66

7-
using System;
8-
using System.Collections.Generic;
97
using System.Reflection;
108

119
namespace Xtensive.Orm.Linq.MemberCompilation
@@ -36,12 +34,19 @@ public interface IMemberCompilerProvider<T> : IMemberCompilerProvider
3634
/// <param name="conflictHandlingMethod">Conflict handling method.</param>
3735
void RegisterCompilers(IEnumerable<KeyValuePair<MemberInfo, Func<MemberInfo, T, T[], T>>> compilerDefinitions, ConflictHandlingMethod conflictHandlingMethod);
3836

37+
readonly struct BoundCompiler(Func<MemberInfo, T, T[], T> compiler, MemberInfo memberInfo)
38+
{
39+
public bool IsNull => compiler == null;
40+
41+
public T Invoke(T arg2, T[] arg3) => compiler(memberInfo, arg2, arg3);
42+
}
43+
3944
/// <summary>
4045
/// Finds compiler for specified <see cref="MemberInfo"/>.
4146
/// </summary>
4247
/// <param name="target"><see cref="MemberInfo"/> to search compiler for.</param>
4348
/// <returns>compiler associated with <see cref="MethodInfo"/>
4449
/// or <see langword="null"/> if compiler is not found.</returns>
45-
Func<T, T[], T> GetCompiler(MemberInfo target);
50+
BoundCompiler GetCompiler(MemberInfo target);
4651
}
4752
}

Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
// Created by: Denis Krjuchkov
55
// Created: 2009.02.09
66

7-
using System;
8-
using System.Collections.Generic;
9-
using System.Linq;
7+
using System.Collections.Frozen;
108
using System.Reflection;
119
using Xtensive.Core;
1210
using Xtensive.Reflection;
@@ -36,24 +34,25 @@ public CompilerKey(MemberInfo memberInfo)
3634
}
3735
}
3836

39-
private readonly Dictionary<CompilerKey, Delegate> compilers
40-
= new Dictionary<CompilerKey, Delegate>();
37+
private IDictionary<CompilerKey, Delegate> compilers = new Dictionary<CompilerKey, Delegate>();
4138

4239
public Type ExpressionType => typeof(T);
4340

44-
public Delegate GetUntypedCompiler(MemberInfo target)
41+
public override void Lock(bool recursive)
4542
{
46-
ArgumentNullException.ThrowIfNull(target);
47-
48-
return compilers.GetValueOrDefault(GetCompilerKey(target));
43+
base.Lock(recursive);
44+
compilers = compilers.ToFrozenDictionary();
4945
}
5046

51-
public Func<T, T[], T> GetCompiler(MemberInfo target)
47+
public Delegate GetUntypedCompiler(MemberInfo target)
5248
{
53-
var compiler = (Func<MemberInfo, T, T[], T>) GetUntypedCompiler(target);
54-
return compiler.Bind(target);
49+
ArgumentNullException.ThrowIfNull(target);
50+
return compilers.TryGetValue(GetCompilerKey(target), out var v) ? v : null;
5551
}
5652

53+
public IMemberCompilerProvider<T>.BoundCompiler GetCompiler(MemberInfo target) =>
54+
new((Func<MemberInfo, T, T[], T>) GetUntypedCompiler(target), target);
55+
5756
public void RegisterCompilers(Type compilerContainer)
5857
{
5958
RegisterCompilers(compilerContainer, ConflictHandlingMethod.Default);

Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -361,18 +361,18 @@ protected override Expression VisitMember(MemberExpression ma)
361361
// Reflected type doesn't have custom compiler defined, so falling back to base class compiler
362362
var declaringType = memberInfo.DeclaringType;
363363
var reflectedType = memberInfo.ReflectedType;
364-
if (customCompiler == null && declaringType != reflectedType && declaringType.IsAssignableFrom(reflectedType)) {
364+
if (customCompiler.IsNull && declaringType != reflectedType && declaringType.IsAssignableFrom(reflectedType)) {
365365
var root = declaringType;
366366
var current = reflectedType;
367-
while (current != root && customCompiler == null) {
367+
while (current != root && customCompiler.IsNull) {
368368
current = current.BaseType;
369369
var member = current.GetProperty(memberInfo.Name, BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
370370
customCompiler = context.CustomCompilerProvider.GetCompiler(member);
371371
}
372372
}
373373

374-
if (customCompiler != null) {
375-
var expression = customCompiler.Invoke(sourceExpression, Array.Empty<Expression>());
374+
if (!customCompiler.IsNull) {
375+
var expression = customCompiler.Invoke(sourceExpression, []);
376376
if (expression == null) {
377377
if (reflectedType.IsInterface)
378378
return Visit(BuildInterfaceExpression(ma));
@@ -435,7 +435,7 @@ protected override Expression VisitMethodCall(MethodCallExpression mc)
435435
using (CreateScope(new TranslatorState(State) { IsTailMethod = mc == context.Query && mc.IsQuery() })) {
436436
var method = mc.Method;
437437
var customCompiler = context.CustomCompilerProvider.GetCompiler(method);
438-
if (customCompiler != null) {
438+
if (!customCompiler.IsNull) {
439439
return Visit(customCompiler.Invoke(mc.Object, mc.Arguments.ToArray()));
440440
}
441441

@@ -1841,7 +1841,7 @@ private Expression BuildExpression(MemberExpression ma, IEnumerable<PropertyInfo
18411841
Expression current = Expression.Constant(defaultValue, propertyType);
18421842
foreach (var field in fields) {
18431843
var compiler = context.CustomCompilerProvider.GetCompiler(field);
1844-
if (compiler == null)
1844+
if (compiler.IsNull)
18451845
continue;
18461846
var expression = compiler.Invoke(Expression.TypeAs(ma.Expression, field.ReflectedType), null);
18471847
current = Expression.Condition(Expression.TypeIs(ma.Expression, field.ReflectedType), expression, current);

Orm/Xtensive.Orm/Orm/Providers/Expressions/ExpressionProcessor.Helpers.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,10 @@ private static SqlExpression OracleBlobCompare(SqlExpression left, SqlExpression
201201

202202
private SqlExpression CompileMember(MemberInfo member, SqlExpression instance, params SqlExpression[] arguments)
203203
{
204-
var memberCompiler = memberCompilerProvider.GetCompiler(member)
205-
?? throw new NotSupportedException(string.Format(Strings.ExMemberXIsNotSupported, member.GetFullName(true)));
204+
var memberCompiler = memberCompilerProvider.GetCompiler(member);
205+
if (memberCompiler.IsNull) {
206+
throw new NotSupportedException(string.Format(Strings.ExMemberXIsNotSupported, member.GetFullName(true)));
207+
}
206208
return memberCompiler.Invoke(instance, arguments);
207209
}
208210

0 commit comments

Comments
 (0)