Skip to content

Invoke runtime System expression-tree values - #659

Open
jogibear9988 wants to merge 1 commit into
dadhi:masterfrom
jogibear9988:fix/runtime-expression-invocation-20261113
Open

jogibear9988 wants to merge 1 commit into
dadhi:masterfrom
jogibear9988:fix/runtime-expression-invocation-20261113

Conversation

@jogibear9988

Copy link
Copy Markdown
Contributor

Invoke handles delegate receivers and literal lambdas, but fails for runtime System.Linq.Expressions.Expression values.

Resolve the typed Compile/Invoke signature, preserve receiver evaluation exactly once, and defer runtime compilation until after arguments when their stack spilling requires it. Teach LightExpression invocation construction to obtain the same return type.

Computed and parameter receivers, changing runtime trees, null and invalid trees, argument/finalizer ordering, incoming ref expression receivers loaded as values, and strict rejection of unsupported by-ref argument signatures.

Calling the runtime expression value’s System Compile method is part of Invoke semantics, not a fallback for compiling the outer FEC lambda. By-ref argument signatures remain unsupported on this path.

Validation: Release solution build; full Standard and LightExpression runners on .NET 8, .NET 10 and .NET Framework. net8.0: 567 TestX cases and 1744 legacy cases; net10.0: 567 TestX cases and 1745 legacy cases; net472: 548 TestX cases and 1610 legacy cases.

Shared-workload measurements

Base: bac81d0734c0 (master); patch: 21e24ce0e832. Windows ARM64, SDK 10.0.301; .NET 8.0.28 and 10.0.9. Expression trees and invocation adapters are prepared outside the timed loops. Strict FEC compilation, tiered compilation disabled, one pinned logical processor, two opposite-load-order processes per runtime, seven alternating paired samples per process, 30 ms calibration target; medians of 14 samples.

Both columns below are FEC. “System trees” names the expression API, not the System compiler. Compilation includes delegate creation; invocation measures the returned delegate through the same adapter on each side. Managed allocations use GC.GetAllocatedBytesForCurrentThread.

Runtime / FEC API Workload Compile μs, master → patch Compile B/op, master → patch Invoke ns, master → patch Invoke B/op, master → patch
net10.0 / LightExpression constructor_arguments 12.542 → 11.438 904.00 11.227 → 28.455 32.00
net10.0 / LightExpression delegate_invoke 13.524 → 15.346 1000.00 9.464 → 7.834 0.00
net10.0 / LightExpression delegate_two_arguments 19.192 → 22.947 1016.22 11.448 → 6.398 0.00
net10.0 / LightExpression scalar_add 8.174 → 7.394 872.13 4.673 → 8.835 0.00
net10.0 / System trees constructor_arguments 16.265 → 10.903 944.66 37.795 → 29.981 32.00
net10.0 / System trees delegate_invoke 26.032 → 18.696 1000.00 8.412 → 9.729 0.00
net10.0 / System trees delegate_two_arguments 24.004 → 16.482 1016.15 7.320 → 7.584 0.00
net10.0 / System trees scalar_add 10.845 → 12.925 872.40 7.104 → 5.819 0.00
net8.0 / LightExpression constructor_arguments 5.702 → 5.200 904.00 33.034 → 28.611 32.00
net8.0 / LightExpression delegate_invoke 6.751 → 11.641 1000.00 8.619 → 9.068 0.00
net8.0 / LightExpression delegate_two_arguments 9.045 → 14.988 1016.20 9.414 → 14.606 0.00
net8.0 / LightExpression scalar_add 1.843 → 4.017 872.18 9.552 → 9.926 0.00
net8.0 / System trees constructor_arguments 8.063 → 6.291 944.26 31.267 → 20.238 32.00
net8.0 / System trees delegate_invoke 11.094 → 10.552 1000.00 9.823 → 7.158 0.00
net8.0 / System trees delegate_two_arguments 6.470 → 13.120 1016.36 9.324 → 5.779 0.00
net8.0 / System trees scalar_add 8.285 → 10.085 872.14 10.301 → 5.565 0.00

Emitted IL matched byte-for-byte for 16/16 shared control delegates. Observed compilation changes range from -33.0% to +117.9%; invocation changes from -46.0% to +153.4%. Slower results are retained. Even identical-IL controls vary, so these short machine timings do not establish a blanket runtime speedup or absence of regressions.
Managed allocations were unchanged on every shared workload in this run.

LightExpression tree construction

Invocation nodes are also measured without compiling them. The existing delegate Invoke lookup is attempted first, so ordinary delegate construction does not perform an extra expression-type/Compile lookup. Literal-lambda construction is an unchanged control. 150 ms target, 14 samples.

Runtime Tree construction Build ns, master → patch Managed B/op, master → patch
net8.0 build_delegate_invocation 40.960 → 42.320 40.00
net8.0 build_literal_invocation 14.772 → 14.850 32.00
net10.0 build_delegate_invocation 322.195 → 297.201 40.00
net10.0 build_literal_invocation 16.456 → 12.600 32.00

Repaired behavior, patch-only measurements

These are executable repaired-case workloads, measured separately from the shared controls. A failed, rejected or incorrect master delegate is not treated as a valid timing baseline. Two isolated loads of the patch provide 14 samples per runtime/API/phase.

Runtime / FEC API Repaired case Compile μs Compile B/op Invoke ns Invoke B/op
net10.0 / LightExpression fixed_case 6.715 1568.00 20697.706 4256.20
net10.0 / LightExpression fixed_case_ignored 7.331 1560.09 20898.710 4256.20
net10.0 / LightExpression fixed_case_void 5.142 1280.00 53840.888 4296.00
net10.0 / System trees fixed_case 13.321 1568.00 38756.920 4256.19
net10.0 / System trees fixed_case_ignored 8.308 1560.09 23649.316 4256.33
net10.0 / System trees fixed_case_void 7.793 1280.00 76364.881 4296.00
net8.0 / LightExpression fixed_case 8.345 1568.00 40345.952 4256.35
net8.0 / LightExpression fixed_case_ignored 5.785 1560.06 30621.838 4256.27
net8.0 / LightExpression fixed_case_void 3.859 1280.00 64777.994 4296.00
net8.0 / System trees fixed_case 13.418 1568.00 54055.773 4256.35
net8.0 / System trees fixed_case_ignored 6.871 1560.05 25014.779 4256.24
net8.0 / System trees fixed_case_void 12.739 1280.00 181847.590 4296.00

Invoke handles delegate receivers and literal lambdas, but fails for runtime System.Linq.Expressions.Expression<TDelegate> values.

Resolve the typed Compile/Invoke signature, preserve receiver evaluation exactly once, and defer runtime compilation until after arguments when their stack spilling requires it. Teach LightExpression invocation construction to obtain the same return type.

Validation: Release solution build and full Standard/LightExpression runners on .NET 8, .NET 10 and .NET Framework.
Computed and parameter receivers, changing runtime trees, null and invalid trees, argument/finalizer ordering, incoming ref expression receivers loaded as values, and strict rejection of unsupported by-ref argument signatures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant