Skip to content

Commit 3560338

Browse files
committed
optimize the pooling of the type arrays for the DynamiMethod creation
1 parent f525daa commit 3560338

File tree

3 files changed

+40
-51
lines changed

3 files changed

+40
-51
lines changed

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ public static TDelegate CompileFast<TDelegate>(this LambdaExpression lambdaExpr,
127127
(TDelegate)(TryCompileBoundToFirstClosureParam(
128128
typeof(TDelegate) == typeof(Delegate) ? lambdaExpr.Type : typeof(TDelegate), lambdaExpr.Body,
129129
#if LIGHT_EXPRESSION
130-
lambdaExpr, RentOrNewClosureTypeToParamTypes(lambdaExpr),
130+
lambdaExpr,
131131
#else
132-
lambdaExpr.Parameters, RentOrNewClosureTypeToParamTypes(lambdaExpr.Parameters),
132+
lambdaExpr.Parameters,
133133
#endif
134134
lambdaExpr.ReturnType, flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys()));
135135

@@ -168,9 +168,9 @@ public static bool CompileFastToIL(this LambdaExpression lambdaExpr, ILGenerator
168168
public static Delegate CompileFast(this LambdaExpression lambdaExpr, bool ifFastFailedReturnNull = false, CompilerFlags flags = CompilerFlags.Default) =>
169169
(Delegate)TryCompileBoundToFirstClosureParam(lambdaExpr.Type, lambdaExpr.Body,
170170
#if LIGHT_EXPRESSION
171-
lambdaExpr, RentOrNewClosureTypeToParamTypes(lambdaExpr),
171+
lambdaExpr,
172172
#else
173-
lambdaExpr.Parameters, RentOrNewClosureTypeToParamTypes(lambdaExpr.Parameters),
173+
lambdaExpr.Parameters,
174174
#endif
175175
lambdaExpr.ReturnType, flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
176176

@@ -220,7 +220,7 @@ public static Func<R> CompileFast<R>(this Expression<Func<R>> lambdaExpr, bool i
220220
#else
221221
lambdaExpr.Parameters,
222222
#endif
223-
_closureAsASingleParamType, typeof(R), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
223+
typeof(R), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
224224

225225
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
226226
public static Func<T1, R> CompileFast<T1, R>(this Expression<Func<T1, R>> lambdaExpr,
@@ -231,7 +231,7 @@ public static Func<T1, R> CompileFast<T1, R>(this Expression<Func<T1, R>> lambda
231231
#else
232232
lambdaExpr.Parameters,
233233
#endif
234-
new[] { typeof(ArrayClosure), typeof(T1) }, typeof(R), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
234+
typeof(R), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
235235

236236
/// <summary>Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
237237
public static Func<T1, T2, R> CompileFast<T1, T2, R>(this Expression<Func<T1, T2, R>> lambdaExpr,
@@ -242,7 +242,6 @@ public static Func<T1, T2, R> CompileFast<T1, T2, R>(this Expression<Func<T1, T2
242242
#else
243243
lambdaExpr.Parameters,
244244
#endif
245-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2) }, // todo: @perf rent and return the array of types to pool
246245
typeof(R), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
247246

248247
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
@@ -254,7 +253,7 @@ public static Func<T1, T2, T3, R> CompileFast<T1, T2, T3, R>(
254253
#else
255254
lambdaExpr.Parameters,
256255
#endif
257-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3) }, typeof(R), flags)
256+
typeof(R), flags)
258257
?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
259258

260259
/// <summary>Compiles lambda expression to TDelegate type. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
@@ -266,7 +265,7 @@ public static Func<T1, T2, T3, T4, R> CompileFast<T1, T2, T3, T4, R>(
266265
#else
267266
lambdaExpr.Parameters,
268267
#endif
269-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, typeof(R), flags)
268+
typeof(R), flags)
270269
?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
271270

272271
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
@@ -278,7 +277,7 @@ public static Func<T1, T2, T3, T4, T5, R> CompileFast<T1, T2, T3, T4, T5, R>(
278277
#else
279278
lambdaExpr.Parameters,
280279
#endif
281-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, typeof(R), flags)
280+
typeof(R), flags)
282281
?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
283282

284283
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
@@ -290,7 +289,7 @@ public static Func<T1, T2, T3, T4, T5, T6, R> CompileFast<T1, T2, T3, T4, T5, T6
290289
#else
291290
lambdaExpr.Parameters,
292291
#endif
293-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }, typeof(R), flags)
292+
typeof(R), flags)
294293
?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
295294

