Skip to content

Improve perf of ActivatorUtilities.CreateInstance() - #91290

Merged
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9
Sep 11, 2023
Merged

Improve perf of ActivatorUtilities.CreateInstance()#91290
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9

Conversation

@steveharter

@stevehartersteveharter commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

Fixes#91186

We should consider porting to v8 since this addresses a ~3x perf regression (or more) in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B

@ghost

Copy link
Copy Markdown

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

Issue Details

Fixes #91186

We should consider porting to v8 since this addresses a ~3x perf regression in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
  • Use of the new ConstructorInvoker.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B
Author:steveharter
Assignees:steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone:-

…onstructorInvoker for possible risk of callstack usage
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-nbuyaa-n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, thank you!

@steveharter
steveharter merged commit 00937b2 into dotnet:mainSep 11, 2023
@steveharter
steveharter deleted the DiPerf9 branch September 11, 2023 13:07
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/backport to release/8.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/6146835734

Sign up for freeto 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.

NET 8.0 Preview 7 Performance Regression: ActivatorUtilities.CreateInstance

5 participants

@steveharter@stephentoub@jkotas@MichalPetryka@buyaa-n
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Improve perf of ActivatorUtilities.CreateInstance() by steveharter · Pull Request #91290 · dotnet/runtime · GitHub
Skip to content

Improve perf of ActivatorUtilities.CreateInstance() - #91290

Merged
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9
Sep 11, 2023
Merged

Improve perf of ActivatorUtilities.CreateInstance()#91290
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9

Conversation

@steveharter

@stevehartersteveharter commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

Fixes#91186

We should consider porting to v8 since this addresses a ~3x perf regression (or more) in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B

@ghost

Copy link
Copy Markdown

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

Issue Details

Fixes #91186

We should consider porting to v8 since this addresses a ~3x perf regression in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
  • Use of the new ConstructorInvoker.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B
Author:steveharter
Assignees:steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone:-

…onstructorInvoker for possible risk of callstack usage
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-nbuyaa-n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, thank you!

@steveharter
steveharter merged commit 00937b2 into dotnet:mainSep 11, 2023
@steveharter
steveharter deleted the DiPerf9 branch September 11, 2023 13:07
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/backport to release/8.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/6146835734

Sign up for freeto 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.

NET 8.0 Preview 7 Performance Regression: ActivatorUtilities.CreateInstance

5 participants

@steveharter@stephentoub@jkotas@MichalPetryka@buyaa-n
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Improve perf of ActivatorUtilities.CreateInstance() by steveharter · Pull Request #91290 · dotnet/runtime · GitHub
Skip to content

Improve perf of ActivatorUtilities.CreateInstance() - #91290

Merged
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9
Sep 11, 2023
Merged

Improve perf of ActivatorUtilities.CreateInstance()#91290
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9

Conversation

@steveharter

@stevehartersteveharter commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

Fixes#91186

We should consider porting to v8 since this addresses a ~3x perf regression (or more) in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B

@ghost

Copy link
Copy Markdown

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

Issue Details

Fixes #91186

We should consider porting to v8 since this addresses a ~3x perf regression in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
  • Use of the new ConstructorInvoker.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B
Author:steveharter
Assignees:steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone:-

…onstructorInvoker for possible risk of callstack usage
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-nbuyaa-n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, thank you!

@steveharter
steveharter merged commit 00937b2 into dotnet:mainSep 11, 2023
@steveharter
steveharter deleted the DiPerf9 branch September 11, 2023 13:07
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/backport to release/8.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/6146835734

Sign up for freeto 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.

NET 8.0 Preview 7 Performance Regression: ActivatorUtilities.CreateInstance

5 participants

@steveharter@stephentoub@jkotas@MichalPetryka@buyaa-n
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Improve perf of ActivatorUtilities.CreateInstance() by steveharter · Pull Request #91290 · dotnet/runtime · GitHub
Skip to content

Improve perf of ActivatorUtilities.CreateInstance() - #91290

Merged
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9
Sep 11, 2023
Merged

Improve perf of ActivatorUtilities.CreateInstance()#91290
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9

Conversation

@steveharter

@stevehartersteveharter commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

Fixes#91186

We should consider porting to v8 since this addresses a ~3x perf regression (or more) in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B

@ghost

Copy link
Copy Markdown

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

Issue Details

Fixes #91186

We should consider porting to v8 since this addresses a ~3x perf regression in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
  • Use of the new ConstructorInvoker.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B
Author:steveharter
Assignees:steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone:-

…onstructorInvoker for possible risk of callstack usage
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-nbuyaa-n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, thank you!

