Skip to content

Optimize the delegate access to the Closure object for the modern .NET #468

Closed
@dadhi

Description

@dadhi

The reason for this goes from here: ncalc/ncalc#408

The FEC fast-compiled code is 2x slower to the result of the System, Compile.

The expression in question:

        var e = new System.Linq.Expressions.Expression[11]; // the unique expressions
        var expr = Lambda<Func<bool>>(
        e[0] = MakeBinary(ExpressionType.Equal,
            e[1] = MakeBinary(ExpressionType.Equal,
                e[2] = MakeBinary(ExpressionType.Add,
                    e[3] = Constant(1),
                    e[4] = Constant(2)),
                e[5] = MakeBinary(ExpressionType.Add,
                    e[6] = Constant(5),
                    e[7] = Constant(-2))),
            e[8] = MakeBinary(ExpressionType.Equal,
                e[9] = Constant(42),
                e[10] = Constant(42))), new ParameterExpression[0]);

The Jitted assembly printed by BDN is almost the same.

I have a couple of ideas to try:

  1. System [Closure](From my recent benchmark, here is the jitted (highly optimized code) of the delegate compile from expression:
    ) is the sealed class with the 2 array fields, vs. FEC ArrayClosure is the unsealed class (has other specialized inherent variants) with a single array field. The idea is to seal it and see how the access changed.
  2. Don't use a closure and compile to the static delegate. Previously (on the older .NET versions) this variant was slower than an instance delegate with the empty closure. Maybe something changed.
  3. Benchmark on .NET 9

Additionally, given that FEC inspects the expression anyway to produce the IL. It may as well optimize logical expressions and avoid emitting the dead code. Minimizing the produced IL (less allocation) and less code size, less work for Jit, etc.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions