Skip to content

Commit f167fe0

Browse files
committed
Now we're talking - closer to the real usage #475
1 parent f92f5cb commit f167fe0

3 files changed

Lines changed: 249 additions & 19 deletions

File tree

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ internal static TDelegate TryCompileWithPreCreatedClosure<TDelegate>(
437437

438438
var delegateType = typeof(TDelegate) != typeof(Delegate) ? typeof(TDelegate) : lambdaExpr.Type;
439439
var @delegate = (TDelegate)(object)method.CreateDelegate(delegateType, new ArrayClosure(closureInfo.Constants.Items));
440-
FreeClosureTypeToParamTypesToPool(closurePlusParamTypes);
440+
FreeClosureTypeAndParamTypes(closurePlusParamTypes);
441441
return @delegate;
442442
}
443443

@@ -468,7 +468,7 @@ public static TDelegate TryCompileWithoutClosure<TDelegate>(this LambdaExpressio
468468

469469
var delegateType = typeof(TDelegate) != typeof(Delegate) ? typeof(TDelegate) : lambdaExpr.Type;
470470
var @delegate = (TDelegate)(object)method.CreateDelegate(delegateType, EmptyArrayClosure);
471-
FreeClosureTypeToParamTypesToPool(closurePlusParamTypes);
471+
FreeClosureTypeAndParamTypes(closurePlusParamTypes);
472472
return @delegate;
473473
}
474474

@@ -545,7 +545,7 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
545545
return null;
546546
il.Demit(OpCodes.Ret);
547547

548-
FreeClosureTypeToParamTypesToPool(closurePlusParamTypes);
548+
FreeClosureTypeAndParamTypes(closurePlusParamTypes);
549549

550550
return method.CreateDelegate(delegateType, closure);
551551
}
@@ -576,8 +576,9 @@ internal static Type[] RentOrNewClosureTypeToParamTypes(IReadOnlyList<PE> paramE
576576
return pooledOrNew;
577577
}
578578

579+
/// <summary>Renting the array of types of closure + plus the passed parameter types</summary>
579580
[MethodImpl((MethodImplOptions)256)]
580-
internal static Type[] RentOrNewClosureTypeToParamTypes(Type p1, Type p2)
581+
public static Type[] RentOrNewClosureTypeToParamTypes(Type p1, Type p2)
581582
{
582583
var pooledOrNew = Interlocked.Exchange(ref _paramTypesPoolWithElem0OfLength1[2], null) ?? new Type[3];
583584
pooledOrNew[0] = typeof(ArrayClosure);
@@ -586,25 +587,28 @@ internal static Type[] RentOrNewClosureTypeToParamTypes(Type p1, Type p2)
586587
return pooledOrNew;
587588
}
588589

590+
/// <summary>Renting the array of the passed parameter types</summary>
589591
[MethodImpl((MethodImplOptions)256)]
590-
internal static Type[] RentParamTypes(Type p0, Type p1)
592+
public static Type[] RentParamTypes(Type p0, Type p1)
591593
{
592594
var pooledOrNew = Interlocked.Exchange(ref _paramTypesPoolWithElem0OfLength1[1], null) ?? new Type[2];
593595
pooledOrNew[0] = p0;
594596
pooledOrNew[1] = p1;
595597
return pooledOrNew;
596598
}
597599

600+
/// <summary>Freeing to the pool the array of types of closure + plus the passed parameter types</summary>
598601
[MethodImpl((MethodImplOptions)256)]
599-
internal static void FreeClosureTypeToParamTypesToPool(Type[] closurePlusParamTypes)
602+
public static void FreeClosureTypeAndParamTypes(Type[] closurePlusParamTypes)
600603
{
601604
var paramCountOnly = closurePlusParamTypes.Length - 1;
602605
if (paramCountOnly != 0 & paramCountOnly < 8)
603606
Interlocked.Exchange(ref _paramTypesPoolWithElem0OfLength1[paramCountOnly], closurePlusParamTypes); // todo: @perf we don't need the Interlocked here
604607
}
605608