@steveharter
steveharter merged commit 00937b2 into dotnet:mainSep 11, 2023
@steveharter
steveharter deleted the DiPerf9 branch September 11, 2023 13:07
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/backport to release/8.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/6146835734

Sign up for freeto 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.

NET 8.0 Preview 7 Performance Regression: ActivatorUtilities.CreateInstance

5 participants

@steveharter@stephentoub@jkotas@MichalPetryka@buyaa-n
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Improve perf of ActivatorUtilities.CreateInstance() by steveharter · Pull Request #91290 · dotnet/runtime · GitHub
Skip to content

Improve perf of ActivatorUtilities.CreateInstance() - #91290

Merged
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9
Sep 11, 2023
Merged

Improve perf of ActivatorUtilities.CreateInstance()#91290
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9

Conversation

@steveharter

@stevehartersteveharter commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

Fixes#91186

We should consider porting to v8 since this addresses a ~3x perf regression (or more) in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B

@ghost

Copy link
Copy Markdown

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

Issue Details

Fixes #91186

We should consider porting to v8 since this addresses a ~3x perf regression in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
  • Use of the new ConstructorInvoker.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B
Author:steveharter
Assignees:steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone:-

…onstructorInvoker for possible risk of callstack usage
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-nbuyaa-n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, thank you!

@steveharter
steveharter merged commit 00937b2 into dotnet:mainSep 11, 2023
@steveharter
steveharter deleted the DiPerf9 branch September 11, 2023 13:07
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/backport to release/8.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/6146835734

Sign up for freeto 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.

NET 8.0 Preview 7 Performance Regression: ActivatorUtilities.CreateInstance

5 participants

@steveharter@stephentoub@jkotas@MichalPetryka@buyaa-n
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Improve perf of ActivatorUtilities.CreateInstance() by steveharter · Pull Request #91290 · dotnet/runtime · GitHub
Skip to content

Improve perf of ActivatorUtilities.CreateInstance() - #91290

Merged
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9
Sep 11, 2023
Merged

Improve perf of ActivatorUtilities.CreateInstance()#91290
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9

Conversation

@steveharter

@stevehartersteveharter commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

Fixes#91186

We should consider porting to v8 since this addresses a ~3x perf regression (or more) in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B

@ghost

Copy link
Copy Markdown

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

Issue Details

Fixes #91186

We should consider porting to v8 since this addresses a ~3x perf regression in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
  • Use of the new ConstructorInvoker.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B
Author:steveharter
Assignees:steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone:-

…onstructorInvoker for possible risk of callstack usage
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-nbuyaa-n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, thank you!

@steveharter
steveharter merged commit 00937b2 into dotnet:mainSep 11, 2023
@steveharter
steveharter deleted the DiPerf9 branch September 11, 2023 13:07
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/backport to release/8.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/6146835734

Sign up for freeto 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.

NET 8.0 Preview 7 Performance Regression: ActivatorUtilities.CreateInstance

5 participants

@steveharter@stephentoub@jkotas@MichalPetryka@buyaa-n
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Improve perf of ActivatorUtilities.CreateInstance() by steveharter · Pull Request #91290 · dotnet/runtime · GitHub
Skip to content

Improve perf of ActivatorUtilities.CreateInstance() - #91290

Merged
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9
Sep 11, 2023
Merged

Improve perf of ActivatorUtilities.CreateInstance()#91290
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9

Conversation

@steveharter

@stevehartersteveharter commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

Fixes#91186

We should consider porting to v8 since this addresses a ~3x perf regression (or more) in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B

@ghost

Copy link
Copy Markdown

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

Issue Details

Fixes #91186

We should consider porting to v8 since this addresses a ~3x perf regression in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
  • Use of the new ConstructorInvoker.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B
Author:steveharter
Assignees:steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone:-

…onstructorInvoker for possible risk of callstack usage
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-nbuyaa-n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, thank you!

@steveharter
steveharter merged commit 00937b2 into dotnet:mainSep 11, 2023
@steveharter
steveharter deleted the DiPerf9 branch September 11, 2023 13:07
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/backport to release/8.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/6146835734

Sign up for freeto 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.

NET 8.0 Preview 7 Performance Regression: ActivatorUtilities.CreateInstance

5 participants

@steveharter@stephentoub@jkotas@MichalPetryka@buyaa-n
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Improve perf of ActivatorUtilities.CreateInstance() by steveharter · Pull Request #91290 · dotnet/runtime · GitHub
Skip to content

Improve perf of ActivatorUtilities.CreateInstance() - #91290

Merged
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9
Sep 11, 2023
Merged

Improve perf of ActivatorUtilities.CreateInstance()#91290
steveharter merged 10 commits into
dotnet:mainfrom
steveharter:DiPerf9

