Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve perf\allocs of ActivatorUtilities.CreateInstance() #99383

Merged
merged 5 commits into from
Mar 14, 2024

Conversation

steveharter
Copy link
Member

@steveharter steveharter commented Mar 6, 2024

Reduces allocs and increases CPU perf of ActivatorUtilities.CreateInstance()

The alloc reduction is significant and depends on number of constructors and the number of constructor parameters (e.g. 4.6x reduction for 3 constructors).

The CPU case depends on whether [ActivatorUtilitiesConstructor] is used. If used, the perf improvements are much better:

  • When used, CPU improvement of ~2x depending on number of constructor parameters.
  • When not used, CPU improvement of ~10%.

The optimizations include:

  • Leveraging the recent changes in ActivatorUtilities.CreateInstance() should respect [ActivatorUtilitiesConstructor] #99175 for when [ActivatorUtilitiesConstructor] is used. We can now ignore most processing of other constructors.
  • Leveraging the ConstructorInvoker class added in v8; this allows use of Span<object> over stack-allocated objects (if <= 4 parameters) or the ability to re-use an allocation of object[] over varying number of constructor parameters.
Benchmarks
| Method                         | Job        | Toolchain              | Mean      | Error    | StdDev   | Median    | Min       | Max       | Ratio | RatioSD | Gen0   | Allocated | Alloc Ratio |
|------------------------------- |----------- |----------------------- |----------:|---------:|---------:|----------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| CreateInstance_1               | Job-ZNAFCX | \DI_AFTER\corerun.exe  |  52.41 ns | 0.979 ns | 0.916 ns |  52.14 ns |  51.04 ns |  54.11 ns |  1.00 |    0.00 | 0.0022 |      24 B |        1.00 |
| CreateInstance_1               | Job-OPANVX | \DI_BEFORE\corerun.exe |  56.80 ns | 0.789 ns | 0.659 ns |  57.04 ns |  55.88 ns |  58.31 ns |  1.08 |    0.02 | 0.0075 |      80 B |        3.33 |
|                                |            |                        |           |          |          |           |           |           |       |         |        |           |             |
| CreateInstance_3               | Job-ZNAFCX | \DI_AFTER\corerun.exe  | 113.72 ns | 1.604 ns | 1.500 ns | 113.75 ns | 111.31 ns | 116.46 ns |  1.00 |    0.00 | 0.0037 |      40 B |        1.00 |
| CreateInstance_3               | Job-OPANVX | \DI_BEFORE\corerun.exe | 133.66 ns | 2.790 ns | 3.213 ns | 133.19 ns | 129.45 ns | 139.60 ns |  1.18 |    0.03 | 0.0173 |     184 B |        4.60 |
|                                |            |                        |           |          |          |           |           |           |       |         |        |           |             |
| CreateInstance_5               | Job-ZNAFCX | \DI_AFTER\corerun.exe  | 247.52 ns | 3.243 ns | 2.708 ns | 246.91 ns | 242.39 ns | 250.86 ns |  1.00 |    0.00 | 0.0150 |     160 B |        1.00 |
| CreateInstance_5               | Job-OPANVX | \DI_BEFORE\corerun.exe | 255.55 ns | 4.273 ns | 3.788 ns | 255.48 ns | 247.91 ns | 263.24 ns |  1.03 |    0.02 | 0.0298 |     320 B |        2.00 |
|                                |            |                        |           |          |          |           |           |           |       |         |        |           |             |
| CreateInstance_1_WithAttrFirst | Job-ZNAFCX | \DI_AFTER\corerun.exe  |  41.69 ns | 0.652 ns | 0.610 ns |  41.52 ns |  40.96 ns |  42.76 ns |  1.00 |    0.00 | 0.0022 |      24 B |        1.00 |
| CreateInstance_1_WithAttrFirst | Job-OPANVX | \DI_BEFORE\corerun.exe |  57.47 ns | 1.305 ns | 1.503 ns |  57.00 ns |  55.08 ns |  59.93 ns |  1.39 |    0.04 | 0.0074 |      80 B |        3.33 |
|                                |            |                        |           |          |          |           |           |           |       |         |        |           |             |
| CreateInstance_3_WithAttrFirst | Job-ZNAFCX | \DI_AFTER\corerun.exe  |  66.32 ns | 1.323 ns | 1.105 ns |  66.13 ns |  64.98 ns |  69.18 ns |  1.00 |    0.00 | 0.0036 |      40 B |        1.00 |
| CreateInstance_3_WithAttrFirst | Job-OPANVX | \DI_BEFORE\corerun.exe | 135.23 ns | 2.654 ns | 2.607 ns | 134.25 ns | 132.12 ns | 140.97 ns |  2.04 |    0.05 | 0.0174 |     184 B |        4.60 |
|                                |            |                        |           |          |          |           |           |           |       |         |        |           |             |
| CreateInstance_5_WithAttrFirst | Job-ZNAFCX | \DI_AFTER\corerun.exe  | 125.68 ns | 2.449 ns | 2.515 ns | 125.37 ns | 121.91 ns | 129.73 ns |  1.00 |    0.00 | 0.0149 |     160 B |        1.00 |
| CreateInstance_5_WithAttrFirst | Job-OPANVX | \DI_BEFORE\corerun.exe | 262.42 ns | 3.933 ns | 4.208 ns | 263.44 ns | 254.65 ns | 268.10 ns |  2.09 |    0.05 | 0.0300 |     320 B |        2.00 |
|                                |            |                        |           |          |          |           |           |           |       |         |        |           |             |
| CreateInstance_1_WithAttrLast  | Job-ZNAFCX | \DI_AFTER\corerun.exe  |  41.54 ns | 0.456 ns | 0.405 ns |  41.53 ns |  40.74 ns |  42.25 ns |  1.00 |    0.00 | 0.0022 |      24 B |        1.00 |
| CreateInstance_1_WithAttrLast  | Job-OPANVX | \DI_BEFORE\corerun.exe |  57.71 ns | 1.284 ns | 1.479 ns |  57.26 ns |  55.75 ns |  60.65 ns |  1.40 |    0.04 | 0.0076 |      80 B |        3.33 |
|                                |            |                        |           |          |          |           |           |           |       |         |        |           |             |
| CreateInstance_3_WithAttrLast  | Job-ZNAFCX | \DI_AFTER\corerun.exe  |  65.44 ns | 0.840 ns | 0.744 ns |  65.45 ns |  64.58 ns |  66.73 ns |  1.00 |    0.00 | 0.0038 |      40 B |        1.00 |
| CreateInstance_3_WithAttrLast  | Job-OPANVX | \DI_BEFORE\corerun.exe | 133.59 ns | 2.429 ns | 2.272 ns | 133.87 ns | 129.38 ns | 136.90 ns |  2.04 |    0.04 | 0.0176 |     184 B |        4.60 |
|                                |            |                        |           |          |          |           |           |           |       |         |        |           |             |
| CreateInstance_5_WithAttrLast  | Job-ZNAFCX | \DI_AFTER\corerun.exe  | 125.32 ns | 5.427 ns | 6.249 ns | 122.53 ns | 119.54 ns | 138.34 ns |  1.00 |    0.00 | 0.0149 |     160 B |        1.00 |
| CreateInstance_5_WithAttrLast  | Job-OPANVX | \DI_BEFORE\corerun.exe | 257.39 ns | 5.177 ns | 5.754 ns | 257.49 ns | 249.35 ns | 271.52 ns |  2.06 |    0.10 | 0.0303 |     320 B |        2.00 |

.NET Framework is also slightly improved for both CPU and allocs:

.NET Framework 4.8.1 Benchmarks
BEFORE
| Method                         | Mean     | Error     | StdDev    | Median   | Min      | Max       | Gen0   | Allocated |
|------------------------------- |---------:|----------:|----------:|---------:|---------:|----------:|-------:|----------:|
| CreateInstance_1               | 1.391 us | 0.0269 us | 0.0309 us | 1.385 us | 1.355 us |  1.447 us | 0.0650 |     417 B |
| CreateInstance_3               | 4.355 us | 0.0778 us | 0.0690 us | 4.348 us | 4.264 us |  4.464 us | 0.1575 |    1043 B |
| CreateInstance_5               | 9.039 us | 0.1724 us | 0.1771 us | 8.978 us | 8.799 us |  9.430 us | 0.2889 |    1926 B |
| CreateInstance_1_WithAttrFirst | 1.993 us | 0.0234 us | 0.0208 us | 1.989 us | 1.957 us |  2.038 us | 0.0788 |     538 B |
| CreateInstance_3_WithAttrFirst | 5.031 us | 0.0971 us | 0.0909 us | 5.004 us | 4.898 us |  5.216 us | 0.1800 |    1164 B |
| CreateInstance_5_WithAttrFirst | 9.790 us | 0.1928 us | 0.1980 us | 9.701 us | 9.551 us | 10.255 us | 0.3104 |    2046 B |
| CreateInstance_1_WithAttrLast  | 2.046 us | 0.0354 us | 0.0347 us | 2.033 us | 1.996 us |  2.117 us | 0.0808 |     538 B |
| CreateInstance_3_WithAttrLast  | 5.405 us | 0.2172 us | 0.2414 us | 5.372 us | 5.155 us |  5.935 us | 0.1829 |    1163 B |
| CreateInstance_5_WithAttrLast  | 9.787 us | 0.0755 us | 0.0630 us | 9.780 us | 9.700 us |  9.877 us | 0.3179 |    2046 B |

