Skip to content

Commit aa23c98

Browse files
authored
⚡Improve performance (#349)
* Custom functions resolution: only fetch the delegate's Invoke method if needed * Delay exception creation to when it's needed * Use Set.UnionWith * Rework the way nullable value types are handled * No longer rely on reflection to promote operators operands * No longer use Linq on the critical resolution path * Add expression conversion if its type is assignable to the target * Avoid array instantiation * Avoid allocations by introducing FindUsedOverload * Avoid allocations * Avoid char alloc when not needed * Avoid allocating new strings if not needed
1 parent 29167bb commit aa23c98

File tree

12 files changed

+370
-221
lines changed

12 files changed

+370
-221
lines changed

DynamicExpresso.sln

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.27130.2010
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.13.35806.99
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicExpresso.UnitTest", "test\DynamicExpresso.UnitTest\DynamicExpresso.UnitTest.csproj", "{33157A92-C6B2-4A51-8262-1FEBFD6558BE}"
77
EndProject

src/DynamicExpresso.Core/Identifier.cs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq.Expressions;
44
using System.Reflection;
5+
using DynamicExpresso.Reflection;
56

67
namespace DynamicExpresso
78
{
@@ -41,9 +42,44 @@ internal void AddOverload(Delegate overload)
4142
/// </summary>
4243
internal class MethodGroupExpression : Expression
4344
{
44-
private readonly List<Delegate> _overloads = new List<Delegate>();
45+
public class Overload
46+
{
47+
public Delegate Delegate { get; }
48+
49+
private MethodData _methodData;
50+
public MethodData Method
51+
{
52+
get
53+
{
54+
if (_methodData == null)
55+
_methodData = MethodData.Gen(Delegate.Method);
56+
57+
return _methodData;
58+
}
59+
}
60+
61+
// we'll most likely never need this: it was needed before https://github.com/dotnet/roslyn/pull/53402
62+
private MethodData _invokeMethod;
63+
public MethodData InvokeMethod
64+
{
65+
get
66+
{
67+
if (_invokeMethod == null)
68+
_invokeMethod = MemberFinder.FindInvokeMethod(Delegate.GetType());
69+
70+
return _invokeMethod;
71+
}
72+
}
73+
74+
public Overload(Delegate @delegate)
75+
{
76+
Delegate = @delegate;
77+
}
78+
}
4579

46-
internal IReadOnlyCollection<Delegate> Overloads
80+
private readonly List<Overload> _overloads = new List<Overload>();
81+
82+
internal IReadOnlyCollection<Overload> Overloads
4783
{
4884
get
4985
{
@@ -60,12 +96,12 @@ internal void AddOverload(Delegate overload)
6096
{
6197
// remove any existing delegate with the exact same signature
6298
RemoveDelegateSignature(overload);
63-
_overloads.Add(overload);
99+
_overloads.Add(new Overload(overload));
64100
}
65101

66102
private void RemoveDelegateSignature(Delegate overload)
67103
{
68-
_overloads.RemoveAll(del => HasSameSignature(overload.Method, del.Method));
104+
_overloads.RemoveAll(del => HasSameSignature(overload.Method, del.Delegate.Method));
69105
}
70106

71107
private static bool HasSameSignature(MethodInfo method, MethodInfo other)
@@ -88,5 +124,29 @@ private static bool HasSameSignature(MethodInfo method, MethodInfo other)
88124

89125
return true;
90126
}
127+
128+
/// <summary>
129+
/// The resolution process will find the best overload for the given arguments,
130+
/// which we then need to match to the correct delegate.
131+
/// </summary>
132+
internal Delegate FindUsedOverload(bool usedInvokeMethod, MethodData methodData)
133+
{
134+
foreach (var overload in _overloads)
135+
{
136+
if (usedInvokeMethod)
137+
{
138+
if (overload.InvokeMethod.MethodBase == methodData.MethodBase)
139+
return overload.Delegate;
140+
}
141+
else
142+
{
143+
if (overload.Method.MethodBase == methodData.MethodBase)
144+
return overload.Delegate;
145+
}
146+
}
147+
148+
// this should never happen
149+
throw new InvalidOperationException("No overload matches the method");
150+
}
91151
}
92152
}

src/DynamicExpresso.Core/Interpreter.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
using DynamicExpresso.Parsing;
2-
using DynamicExpresso.Reflection;
3-
using DynamicExpresso.Visitors;
41
using System;
52
using System.Collections.Generic;
63
using System.Linq;
74
using System.Linq.Expressions;
85
using DynamicExpresso.Exceptions;
6+
using DynamicExpresso.Parsing;
7+
using DynamicExpresso.Reflection;
98
using DynamicExpresso.Resources;
9+
using DynamicExpresso.Visitors;
1010

1111
namespace DynamicExpresso
1212
{
@@ -187,7 +187,7 @@ public Interpreter SetFunction(string name, Delegate value)
187187
throw new ArgumentNullException(nameof(name));
188188

189189
if (_settings.Identifiers.TryGetValue(name, out var identifier) &&
190-
identifier is FunctionIdentifier fIdentifier)
190+
identifier is FunctionIdentifier fIdentifier)
191191
{
192192
fIdentifier.AddOverload(value);
193193
}
@@ -389,11 +389,7 @@ public Interpreter Reference(ReferenceType type)
389389
throw new ArgumentNullException(nameof(type));
390390

391391
_settings.KnownTypes[type.Name] = type;
392-
393-
foreach (var extensionMethod in type.ExtensionMethods)
394-
{
395-
_settings.ExtensionMethods.Add(extensionMethod);
396-
}
392+
_settings.ExtensionMethods.UnionWith(type.ExtensionMethods);
397393

398394
return this;
399395
}

src/DynamicExpresso.Core/Parsing/ParseSignatures.cs

Lines changed: 116 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,143 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using DynamicExpresso.Reflection;
25

36
namespace DynamicExpresso.Parsing
47
{
5-
internal class ParseSignatures
8+
internal static class ParseSignatures
69
{
7-
public interface ILogicalSignatures
10+
private static MethodData[] MakeUnarySignatures(params Type[] possibleOperandTypes)
811
{
9-
void F(bool x, bool y);
10-
void F(bool? x, bool? y);
12+
var signatures = new MethodData[possibleOperandTypes.Length];
13+
for (var i = 0; i < possibleOperandTypes.Length; i++)
14+
{
15+
signatures[i] = new MethodData
16+
{
17+
Parameters = new[] { new SimpleParameterInfo(possibleOperandTypes[i]) },
18+
};
19+
}
20+
return signatures;
1121
}
1222

13-
public interface IArithmeticSignatures
23+
private static MethodData[] MakeBinarySignatures(IList<(Type, Type)> possibleOperandTypes)
1424
{
15-
void F(int x, int y);
16-
void F(uint x, uint y);
17-
void F(long x, long y);
18-
void F(ulong x, ulong y);
19-
void F(float x, float y);
20-
void F(double x, double y);
21-
void F(decimal x, decimal y);
22-
void F(int? x, int? y);
23-
void F(uint? x, uint? y);
24-
void F(long? x, long? y);
25-
void F(ulong? x, ulong? y);
26-
void F(float? x, float? y);
27-
void F(double? x, double? y);
28-
void F(decimal? x, decimal? y);
25+
var signatures = new MethodData[possibleOperandTypes.Count];
26+
for (var i = 0; i < possibleOperandTypes.Count; i++)
27+
{
28+
var (left, right) = possibleOperandTypes[i];
29+
signatures[i] = new MethodData
30+
{
31+
Parameters = new[] { new SimpleParameterInfo(left), new SimpleParameterInfo(right) },
32+
};
33+
}
34+
return signatures;
2935
}
3036

31-
public interface IRelationalSignatures : IArithmeticSignatures
37+
/// <summary>
38+
/// Signatures for the binary logical operators.
39+
/// </summary>
40+
public static MethodData[] LogicalSignatures = MakeBinarySignatures(new[]
3241
{
33-
void F(string x, string y);
34-
void F(char x, char y);
35-
void F(DateTime x, DateTime y);
36-
void F(TimeSpan x, TimeSpan y);
37-
void F(char? x, char? y);
38-
void F(DateTime? x, DateTime? y);
39-
void F(TimeSpan? x, TimeSpan? y);
40-
}
42+
(typeof(bool), typeof(bool) ),
43+
(typeof(bool?), typeof(bool?)),
44+
});
4145

42-
public interface IEqualitySignatures : IRelationalSignatures
46+
/// <summary>
47+
/// Signatures for the binary arithmetic operators.
48+
/// </summary>
49+
public static MethodData[] ArithmeticSignatures = MakeBinarySignatures(new[]
4350
{
44-
void F(bool x, bool y);
45-
void F(bool? x, bool? y);
46-
}
51+
(typeof(int), typeof(int) ),
52+
(typeof(uint), typeof(uint) ),
53+
(typeof(long), typeof(long) ),
54+
(typeof(ulong), typeof(ulong) ),
55+
(typeof(float), typeof(float) ),
56+
(typeof(double), typeof(double) ),
57+
(typeof(decimal), typeof(decimal) ),
58+
(typeof(int?), typeof(int?) ),
59+
(typeof(uint?), typeof(uint?) ),
60+
(typeof(long?), typeof(long?) ),
61+
(typeof(ulong?), typeof(ulong?) ),
62+
(typeof(float?), typeof(float?) ),
63+
(typeof(double?), typeof(double?) ),
64+
(typeof(decimal?), typeof(decimal?)),
65+
});
4766

48-
public interface IAddSignatures : IArithmeticSignatures
67+
/// <summary>
68+
/// Signatures for the binary relational operators.
69+
/// </summary>
70+
public static MethodData[] RelationalSignatures = ArithmeticSignatures.Concat(MakeBinarySignatures(new[]
4971
{
50-
void F(DateTime x, TimeSpan y);
51-
void F(TimeSpan x, TimeSpan y);
52-
void F(DateTime? x, TimeSpan? y);
53-
void F(TimeSpan? x, TimeSpan? y);
54-
}
72+
(typeof(string), typeof(string) ),
73+
(typeof(char), typeof(char) ),
74+
(typeof(DateTime), typeof(DateTime) ),
75+
(typeof(TimeSpan), typeof(TimeSpan) ),
76+
(typeof(char?), typeof(char?) ),
77+
(typeof(DateTime?), typeof(DateTime?)),
78+
(typeof(TimeSpan?), typeof(TimeSpan?)),
79+
})).ToArray();
5580

56-
public interface ISubtractSignatures : IAddSignatures
57-
{
58-
void F(DateTime x, DateTime y);
59-
void F(DateTime? x, DateTime? y);
60-
}
81+
/// <summary>
82+
/// Signatures for the binary equality operators.
83+
/// </summary>
84+
public static MethodData[] EqualitySignatures = RelationalSignatures.Concat(LogicalSignatures).ToArray();
6185

62-
public interface INegationSignatures
86+
/// <summary>
87+
/// Signatures for the binary + operators.
88+
/// </summary>
89+
public static MethodData[] AddSignatures = ArithmeticSignatures.Concat(MakeBinarySignatures(new[]
6390
{
64-
void F(int x);
65-
void F(long x);
66-
void F(float x);
67-
void F(double x);
68-
void F(decimal x);
69-
void F(int? x);
70-
void F(long? x);
71-
void F(float? x);
72-
void F(double? x);
73-
void F(decimal? x);
74-
}
91+
(typeof(DateTime), typeof(TimeSpan) ),
92+
(typeof(TimeSpan), typeof(TimeSpan) ),
93+
(typeof(DateTime?), typeof(TimeSpan?)),
94+
(typeof(TimeSpan?), typeof(TimeSpan?)),
95+
})).ToArray();
7596

76-
public interface INotSignatures
97+
/// <summary>
98+
/// Signatures for the binary - operators.
99+
/// </summary>
100+
public static MethodData[] SubtractSignatures = AddSignatures.Concat(MakeBinarySignatures(new[]
77101
{
78-
void F(bool x);
79-
void F(bool? x);
80-
}
102+
(typeof(DateTime), typeof(DateTime)),
103+
(typeof(DateTime?), typeof(DateTime?)),
104+
})).ToArray();
81105