Conversation

@steveharter

@stevehartersteveharter commented Aug 29, 2023

Copy link
Copy Markdown
Contributor

Fixes#91186

We should consider porting to v8 since this addresses a ~3x perf regression (or more) in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B

@ghost

Copy link
Copy Markdown

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

Issue Details

Fixes #91186

We should consider porting to v8 since this addresses a ~3x perf regression in certain cases.

This gets back the v8 regressions in ActivatorUtilities.CreateInstance plus a lot more. The regressions were caused by the new v8 "keyed services" feature and are most pronounced when a service's constructor contains many other services.

The optimizations include caching of ConstructorInfo-related state, in order of perf gain:

  • The values from [FromKeyedServicesAttribute] attribute lookup (the v8 regression)
  • Caching of Type.GetConstructors() along with each ConstructorInfo.Parameters
  • The values from [ActivatorUtilitiesConstructorAttribute] attribute lookup.
  • Use of the new ConstructorInvoker.
Benchmarks from issue #91186: ~2.1-4.5x faster than 8.0 and ~1.3x-2.4x faster than 7.0 and with fewer allocs 7.0
MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA147.9 ns1.07 ns0.95 ns148.1 ns146.0 ns149.3 ns0.0198208 B
CreateB249.1 ns2.42 ns2.15 ns249.7 ns245.1 ns251.8 ns0.0405432 B
CreateC566.5 ns2.64 ns2.21 ns567.0 ns560.4 ns569.2 ns0.0610648 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA134.5 ns1.13 ns1.00 ns134.5 ns133.2 ns136.2 ns0.0196208 B
CreateB473.9 ns6.06 ns5.06 ns471.3 ns467.9 ns481.4 ns0.0386456 B
CreateC1,929.6 ns28.26 ns25.05 ns1,927.4 ns1,895.7 ns1,978.6 ns0.0484600 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
CreateA63.68 ns1.372 ns1.580 ns62.95 ns61.99 ns67.40 ns0.0167176 B
CreateB146.39 ns2.143 ns1.789 ns145.91 ns143.86 ns150.72 ns0.0372392 B
CreateC424.74 ns1.828 ns1.621 ns425.22 ns420.82 ns427.47 ns0.0473496 B
Existing benchmarks: CreateInstance_5 is ~3.5x faster than 8.0 and ~3.3 faster than 7.0 and with half the allocs

7.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected22.12 ns0.483 ns0.536 ns21.82 ns21.62 ns23.13 ns0.002324 B
GetService_1Injected21.55 ns0.464 ns0.456 ns21.38 ns21.09 ns22.61 ns0.002324 B
GetService_2Injected22.56 ns0.328 ns0.274 ns22.45 ns22.31 ns23.22 ns0.003032 B
GetService_3Injected23.72 ns0.115 ns0.090 ns23.70 ns23.63 ns23.96 ns0.003840 B
GetService_4Injected25.86 ns0.194 ns0.162 ns25.86 ns25.52 ns26.19 ns0.004548 B
GetService_5Injected29.45 ns0.330 ns0.292 ns29.37 ns29.14 ns30.00 ns0.005356 B
CreateInstance_098.69 ns0.826 ns0.772 ns98.54 ns97.18 ns99.68 ns0.007680 B
CreateInstance_1343.67 ns3.327 ns2.950 ns342.32 ns340.31 ns349.16 ns0.0252264 B
CreateInstance_2444.98 ns2.626 ns2.456 ns445.19 ns440.92 ns448.47 ns0.0342360 B
CreateInstance_3169.82 ns1.253 ns1.172 ns170.01 ns167.93 ns172.14 ns0.0154168 B
CreateInstance_4505.42 ns5.853 ns5.475 ns505.84 ns495.27 ns515.61 ns0.0385408 B
CreateInstance_5529.31 ns4.613 ns4.089 ns529.38 ns523.04 ns538.25 ns0.0467496 B
Factory_1Injected_2Explicit28.16 ns0.167 ns0.156 ns28.18 ns27.93 ns28.38 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder29.03 ns0.152 ns0.142 ns29.04 ns28.71 ns29.21 ns0.003740 B
Factory_3Explicit10.24 ns0.121 ns0.101 ns10.24 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected65.30 ns0.802 ns0.751 ns65.15 ns64.29 ns66.78 ns0.003840 B

8.0

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.73 ns0.281 ns0.300 ns11.59 ns11.402 ns12.38 ns0.002224 B
GetService_1Injected12.16 ns0.278 ns0.273 ns12.08 ns11.757 ns12.67 ns0.002324 B
GetService_2Injected14.00 ns0.287 ns0.268 ns13.88 ns13.665 ns14.60 ns0.003032 B
GetService_3Injected14.93 ns0.112 ns0.105 ns14.91 ns14.812 ns15.11 ns0.003840 B
GetService_4Injected17.65 ns0.132 ns0.123 ns17.67 ns17.364 ns17.85 ns0.004648 B
GetService_5Injected18.72 ns0.131 ns0.122 ns18.70 ns18.500 ns18.90 ns0.005356 B
CreateInstance_0112.66 ns0.547 ns0.484 ns112.62 ns111.720 ns113.60 ns0.007480 B
CreateInstance_1341.42 ns1.247 ns0.974 ns341.34 ns339.797 ns343.13 ns0.0247264 B
CreateInstance_2477.02 ns2.629 ns2.459 ns476.49 ns473.083 ns481.34 ns0.0327360 B
CreateInstance_3176.83 ns1.213 ns1.135 ns176.74 ns174.181 ns178.78 ns0.0158168 B
CreateInstance_4520.00 ns1.763 ns1.472 ns519.93 ns518.101 ns523.24 ns0.0372408 B
CreateInstance_5558.50 ns4.572 ns4.277 ns557.23 ns553.463 ns566.70 ns0.0401432 B
Factory_1Injected_2Explicit17.59 ns0.128 ns0.114 ns17.60 ns17.408 ns17.78 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.77 ns0.405 ns0.338 ns17.74 ns17.384 ns18.62 ns0.003840 B
Factory_3Explicit10.09 ns0.126 ns0.112 ns10.09 ns9.910 ns10.30 ns0.003840 B
Factory_3Injected34.62 ns0.286 ns0.239 ns34.60 ns34.202 ns35.16 ns0.003840 B

This PR

MethodMeanErrorStdDevMedianMinMaxGen0Allocated
GetService_0Injected11.21 ns0.265 ns0.248 ns11.09 ns10.95 ns11.74 ns0.002324 B
GetService_1Injected14.85 ns0.098 ns0.092 ns14.86 ns14.72 ns15.05 ns0.002324 B
GetService_2Injected14.53 ns0.125 ns0.117 ns14.54 ns14.34 ns14.72 ns0.003032 B
GetService_3Injected14.72 ns0.126 ns0.118 ns14.69 ns14.50 ns14.97 ns0.003840 B
GetService_4Injected18.26 ns0.157 ns0.146 ns18.25 ns18.04 ns18.49 ns0.004548 B
GetService_5Injected19.25 ns0.101 ns0.084 ns19.23 ns19.12 ns19.42 ns0.005356 B
CreateInstance_032.25 ns0.199 ns0.176 ns32.25 ns31.98 ns32.63 ns0.004648 B
CreateInstance_176.11 ns0.719 ns0.638 ns75.94 ns75.33 ns77.72 ns0.0113120 B
CreateInstance_2107.63 ns1.046 ns0.979 ns107.85 ns105.58 ns109.16 ns0.0158168 B
CreateInstance_371.51 ns1.264 ns1.121 ns71.11 ns70.08 ns73.63 ns0.008488 B
CreateInstance_4130.70 ns1.313 ns1.164 ns130.44 ns129.37 ns133.38 ns0.0186200 B
CreateInstance_5158.43 ns1.394 ns1.304 ns158.12 ns156.94 ns160.70 ns0.0203216 B
Factory_1Injected_2Explicit18.26 ns0.134 ns0.125 ns18.23 ns18.01 ns18.48 ns0.003840 B
Factory_1Injected_2Explicit_OutOfOrder17.80 ns0.170 ns0.151 ns17.77 ns17.54 ns18.09 ns0.003840 B
Factory_3Explicit10.23 ns0.093 ns0.087 ns10.22 ns10.09 ns10.42 ns0.003840 B
Factory_3Injected34.31 ns0.269 ns0.225 ns34.27 ns33.93 ns34.73 ns0.003740 B
Author:steveharter
Assignees:steveharter
Labels:

tenet-performance, area-Extensions-DependencyInjection

Milestone:-

…onstructorInvoker for possible risk of callstack usage
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@buyaa-nbuyaa-n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me, thank you!

@steveharter
steveharter merged commit 00937b2 into dotnet:mainSep 11, 2023
@steveharter
steveharter deleted the DiPerf9 branch September 11, 2023 13:07
@steveharter

Copy link
Copy Markdown
ContributorAuthor

/backport to release/8.0

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/6146835734

Sign up for freeto 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.

NET 8.0 Preview 7 Performance Regression: ActivatorUtilities.CreateInstance

5 participants

@steveharter@stephentoub@jkotas@MichalPetryka@buyaa-n