AFTER
| Method                         | Mean     | Error     | StdDev    | Median   | Min      | Max      | Gen0   | Allocated |
|------------------------------- |---------:|----------:|----------:|---------:|---------:|---------:|-------:|----------:|
| CreateInstance_1               | 1.257 us | 0.0148 us | 0.0124 us | 1.254 us | 1.246 us | 1.290 us | 0.0601 |     393 B |
| CreateInstance_3               | 4.120 us | 0.0342 us | 0.0320 us | 4.124 us | 4.073 us | 4.181 us | 0.1464 |     947 B |
| CreateInstance_5               | 8.723 us | 0.0864 us | 0.0808 us | 8.708 us | 8.611 us | 8.867 us | 0.2405 |    1725 B |
| CreateInstance_1_WithAttrFirst | 1.925 us | 0.0215 us | 0.0201 us | 1.920 us | 1.904 us | 1.959 us | 0.0767 |     514 B |
| CreateInstance_3_WithAttrFirst | 4.909 us | 0.0628 us | 0.0588 us | 4.895 us | 4.835 us | 5.012 us | 0.1582 |    1067 B |
| CreateInstance_5_WithAttrFirst | 9.309 us | 0.0847 us | 0.0751 us | 9.278 us | 9.199 us | 9.470 us | 0.2623 |    1846 B |
| CreateInstance_1_WithAttrLast  | 1.931 us | 0.0288 us | 0.0269 us | 1.926 us | 1.898 us | 1.975 us | 0.0804 |     514 B |
| CreateInstance_3_WithAttrLast  | 4.885 us | 0.0635 us | 0.0594 us | 4.862 us | 4.815 us | 5.022 us | 0.1576 |    1067 B |
| CreateInstance_5_WithAttrLast  | 9.392 us | 0.1023 us | 0.0957 us | 9.360 us | 9.277 us | 9.575 us | 0.2593 |    1846 B |

@ghost
Copy link

ghost commented Mar 6, 2024

Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection
See info in area-owners.md if you want to be subscribed.

Issue Details

[verifying tests; reduces allocs by using new invoker]

Author: steveharter
Assignees: steveharter
Labels:

area-Extensions-DependencyInjection

Milestone: -

@steveharter steveharter added the tenet-performance Performance related issue label Mar 6, 2024
@stephentoub
Copy link
Member

verifying tests

Given the nature of the change, it'd be good to see perf test results on both .NET 9 and .NET Framework.

@steveharter steveharter marked this pull request as ready for review March 7, 2024 20:51
@steveharter steveharter requested a review from buyaa-n March 7, 2024 20:51
Copy link
Contributor

@buyaa-n buyaa-n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left an NIT, overall LGTM.

@steveharter steveharter merged commit 4707b43 into dotnet:main Mar 14, 2024
107 of 111 checks passed
@steveharter steveharter deleted the FastCreateFactory branch March 14, 2024 14:00
@github-actions github-actions bot locked and limited conversation to collaborators Apr 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants