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 of ActivatorUtilities.CreateInstance() by avoiding an alloc per ctor #99321

Closed
wants to merge 2 commits into from

Conversation

steveharter
Copy link
Member

@steveharter steveharter commented Mar 5, 2024

The current implementation allocs a struct for each ctor; the struct was changed to be ref struct \ byref-like type

Found when working on #99175.

Affects the CreateInstance* benchmarks; 3x-4x faster for existing benchmarks (which typically have 3 ctors);

| Method                                 | Job        | Toolchain              | Mean      | Error    | StdDev   | Median    | Min       | Max       | Ratio | RatioSD | Gen0   | Allocated | Alloc Ratio |
|--------------------------------------- |----------- |----------------------- |----------:|---------:|---------:|----------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| CreateInstance_0                       | Job-IPPVWX | \DI_AFTER\corerun.exe  |  32.73 ns | 0.158 ns | 0.140 ns |  32.72 ns |  32.47 ns |  33.00 ns |  1.00 |    0.00 | 0.0046 |      48 B |        1.00 |
| CreateInstance_0                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 112.61 ns | 0.679 ns | 0.567 ns | 112.61 ns | 111.73 ns | 113.76 ns |  3.44 |    0.02 | 0.0074 |      80 B |        1.67 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_1                       | Job-IPPVWX | \DI_AFTER\corerun.exe  |  82.42 ns | 0.869 ns | 0.813 ns |  82.21 ns |  81.33 ns |  84.40 ns |  1.00 |    0.00 | 0.0112 |     120 B |        1.00 |
| CreateInstance_1                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 350.47 ns | 3.106 ns | 2.905 ns | 351.30 ns | 344.55 ns | 354.73 ns |  4.25 |    0.05 | 0.0239 |     264 B |        2.20 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_2                       | Job-IPPVWX | \DI_AFTER\corerun.exe  | 109.00 ns | 1.101 ns | 1.030 ns | 108.67 ns | 107.70 ns | 111.20 ns |  1.00 |    0.00 | 0.0160 |     168 B |        1.00 |
| CreateInstance_2                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 471.02 ns | 2.956 ns | 2.469 ns | 470.22 ns | 467.44 ns | 476.03 ns |  4.33 |    0.04 | 0.0342 |     360 B |        2.14 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_3                       | Job-IPPVWX | \DI_AFTER\corerun.exe  |  71.83 ns | 0.722 ns | 0.563 ns |  71.81 ns |  70.54 ns |  72.81 ns |  1.00 |    0.00 | 0.0082 |      88 B |        1.00 |
| CreateInstance_3                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 182.74 ns | 1.460 ns | 1.294 ns | 182.96 ns | 180.28 ns | 184.77 ns |  2.54 |    0.03 | 0.0156 |     168 B |        1.91 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_4                       | Job-IPPVWX | \DI_AFTER\corerun.exe  | 138.91 ns | 1.209 ns | 1.072 ns | 138.69 ns | 137.40 ns | 140.92 ns |  1.00 |    0.00 | 0.0186 |     200 B |        1.00 |
| CreateInstance_4                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 505.69 ns | 1.958 ns | 1.529 ns | 505.54 ns | 503.81 ns | 507.96 ns |  3.64 |    0.03 | 0.0386 |     408 B |        2.04 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_5                       | Job-IPPVWX | \DI_AFTER\corerun.exe  | 170.57 ns | 1.498 ns | 1.401 ns | 169.98 ns | 169.04 ns | 173.50 ns |  1.00 |    0.00 | 0.0204 |     216 B |        1.00 |
| CreateInstance_5                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 538.60 ns | 4.917 ns | 4.600 ns | 538.12 ns | 532.02 ns | 546.83 ns |  3.16 |    0.04 | 0.0405 |     432 B |        2.00 |

@steveharter steveharter added the tenet-performance Performance related issue label Mar 5, 2024
@steveharter steveharter added this to the 9.0.0 milestone Mar 5, 2024
@steveharter steveharter requested a review from buyaa-n March 5, 2024 18:41
@steveharter steveharter self-assigned this Mar 5, 2024
@ghost
Copy link

ghost commented Mar 5, 2024

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

Issue Details

The current implementation allocs a struct for each ctor; the struct was changed to be ref struct \ byref-like type

