Skip to content

Conversation

@ToddGrun
Copy link
Contributor

Customer feedback ticket shows ~40% of CPU costs are in this method. Of the 33 seconds of CPU time in this method, 21 seconds are in it's calls to GetTypeNamespace and GetSpanWithoutGlobalPrefix.

VisitRazorDirective is called many times (we'll call that number 'l'), depending on the number of directives in the page. Each call costs O(m x n) where m is the number of import statements in the page and n is the number of directives that aren't fully qualified. Evidently, for this customer on this page, this O(l * m * n) is too much.

  1. GetTypeNamespace -- Reduced to O(n) calls by storing it's result in _nonFullyQualifiedComponents
  2. GetSpanWithoutGlobalPrefix -- Reduced to O(l * m) by moving outside the innnermost loop

Fixes: https://developercommunity.visualstudio.com/t/Razor-Development:-30-Second-CPU-Churn-A/10904811

image

Customer feedback ticket shows ~40% of CPU costs are in this method. Of the 32 seconds of CPU time in this method, 21 seconds are in it's calls to GetTypeNamespace and GetSpanWithoutGlobalPrefix.

VisitRazorDirective is called many times (we'll call that number 'l'), depending on the number of directives in the page. Each call costs O(m x n) where m is the number of import statements in the page and n is the number of directives that aren't fully qualified. Evidently, for this customer on this page, this O(l * m * n) is too much.

1) GetTypeNamespace -- Reduced to O(n) calls by storing it's result in _nonFullyQualifiedComponents
2) GetSpanWithoutGlobalPrefix -- Reduced to O(l * m) by moving outside the innnermost loop

Fixes: https://developercommunity.visualstudio.com/t/Razor-Development:-30-Second-CPU-Churn-A/10904811
@ToddGrun ToddGrun requested a review from a team as a code owner May 22, 2025 18:26
@ToddGrun
Copy link
Contributor Author

@dotnet/razor-compiler for reviews

Copy link
Member

@chsienki chsienki left a comment

Choose a reason for hiding this comment

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

LGTM.

This is great @ToddGrun, there is a ton of low hanging fruit in the compiler for optimization. We've been planning on getting to it, but not yet had the time. Great to see some it being addressed.

internal sealed class ComponentDirectiveVisitor : DirectiveVisitor
{
private readonly List<TagHelperDescriptor> _nonFullyQualifiedComponents = [];
private readonly List<(TagHelperDescriptor TagHelper, string TypeNamespace)> _nonFullyQualifiedComponents = [];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

List<(TagHelperDescriptor TagHelper, string TypeNamespace)>

I want to look into this a bit more, hold off on reviews please

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@chsienki -- can you take another look? I've changed it so that the innermost loop should be over a much smaller set of items.

… search for those with a matching namespace and add those taghelper descriptors.

namespace Microsoft.AspNetCore.Razor.Language;

internal sealed class ReadOnlyMemoryOfCharComparer : IEqualityComparer<ReadOnlyMemory<char>>
Copy link
Member

Choose a reason for hiding this comment

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

Consider moving this helper into Microsoft.AspNetCore.Razor.Utilities.Shared.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I originally had placed this under utilities, but hit a snag with it needing HashCodeCombiner which wasn't available there. Didn't want to start moving code around as part of this PR, so I just placed it in the most convenient location. Agreed that it should be more global though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants