Skip to content

Emit selected unary assignment methods without temporary expression trees - #660

Open
jogibear9988 wants to merge 1 commit into
dadhi:masterfrom
jogibear9988:fix/direct-unary-parameter-assignment-20261113
Open

jogibear9988 wants to merge 1 commit into
dadhi:masterfrom
jogibear9988:fix/direct-unary-parameter-assignment-20261113

Conversation

@jogibear9988

Copy link
Copy Markdown
Contributor

Increment/decrement assignment with an operator method builds temporary assignment/block expression nodes during compilation even for an ordinary parameter or local variable.

Load the value, call the selected method, store the assignment and preserve the required pre/post result directly. Retain the existing lowering for captured/ref storage, lifted nullable calls, intrinsic operands and complex targets.

Pre/post increment and decrement, locals and parameters, selected methods, discarded results, value-type results used as receivers, lifted/nonlifted nullable methods, and existing ref/captured storage behavior.

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

Shared-workload measurements

Base: bac81d0734c0 (master); patch: 36cea7097a41. 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 custom_decrement 3.009 → 3.047 1040.00 → 928.00 2.000 → 2.038 0.00
net10.0 / LightExpression custom_increment 3.140 → 2.894 1040.00 → 928.00 2.039 → 1.983 0.00
net10.0 / LightExpression decimal_add 3.289 → 3.098 1024.00 4.242 → 4.090 0.00
net10.0 / LightExpression ref_compound 3.073 → 3.143 880.03 2.091 → 2.054 0.00
net10.0 / LightExpression scalar_add 2.576 → 2.593 872.03 1.988 → 1.980 0.00
net10.0 / System trees custom_decrement 4.500 → 3.557 1040.16 → 928.16 1.965 → 1.938 0.00
net10.0 / System trees custom_increment 4.805 → 3.628 1040.16 → 928.16 1.997 → 2.134 0.00
net10.0 / System trees decimal_add 3.846 → 4.119 1024.00 4.035 → 4.107 0.00
net10.0 / System trees ref_compound 3.909 → 3.986 880.21 2.105 → 2.000 0.00
net10.0 / System trees scalar_add 4.575 → 4.667 872.06 1.986 → 2.014 0.00
net8.0 / LightExpression custom_decrement 6.424 → 5.302 1040.00 → 928.00 2.749 → 3.375 0.00
net8.0 / LightExpression custom_increment 3.168 → 2.640 1040.00 → 928.00 2.303 → 2.502 0.00
net8.0 / LightExpression decimal_add 3.273 → 3.201 1064.00 5.854 → 6.482 0.00
net8.0 / LightExpression ref_compound 2.706 → 2.683 880.04 2.259 → 2.348 0.00
net8.0 / LightExpression scalar_add 2.452 → 2.329 872.03 2.237 → 2.121 0.00
net8.0 / System trees custom_decrement 5.638 → 3.931 1040.16 → 928.16 2.630 → 2.449 0.00
net8.0 / System trees custom_increment 5.174 → 3.613 1040.16 → 928.16 2.353 → 2.309 0.00
net8.0 / System trees decimal_add 4.058 → 3.672 1064.00 5.649 → 6.767 0.00
net8.0 / System trees ref_compound 3.846 → 3.851 881.20 2.266 → 2.714 0.00
net8.0 / System trees scalar_add 2.886 → 3.039 872.05 2.292 → 2.138 0.00

Emitted IL matched byte-for-byte for 20/20 shared control delegates. Observed compilation changes range from -30.3% to +7.1%; invocation changes from -6.9% to +22.8%. 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 allocation changes: net8.0/Standard/custom_increment/compile: 1040.16 → 928.16 B/op; net8.0/Standard/custom_decrement/compile: 1040.16 → 928.16 B/op; net8.0/Light/custom_increment/compile: 1040.00 → 928.00 B/op; net8.0/Light/custom_decrement/compile: 1040.00 → 928.00 B/op; net10.0/Standard/custom_increment/compile: 1040.16 → 928.16 B/op; net10.0/Standard/custom_decrement/compile: 1040.16 → 928.16 B/op; net10.0/Light/custom_increment/compile: 1040.00 → 928.00 B/op; net10.0/Light/custom_decrement/compile: 1040.00 → 928.00 B/op.

Longer confirmation run

Same base, patch and paired protocol, with a 150 ms calibration target instead of 30 ms. These results are additional measurements of the same code; they do not replace slower observations above.

Runtime / FEC API Workload Compile μs, master → patch Compile B/op Invoke ns, master → patch Invoke B/op
net10.0 / LightExpression custom_decrement 6.728 → 5.381 1040.00 → 928.00 2.688 → 4.016 0.00
net10.0 / LightExpression custom_increment 5.669 → 6.993 1040.00 → 928.00 6.036 → 5.548 0.00
net10.0 / LightExpression decimal_add 5.588 → 6.615 1024.00 7.957 → 7.902 0.00
net10.0 / LightExpression ref_compound 5.271 → 5.724 880.01 3.418 → 3.457 0.00
net10.0 / LightExpression scalar_add 4.309 → 4.441 872.01 3.366 → 3.380 0.00
net10.0 / System trees custom_decrement 7.238 → 5.878 1040.05 → 928.05 3.236 → 3.153 0.00
net10.0 / System trees custom_increment 7.853 → 6.493 1040.06 → 928.06 3.717 → 3.375 0.00
net10.0 / System trees decimal_add 7.213 → 6.557 1024.00 6.551 → 7.130 0.00
net10.0 / System trees ref_compound 7.779 → 7.307 880.06 4.273 → 4.541 0.00
net10.0 / System trees scalar_add 5.820 → 5.373 872.01 2.615 → 2.519 0.00
net8.0 / LightExpression custom_decrement 4.711 → 4.533 1040.00 → 928.00 3.478 → 3.439 0.00
net8.0 / LightExpression custom_increment 3.161 → 2.840 1040.00 → 928.00 3.326 → 3.168 0.00
net8.0 / LightExpression decimal_add 5.468 → 5.182 1064.00 8.205 → 8.452 0.00
net8.0 / LightExpression ref_compound 4.493 → 4.173 880.01 3.413 → 3.475 0.00
net8.0 / LightExpression scalar_add 4.190 → 4.261 872.01 3.293 → 2.888 0.00
net8.0 / System trees custom_decrement 5.838 → 4.480 1040.04 → 928.04 2.899 → 3.406 0.00
net8.0 / System trees custom_increment 6.024 → 4.261 1040.03 → 928.03 2.567 → 2.682 0.00
net8.0 / System trees decimal_add 3.261 → 3.244 1064.00 5.423 → 5.687 0.00
net8.0 / System trees ref_compound 3.189 → 3.284 880.04 2.028 → 2.070 0.00
net8.0 / System trees scalar_add 3.272 → 3.222 872.01 2.152 → 2.123 0.00

Increment/decrement assignment with an operator method builds temporary assignment/block expression nodes during compilation even for an ordinary parameter or local variable.

Load the value, call the selected method, store the assignment and preserve the required pre/post result directly. Retain the existing lowering for captured/ref storage, lifted nullable calls, intrinsic operands and complex targets.

Validation: Release solution build and full Standard/LightExpression runners on .NET 8, .NET 10 and .NET Framework.
Pre/post increment and decrement, locals and parameters, selected methods, discarded results, value-type results used as receivers, lifted/nonlifted nullable methods, and existing ref/captured storage behavior.
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