Skip to content

Commit bdb8f66

Browse files
commit-n-runAlexUstinov
authored andcommitted
LambdaExpressionFactory: call internal factory method to avoid hitting small delegate cache in Expression.CreateLambda()
1 parent b4d0472 commit bdb8f66

2 files changed

Lines changed: 118 additions & 15 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (C) 2003-2010 Xtensive LLC.
2+
// All rights reserved.
3+
// For conditions of distribution and use, see license.
4+
// Created by: Denis Krjuchkov
5+
// Created: 2020.14.02
6+
7+
using System;
8+
using System.Linq.Expressions;
9+
using NUnit.Framework;
10+
using Xtensive.Core;
11+
using Xtensive.Linq;
12+
13+
namespace Xtensive.Orm.Tests.Core.Linq
14+
{
15+
[TestFixture]
16+
public class LambdaExpressionFactoryTests
17+
{
18+
private readonly Type delegateType = typeof(Func<int, int>);
19+
private readonly Expression<Func<int, int>> plusOne = i => i + 1;
20+
21+
[Test]
22+
public void ShouldUseFastFactory() => Assert.That(LambdaExpressionFactory.CanUseFastFactory());
23+
24+
[Test]
25+
public void ShouldCreateFactoryFast()
26+
{
27+
var lambda = (Func<int, int>) LambdaExpressionFactory
28+
.CreateFactoryFast(delegateType)
29+
.Invoke(plusOne.Body, plusOne.Parameters.ToArray())
30+
.Compile();
31+
32+
Assert.That(lambda.Invoke(1), Is.EqualTo(2));
33+
}
34+
35+
[Test]
36+
public void ShouldCreateFactorySlow()
37+
{
38+
var lambda = (Func<int, int>) LambdaExpressionFactory.Instance
39+
.CreateFactorySlow(delegateType)
40+
.Invoke(plusOne.Body, plusOne.Parameters.ToArray())
41+
.Compile();
42+
43+
Assert.That(lambda.Invoke(1), Is.EqualTo(2));
44+
}
45+
46+
[Test]
47+
public void ShouldCreateLambda()
48+
{
49+
var lambda = (Func<int, int>) LambdaExpressionFactory.Instance
50+
.CreateLambda(delegateType, plusOne.Body, plusOne.Parameters.ToArray())
51+
.Compile();
52+
53+
Assert.That(lambda.Invoke(1), Is.EqualTo(2));
54+
}
55+
}
56+
}

Orm/Xtensive.Orm/Linq/Internals/LambdaExpressionFactory.cs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,33 @@
1313
using Xtensive.Reflection;
1414

1515
using Factory = System.Func<
16-
System.Linq.Expressions.Expression,
17-
System.Collections.Generic.IEnumerable<System.Linq.Expressions.ParameterExpression>,
18-
System.Linq.Expressions.LambdaExpression
16+
System.Linq.Expressions.Expression,
17+
System.Linq.Expressions.ParameterExpression[],
18+
System.Linq.Expressions.LambdaExpression
19+
>;
20+
21+
using SlowFactory = System.Func<
22+
System.Linq.Expressions.Expression,
23+
System.Collections.Generic.IEnumerable<System.Linq.Expressions.ParameterExpression>,
24+
System.Linq.Expressions.LambdaExpression
25+
>;
26+
27+
using FastFactory = System.Func<
28+
System.Linq.Expressions.Expression,
29+
string,
30+
bool,
31+
System.Collections.Generic.IReadOnlyList<System.Linq.Expressions.ParameterExpression>,
32+
System.Linq.Expressions.LambdaExpression
1933
>;
2034

2135
namespace Xtensive.Linq
2236
{
2337
internal sealed class LambdaExpressionFactory
2438
{
39+
private static readonly Type[] internalFactorySignature = new[] {
40+
typeof(Expression), typeof(string), typeof(bool), typeof(IReadOnlyList<ParameterExpression>)
41+
};
42+
2543
private static readonly object _lock = new object();
2644
private static volatile LambdaExpressionFactory instance;
2745

@@ -32,27 +50,54 @@ public static LambdaExpressionFactory Instance {
3250
return instance;
3351
}
3452
}
35-
36-
private readonly MethodInfo factoryMethod;
53+
3754
private readonly ThreadSafeDictionary<Type, Factory> cache;
55+
private readonly Func<Type, Factory> createHandler;
56+
private readonly MethodInfo slowFactoryMethod;
3857

3958
public LambdaExpression CreateLambda(Type delegateType, Expression body, ParameterExpression[] parameters)
4059
{
41-
var factory = cache.GetValue(delegateType, (_delegateType, _this) => _this.CreateFactory(_delegateType), this);
60+
var factory = cache.GetValue(delegateType, createHandler);
4261
return factory.Invoke(body, parameters);
4362
}
44-
63+
4564
public LambdaExpression CreateLambda(Expression body, ParameterExpression[] parameters)
4665
{
4766
var delegateType = DelegateHelper.MakeDelegateType(body.Type, parameters.Select(p => p.Type));
4867
return CreateLambda(delegateType, body, parameters);
4968
}
5069

5170
#region Private / internal methods
52-
53-
private Factory CreateFactory(Type delegateType)
71+
72+
internal Factory CreateFactorySlow(Type delegateType)
73+
{
74+
var factory = (SlowFactory) Delegate.CreateDelegate(
75+
typeof(SlowFactory), slowFactoryMethod.MakeGenericMethod(delegateType));
76+
77+
return (body, parameters) => factory.Invoke(body, parameters);
78+
}
79+
80+
internal static Factory CreateFactoryFast(Type delegateType)
5481
{
55-
return (Factory) Delegate.CreateDelegate(typeof (Factory), factoryMethod.MakeGenericMethod(delegateType));
82+
var method = typeof(Expression<>).MakeGenericType(delegateType).GetMethod(
83+
"Create", BindingFlags.Static | BindingFlags.NonPublic, null, internalFactorySignature, null);
84+
85+
if (method == null) {
86+
return null;
87+
}
88+
89+
var factory = (FastFactory) Delegate.CreateDelegate(typeof(FastFactory), null, method);
90+
return (body, parameters) => factory.Invoke(body, null, false, parameters);
91+
}
92+
93+
internal static bool CanUseFastFactory()
94+
{
95+
try {
96+
return CreateFactoryFast(typeof(Func<int>)) != null;
97+
}
98+
catch {
99+
return false;
100+
}
56101
}
57102

58103
#endregion
@@ -62,11 +107,13 @@ private Factory CreateFactory(Type delegateType)
62107
private LambdaExpressionFactory()
63108
{
64109
cache = ThreadSafeDictionary<Type, Factory>.Create(new object());
65-
factoryMethod = typeof (Expression).GetMethods()
66-
.Where(m => m.IsGenericMethod
67-
&& m.Name == "Lambda"
68-
&& m.GetParameters()[1].ParameterType == typeof(IEnumerable<ParameterExpression>))
69-
.Single();
110+
111+
slowFactoryMethod = typeof(Expression).GetMethods().Single(m =>
112+
m.IsGenericMethod &&
113+
m.Name == "Lambda" &&
114+
m.GetParameters()[1].ParameterType == typeof(IEnumerable<ParameterExpression>));
115+
116+
createHandler = CanUseFastFactory() ? (Func<Type, Factory>) CreateFactoryFast : CreateFactorySlow;
70117
}
71118
}
72119
}

0 commit comments

Comments
 (0)