82-
public interface IBitwiseComplementSignatures
83-
{
84-
void F(int x, int count);
85-
void F(uint x, int count);
86-
void F(long x, int count);
87-
void F(ulong x, int count);
88-
void F(int? x, int? count);
89-
void F(uint? x, int? count);
90-
void F(long? x, int? count);
91-
void F(ulong? x, int? count);
92-
}
106+
/// <summary>
107+
/// Signatures for the unary - operators.
108+
/// </summary>
109+
public static MethodData[] NegationSignatures = MakeUnarySignatures(
110+
typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal),
111+
typeof(int?), typeof(uint?), typeof(long?), typeof(ulong?), typeof(float?), typeof(double?), typeof(decimal?)
112+
);
93113

94-
// the signatures are the same for the left and right shifts
95-
public interface IShiftSignatures
114+
/// <summary>
115+
/// Signatures for the unary not (!) operator.
116+
/// </summary>
117+
public static MethodData[] NotSignatures = MakeUnarySignatures(typeof(bool), typeof(bool?));
118+
119+
/// <summary>
120+
/// Signatures for the bitwise completement operators.
121+
/// </summary>
122+
public static MethodData[] BitwiseComplementSignatures = MakeUnarySignatures(
123+
typeof(int), typeof(uint), typeof(long), typeof(ulong),
124+
typeof(int?), typeof(uint?), typeof(long?), typeof(ulong?)
125+
);
126+
127+
/// <summary>
128+
/// Signatures for the left and right shift operators.
129+
/// </summary>
130+
public static MethodData[] ShiftSignatures = MakeBinarySignatures(new[]
96131
{
97-
void F(int x);
98-
void F(uint x);
99-
void F(long x);
100-
void F(ulong x);
101-
void F(int? x);
102-
void F(uint? x);
103-
void F(long? x);
104-
void F(ulong? x);
105-
}
132+
(typeof(int), typeof(int) ),
133+
(typeof(uint), typeof(int) ),
134+
(typeof(long), typeof(int) ),
135+
(typeof(ulong), typeof(int) ),
136+
(typeof(int?), typeof(int?) ),
137+
(typeof(uint?), typeof(int?) ),
138+
(typeof(long?), typeof(int?) ),
139+
(typeof(ulong?),typeof(int?) ),
140+
});
106141

107142
//interface IEnumerableSignatures
108143
//{

0 commit comments

Comments
 (0)