296295
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
@@ -301,7 +300,7 @@ public static Action CompileFast(this Expression<Action> lambdaExpr, bool ifFast
301300
#else
302301
lambdaExpr.Parameters,
303302
#endif
304-
_closureAsASingleParamType, typeof(void), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
303+
typeof(void), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
305304

306305
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
307306
public static Action<T1> CompileFast<T1>(this Expression<Action<T1>> lambdaExpr,
@@ -312,7 +311,7 @@ public static Action<T1> CompileFast<T1>(this Expression<Action<T1>> lambdaExpr,
312311
#else
313312
lambdaExpr.Parameters,
314313
#endif
315-
new[] { typeof(ArrayClosure), typeof(T1) }, typeof(void), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
314+
typeof(void), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
316315

317316
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
318317
public static Action<T1, T2> CompileFast<T1, T2>(this Expression<Action<T1, T2>> lambdaExpr,
@@ -323,7 +322,7 @@ public static Action<T1, T2> CompileFast<T1, T2>(this Expression<Action<T1, T2>>
323322
#else
324323
lambdaExpr.Parameters,
325324
#endif
326-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2) }, typeof(void), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
325+
typeof(void), flags) ?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
327326

328327
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
329328
public static Action<T1, T2, T3> CompileFast<T1, T2, T3>(this Expression<Action<T1, T2, T3>> lambdaExpr,
@@ -334,7 +333,7 @@ public static Action<T1, T2, T3> CompileFast<T1, T2, T3>(this Expression<Action<
334333
#else
335334
lambdaExpr.Parameters,
336335
#endif
337-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3) }, typeof(void), flags)
336+
typeof(void), flags)
338337
?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
339338

340339
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
@@ -346,7 +345,7 @@ public static Action<T1, T2, T3, T4> CompileFast<T1, T2, T3, T4>(
346345
#else
347346
lambdaExpr.Parameters,
348347
#endif
349-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, typeof(void), flags)
348+
typeof(void), flags)
350349
?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
351350

352351
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
@@ -358,7 +357,7 @@ public static Action<T1, T2, T3, T4, T5> CompileFast<T1, T2, T3, T4, T5>(
358357
#else
359358
lambdaExpr.Parameters,
360359
#endif
361-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, typeof(void), flags)
360+
typeof(void), flags)
362361
?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
363362

364363
/// <summary>Compiles lambda expression to delegate. Use ifFastFailedReturnNull parameter to Not fallback to Expression.Compile, useful for testing.</summary>
@@ -370,7 +369,7 @@ public static Action<T1, T2, T3, T4, T5, T6> CompileFast<T1, T2, T3, T4, T5, T6>
370369
#else
371370
lambdaExpr.Parameters,
372371
#endif
373-
new[] { typeof(ArrayClosure), typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6) }, typeof(void), flags)
372+
typeof(void), flags)
374373
?? (ifFastFailedReturnNull ? null : lambdaExpr.CompileSys());
375374

376375
#endregion
@@ -380,9 +379,9 @@ public static TDelegate TryCompile<TDelegate>(this LambdaExpression lambdaExpr,
380379
where TDelegate : class =>
381380
(TDelegate)TryCompileBoundToFirstClosureParam(typeof(TDelegate) == typeof(Delegate) ? lambdaExpr.Type : typeof(TDelegate), lambdaExpr.Body,
382381
#if LIGHT_EXPRESSION
383-
lambdaExpr, RentOrNewClosureTypeToParamTypes(lambdaExpr),
382+
lambdaExpr,
384383
#else
385-
lambdaExpr.Parameters, RentOrNewClosureTypeToParamTypes(lambdaExpr.Parameters),
384+
lambdaExpr.Parameters,
386385
#endif
387386
lambdaExpr.ReturnType, flags);
388387

@@ -486,14 +485,19 @@ private static Delegate CompileNoArgsNew(ConstructorInfo ctor, Type delegateType
486485

487486
#if LIGHT_EXPRESSION
488487
internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Expression bodyExpr, IParameterProvider paramExprs,
489-
Type[] closurePlusParamTypes, Type returnType, CompilerFlags flags)
488+
Type returnType, CompilerFlags flags)
490489
{
490+
var closurePlusParamTypes = RentOrNewClosureTypeToParamTypes(paramExprs);
491491
if (bodyExpr is NoArgsNewClassIntrinsicExpression newNoArgs)
492+
{
493+
// there is no Return of the pooled parameter types here, because in the rarest case with the unused lambda arguments we may just exaust the pooled instance
492494
return CompileNoArgsNew(newNoArgs.Constructor, delegateType, closurePlusParamTypes, returnType);
495+
}
493496
#else
494497
internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Expression bodyExpr, IReadOnlyList<PE> paramExprs,
495-
Type[] closurePlusParamTypes, Type returnType, CompilerFlags flags)
498+
Type returnType, CompilerFlags flags)
496499
{
500+
var closurePlusParamTypes = RentOrNewClosureTypeToParamTypes(paramExprs);
497501
#endif
498502
// Try to avoid compilation altogether for Func<bool> delegates via Interpreter, see #468
499503
if (returnType == typeof(bool) & closurePlusParamTypes.Length == 1
@@ -541,13 +545,14 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
541545
return null;
542546
il.Demit(OpCodes.Ret);
543547

548+
ReturnClosureTypeToParamTypesToPool(closurePlusParamTypes);
549+
544550
return method.CreateDelegate(delegateType, closure);
545551
}
546552

547553
private static readonly Type[] _closureAsASingleParamType = { typeof(ArrayClosure) };
548554
private static readonly Type[][] _closureTypePlusParamTypesPool = new Type[8][]; // todo: @perf @mem could we use this for other Type arrays?
549555

550-
// todo: @perf optimize
551556
#if LIGHT_EXPRESSION
552557
private static Type[] RentOrNewClosureTypeToParamTypes(IParameterProvider paramExprs)
553558
{
@@ -560,37 +565,23 @@ private static Type[] RentOrNewClosureTypeToParamTypes(IReadOnlyList<PE> paramEx
560565
if (count == 0)
561566
return _closureAsASingleParamType;
562567

563-
if (count < 8)
564-
{
565-
var pooledClosureAndParamTypes = Interlocked.Exchange(ref _closureTypePlusParamTypesPool[count], null);
566-
if (pooledClosureAndParamTypes != null)
567-
{
568-
for (var i = 0; i < count; i++)
569-
{
570-
var parameterExpr = paramExprs.GetParameter(i); // todo: @perf can we avoid calling virtual GetParameter() and maybe use intrinsic with NoByRef?
571-
pooledClosureAndParamTypes[i + 1] = parameterExpr.IsByRef ? parameterExpr.Type.MakeByRefType() : parameterExpr.Type;
572-
}
573-
return pooledClosureAndParamTypes;
574-
}
575-
}
576-
577-
// todo: @perf the code maybe simplified and then will be the candidate for the inlining
578-
var closureAndParamTypes = new Type[count + 1];
579-
closureAndParamTypes[0] = typeof(ArrayClosure);
568+
var pooled = count < 8 ? Interlocked.Exchange(ref _closureTypePlusParamTypesPool[count], null) ?? new Type[count + 1] : new Type[count + 1];
569+
pooled[0] = typeof(ArrayClosure);
580570
for (var i = 0; i < count; i++)
581571
{
582-
var parameterExpr = paramExprs.GetParameter(i);
583-
closureAndParamTypes[i + 1] = parameterExpr.IsByRef ? parameterExpr.Type.MakeByRefType() : parameterExpr.Type;
572+
var paramExpr = paramExprs.GetParameter(i); // todo: @perf can we avoid calling virtual GetParameter() and maybe use intrinsic with NoByRef?
573+
pooled[i + 1] = !paramExpr.IsByRef ? paramExpr.Type : paramExpr.Type.MakeByRefType();
584574
}
585-
return closureAndParamTypes;
575+
576+
return pooled;
586577
}
587578

588579
[MethodImpl((MethodImplOptions)256)]
589580
private static void ReturnClosureTypeToParamTypesToPool(Type[] closurePlusParamTypes)
590581
{
591-
var paramCount = closurePlusParamTypes.Length - 1;
592-
if (paramCount != 0 && paramCount < 8)
593-
Interlocked.Exchange(ref _closureTypePlusParamTypesPool[paramCount], closurePlusParamTypes); // todo: @perf we don't need the Interlocked here
582+
var paramCountOnly = closurePlusParamTypes.Length - 1;
583+
if (paramCountOnly != 0 & paramCountOnly < 8)
584+
Interlocked.Exchange(ref _closureTypePlusParamTypesPool[paramCountOnly], closurePlusParamTypes); // todo: @perf we don't need the Interlocked here
594585
}
595586

596587
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
@@ -2105,7 +2096,7 @@ public static bool TryEmit(Expression expr,
21052096
|| TryEmitArithmetic(((BinaryExpression)expr).Left, ((BinaryExpression)expr).Right, nodeType, exprType, paramExprs, il,
21062097
ref closure, setup, parent);
21072098
}
2108-
// todo: @wip @feature #472 add interpretation when those node types are supported
2099+
// todo: @feature #472 add interpretation when those node types are supported
21092100
case ExpressionType.AddChecked:
21102101
case ExpressionType.SubtractChecked:
21112102
case ExpressionType.MultiplyChecked:

test/FastExpressionCompiler.IssueTests/Issue468_Optimize_the_delegate_access_to_the_Closure_object_for_the_modern_NET.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ public struct Issue468_Optimize_the_delegate_access_to_the_Closure_object_for_th
1414
{
1515
public void Run(TestRun t)
1616
{
17-
Original_expression_with_closure(t);
1817
Original_expression(t);
18+
Original_expression_with_closure(t);
1919
}
2020

2121
// Exposing for the benchmarking

test/FastExpressionCompiler.TestsRunner/Program.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@ public static void Main()
1313
{
1414
var t = new LightExpression.TestRun();
1515

16-
t.Run(new LightExpression.IssueTests.Issue473_InvalidProgramException_when_using_Expression_Condition_with_converted_decimal_expression());
17-
1816
t.Run(new LightExpression.IssueTests.Issue468_Optimize_the_delegate_access_to_the_Closure_object_for_the_modern_NET());
19-
2017
t.Run(new LightExpression.IssueTests.Issue472_TryInterpret_and_Reduce_primitive_arithmetic_and_logical_expressions_during_the_compilation());
18+
t.Run(new LightExpression.IssueTests.Issue473_InvalidProgramException_when_using_Expression_Condition_with_converted_decimal_expression());
2119

2220
// new Issue55_CompileFast_crash_with_ref_parameter().Run();
2321

0 commit comments

Comments
 (0)