Affects the CreateInstance* benchmarks; 3x-4x faster for existing benchmarks (which typically have 3 ctors);

| Method                                 | Job        | Toolchain              | Mean      | Error    | StdDev   | Median    | Min       | Max       | Ratio | RatioSD | Gen0   | Allocated | Alloc Ratio |
|--------------------------------------- |----------- |----------------------- |----------:|---------:|---------:|----------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| CreateInstance_0                       | Job-IPPVWX | \DI_AFTER\corerun.exe  |  32.73 ns | 0.158 ns | 0.140 ns |  32.72 ns |  32.47 ns |  33.00 ns |  1.00 |    0.00 | 0.0046 |      48 B |        1.00 |
| CreateInstance_0                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 112.61 ns | 0.679 ns | 0.567 ns | 112.61 ns | 111.73 ns | 113.76 ns |  3.44 |    0.02 | 0.0074 |      80 B |        1.67 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_1                       | Job-IPPVWX | \DI_AFTER\corerun.exe  |  82.42 ns | 0.869 ns | 0.813 ns |  82.21 ns |  81.33 ns |  84.40 ns |  1.00 |    0.00 | 0.0112 |     120 B |        1.00 |
| CreateInstance_1                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 350.47 ns | 3.106 ns | 2.905 ns | 351.30 ns | 344.55 ns | 354.73 ns |  4.25 |    0.05 | 0.0239 |     264 B |        2.20 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_2                       | Job-IPPVWX | \DI_AFTER\corerun.exe  | 109.00 ns | 1.101 ns | 1.030 ns | 108.67 ns | 107.70 ns | 111.20 ns |  1.00 |    0.00 | 0.0160 |     168 B |        1.00 |
| CreateInstance_2                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 471.02 ns | 2.956 ns | 2.469 ns | 470.22 ns | 467.44 ns | 476.03 ns |  4.33 |    0.04 | 0.0342 |     360 B |        2.14 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_3                       | Job-IPPVWX | \DI_AFTER\corerun.exe  |  71.83 ns | 0.722 ns | 0.563 ns |  71.81 ns |  70.54 ns |  72.81 ns |  1.00 |    0.00 | 0.0082 |      88 B |        1.00 |
| CreateInstance_3                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 182.74 ns | 1.460 ns | 1.294 ns | 182.96 ns | 180.28 ns | 184.77 ns |  2.54 |    0.03 | 0.0156 |     168 B |        1.91 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_4                       | Job-IPPVWX | \DI_AFTER\corerun.exe  | 138.91 ns | 1.209 ns | 1.072 ns | 138.69 ns | 137.40 ns | 140.92 ns |  1.00 |    0.00 | 0.0186 |     200 B |        1.00 |
| CreateInstance_4                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 505.69 ns | 1.958 ns | 1.529 ns | 505.54 ns | 503.81 ns | 507.96 ns |  3.64 |    0.03 | 0.0386 |     408 B |        2.04 |
|                                        |            |                        |           |          |          |           |           |           |       |         |        |           |
| CreateInstance_5                       | Job-IPPVWX | \DI_AFTER\corerun.exe  | 170.57 ns | 1.498 ns | 1.401 ns | 169.98 ns | 169.04 ns | 173.50 ns |  1.00 |    0.00 | 0.0204 |     216 B |        1.00 |
| CreateInstance_5                       | Job-LQPVEO | \DI_BEFORE\corerun.exe | 538.60 ns | 4.917 ns | 4.600 ns | 538.12 ns | 532.02 ns | 546.83 ns |  3.16 |    0.04 | 0.0405 |     432 B |        2.00 |
Author: steveharter
Assignees: steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone: 9.0.0

@jkotas
Copy link
Member

jkotas commented Mar 5, 2024

How does changing the struct to ref struct avoids the allocation?

@steveharter steveharter closed this Mar 5, 2024
@steveharter
Copy link
Member Author

How does changing the struct to ref struct avoids the allocation?

Yes - sorry. This was premature and got conflated with a previous commit that cached the ConstructorInfoExs in a static.

@github-actions github-actions bot locked and limited conversation to collaborators Apr 7, 2024
@steveharter steveharter deleted the AU.CreateInstancePerf branch September 30, 2024 17:42
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.

2 participants