609+
/// <summary>Freeing to the pool the array of the passed parameter types</summary>
606610
[MethodImpl((MethodImplOptions)256)]
607-
internal static void FreeParamTypes(Type[] paramTypes)
611+
public static void FreeParamTypes(Type[] paramTypes)
608612
{
609613
var paramCount = paramTypes.Length;
610614
if (paramCount != 0 & paramCount < 8)
@@ -1762,7 +1766,7 @@ private static bool TryCompileNestedLambda(ref ClosureInfo nestedClosureInfo, Ne
17621766
{
17631767
var paramTypes = RentOrNewClosureTypeToParamTypes(nestedLambdaParamExprs);
17641768
nestedLambdaInfo.Lambda = CompileNoArgsNew(newNoArgs.Constructor, nestedLambdaExpr.Type, paramTypes, nestedReturnType);
1765-
FreeClosureTypeToParamTypesToPool(paramTypes);
1769+
FreeClosureTypeAndParamTypes(paramTypes);
17661770
return true;
17671771
}
17681772
#else
@@ -1807,7 +1811,7 @@ private static bool TryCompileNestedLambda(ref ClosureInfo nestedClosureInfo, Ne
18071811
: nestedConstsAndLambdas == null ? new NestedLambdaForNonPassedParams(nestedLambda)
18081812
: new NestedLambdaForNonPassedParamsWithConstants(nestedLambda, nestedConstsAndLambdas);
18091813

1810-
FreeClosureTypeToParamTypesToPool(closurePlusParamTypes);
1814+
FreeClosureTypeAndParamTypes(closurePlusParamTypes);
18111815
return true;
18121816
}
18131817

@@ -1999,7 +2003,8 @@ internal static bool TryEmitUnboxOf(this ILGenerator il, Type sourceType)
19992003
public static class EmittingVisitor
20002004
{
20012005
// todo: @perf use UnsafeAccessAttribute
2002-
private static readonly MethodInfo _getTypeFromHandleMethod =
2006+
/// <summary>Get a type from handle</summary>
2007+
public static readonly MethodInfo GetTypeFromHandleMethod =
20032008
((Func<RuntimeTypeHandle, Type>)Type.GetTypeFromHandle).Method;
20042009
private static readonly MethodInfo _objectEqualsMethod =
20052010
((Func<object, object, bool>)object.Equals).Method;
@@ -3585,7 +3590,7 @@ public static bool TryEmitConstant(bool considerClosure, Type exprType, Type con
35853590
if (constValue is Type t)
35863591
{
35873592
il.Demit(OpCodes.Ldtoken, t);
3588-
return EmitMethodCall(il, _getTypeFromHandleMethod);
3593+
return EmitMethodCall(il, GetTypeFromHandleMethod);
35893594
}
35903595
if (!TryEmitPrimitiveOrEnumOrDecimalConstant(il, constValue, constType))
35913596
return false;
@@ -6244,8 +6249,9 @@ private static bool EmitIncOrDec(ILGenerator il, bool isInc = false)
62446249
return true;
62456250
}
62466251

6252+
/// <summary>Store the variable location on stack</summary>
62476253
[MethodImpl((MethodImplOptions)256)]
6248-
private static void EmitStoreLocalVariable(ILGenerator il, int location)
6254+
public static void EmitStoreLocalVariable(ILGenerator il, int location)
62496255
{
62506256
if (location == 0)
62516257
il.Demit(OpCodes.Stloc_0);
@@ -6261,8 +6267,9 @@ private static void EmitStoreLocalVariable(ILGenerator il, int location)
62616267
il.Demit(OpCodes.Stloc, (short)location);
62626268
}
62636269

6270+
/// <summary>Get and store the variable of the type on stack</summary>
62646271
[MethodImpl((MethodImplOptions)256)]
6265-
private static int EmitStoreLocalVariable(ILGenerator il, Type type)
6272+
public static int EmitStoreLocalVariable(ILGenerator il, Type type)
62666273
{
62676274
var location = il.GetNextLocalVarIndex(type);
62686275
EmitStoreLocalVariable(il, location);
@@ -8407,7 +8414,7 @@ static ILGeneratorHacks()
84078414
_getNextLocalVarIndex = (Func<ILGenerator, Type, int>)efficientMethod.CreateDelegate(
84088415
typeof(Func<ILGenerator, Type, int>), ExpressionCompiler.EmptyArrayClosure);
84098416

8410-
ExpressionCompiler.FreeClosureTypeToParamTypesToPool(paramTypes);
8417+
ExpressionCompiler.FreeClosureTypeAndParamTypes(paramTypes);
84118418

84128419
// todo: @perf do batch Emit by manually calling `EnsureCapacity` once then `InternalEmit` multiple times
84138420
// todo: @perf Replace the `Emit(opcode, int)` with the more specialized `Emit(opcode)`, `Emit(opcode, byte)` or `Emit(opcode, short)`

test/FastExpressionCompiler.Benchmarks/Issue468_Compile_vs_FastCompile.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,16 +308,24 @@ .NET SDK 9.0.203
308308
| ReuseILGenerator | 4.531 us | 0.0898 us | 0.1451 us | 0.87 | 0.04 | 1 | 0.2747 | 0.2670 | 1.71 KB | 0.74 |
309309
| NoReuseILGenerator | 5.211 us | 0.1025 us | 0.1874 us | 1.00 | 0.05 | 2 | 0.3738 | 0.3586 | 2.3 KB | 1.00 |
310310
311+
## Closer to the usage example of pooling a single ILGenerator instance, reusable for any method signature
312+
313+
| Method | Mean | Error | StdDev | Median | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio |
314+
|------------------- |---------:|----------:|----------:|---------:|------:|--------:|-----:|-------:|-------:|----------:|------------:|
315+
| PoolILGenerator | 2.173 us | 0.0407 us | 0.0734 us | 2.140 us | 0.78 | 0.04 | 1 | 0.1068 | 0.1030 | 687 B | 0.60 |
316+
| CreateILGenerator | 2.799 us | 0.0559 us | 0.0949 us | 2.767 us | 1.00 | 0.05 | 2 | 0.1793 | 0.1755 | 1144 B | 1.00 |
317+
311318
*/
312319
[Benchmark(Baseline = true)]
313320
public object NoReuseILGenerator()
314321
{
315-
return IssueTests.Issue475_Reuse_DynamicMethod_if_possible.NoReuseILGenerator();
322+
// return IssueTests.Issue475_Reuse_DynamicMethod_if_possible.NoReuseILGenerator();
323+
return IssueTests.Issue475_Reuse_DynamicMethod_if_possible.CreateDynamicILGenerator();
316324
}
317325

318326
[Benchmark]
319327
public object ReuseILGenerator()
320328
{
321-
return IssueTests.Issue475_Reuse_DynamicMethod_if_possible.ReuseILGenerator();
329+
return IssueTests.Issue475_Reuse_DynamicMethod_if_possible.TryPoolDynamicILGenerator();
322330
}
323331
}

0 commit comments

Comments
 (0)