Skip to content

Conversation

@chsienki
Copy link
Member

@chsienki chsienki commented Feb 2, 2023

Change the structure of the generator to ensure we don't recalculate things that don't need to be re-calculated:

  • Break up tag helper discovery into components (from razor files) and compilation (from csharp)
  • When discovering tag helpers from components, look at each component individually rather than as a group
  • Break up parsing into multiple steps
    • Do an initial parse only once
    • If a document has changed, re-write the tag helpers, recording which ones were used
    • Re-run the tag helper re-write step, but check if the tag helpers have changed, and skip if we can

@chsienki chsienki added perf 📊 area-compiler Umbrella for all compiler issues labels Feb 2, 2023
@chsienki chsienki added this to the 17.6 P1 milestone Feb 2, 2023

var tagHelpersFromCompilation = compilation
.Combine(generatedDeclarationSyntaxTrees.Collect())
var tagHelpersFromComponents = generatedDeclarationSyntaxTrees
Copy link
Member Author

Choose a reason for hiding this comment

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

This allows us to only look at razor files that have changed to get their updated tag helpers, rather than re-discovering everything anytime a single file changes.

var compilationWithDeclarations = compilation.AddSyntaxTrees(generatedDeclarationSyntaxTree);

// try and find the specific class this component is declaring, falling back to the assembly if for any reason we can't
ISymbol targetSymbol = compilationWithDeclarations.Assembly;
Copy link
Member Author

Choose a reason for hiding this comment

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

We need to look at the whole compilation with the component added (as it might reference stuff from csharp), but there's no need to actually discover anything else from the compilation, so only search the component itself.

.WithLambdaComparer(static (a, b) =>
});

var tagHelpersFromCompilation = compilation
Copy link
Member Author

Choose a reason for hiding this comment

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

We do a single compilation lookup here. If a user edits CSharp this runs without running any of the component stuff again. If the user edits a component, this no longer runs.

})

// Add the tag helpers in, but ignore if they've changed or not, only reprocessing the actual document changed
.Combine(allTagHelpers)
Copy link
Member Author

@chsienki chsienki Feb 3, 2023

Choose a reason for hiding this comment

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

This is the most impactful part of the change:

  • We re-write the document if it has changed, but we ignore the status of the tag helpers.
  • The first time through all documents will be changed, so we run with an up-to-date set of tag helpers.
  • Next time through, only documents that have changed will be re-written (using the latest tag helpers). Those that haven't changed are skipped (and might have incorrect tag helpers at this point).

Then, on line 253 we re-run again, not skipping the tag helper check:

  • (If neither tag helper nor document has changed, there is no work to do)
  • When we re-process we pass the checkIdempotency flag to the engine. This will see if the same taghelpers that were used last time are being passed this time and skip if so.

This effectively makes the two steps mutually exclusive: we either ran the first step, and thus the tag helpers are up to date and we skip doing anything here; or the document didn't change (so we skipped the first step) but the tag helpers are different meaning we need to re-write them.

int startIndex = discoveryPhaseIndex;
var codeDocument = sgDocument.CodeDocument;
var previousTagHelpers = codeDocument.GetTagHelpers();
if (checkForIdempotency && previousTagHelpers is not null)
Copy link
Member Author

Choose a reason for hiding this comment

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

This is where we check the idempotency of the tag helpers. If they haven't changed, we can skip re-writing. If they have, we can check if the ones that changed can actually have an impact on the document and skip if not.

@chsienki
Copy link
Member Author

chsienki commented Feb 3, 2023

@dotnet/razor-compiler for review please. It's not too big, but I did try and split things up logically with the commits so you may find it easier to go commit-by-commit. I've also added some extra comments explaining the logic a bit in a few places.

@chsienki chsienki marked this pull request as ready for review February 3, 2023 00:27
@chsienki chsienki requested a review from a team as a code owner February 3, 2023 00:27

// try and find the specific class this component is declaring, falling back to the assembly if for any reason we can't
ISymbol targetSymbol = compilationWithDeclarations.Assembly;
var classSyntax = generatedDeclarationSyntaxTree.GetRoot(ct).DescendantNodes().SingleOrDefault(n => n.IsKind(CodeAnalysis.CSharp.SyntaxKind.ClassDeclaration));
Copy link
Member

Choose a reason for hiding this comment

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

Can there be nested classes in the declaration syntax tree? Then SingleOrDefault could throw.

Copy link
Member Author

@chsienki chsienki Feb 3, 2023

Choose a reason for hiding this comment

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

Great catch. I've added a test, and fixed via patterns instead.

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

Putting out some comments now, will look more later this afternoon.

@jjonescz jjonescz mentioned this pull request Feb 14, 2023
3 tasks
@chsienki chsienki changed the base branch from release/dev17.6 to main February 23, 2023 19:40
@chsienki
Copy link
Member Author

@333fred for a second review, thanks

@chsienki
Copy link
Member Author

Hmm, merge looks bad. Investigating.

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

Done review pass. Only minor comments.

@chsienki chsienki enabled auto-merge (squash) March 28, 2023 20:25
@chsienki chsienki merged commit 4ab512e into dotnet:main Mar 28, 2023
jjonescz added a commit to jjonescz/razor that referenced this pull request May 19, 2023
jjonescz added a commit to jjonescz/razor that referenced this pull request May 19, 2023
jaredpar pushed a commit that referenced this pull request May 22, 2023
* Revert "Perf/generator (#8212)"

This reverts commit 4ab512e.

* Add more tests
333fred added a commit that referenced this pull request May 22, 2023
…elease/dev17.6-to-release/dev17.7

* upstream/release/dev17.6:
  [17.6] Revert "Perf/generator (#8212)" (#8742)
333fred added a commit that referenced this pull request May 30, 2023
…elease/dev17.7-to-main

* upstream/release/dev17.7:
  Update baseline.
  [17.6] Revert "Perf/generator (#8212)" (#8742)
333fred added a commit that referenced this pull request May 31, 2023
…elease/dev17.7-to-main

* upstream/release/dev17.7:
  Update baseline.
  [17.6] Revert "Perf/generator (#8212)" (#8742)
chsienki added a commit to chsienki/razor-tooling that referenced this pull request Jul 20, 2023
Based on dotnet#8212

- Do an initial parse only once
- If a document has changed, re-write the tag helpers, recording which ones were used
- Re-run the tag helper re-write step, but check if the tag helpers have changed, and skip if we can
@chsienki chsienki mentioned this pull request Jul 20, 2023
chsienki added a commit that referenced this pull request Jul 26, 2023
* Restore perf work.
Based on #8212

- Do an initial parse only once
- If a document has changed, re-write the tag helpers, recording which ones were used
- Re-run the tag helper re-write step, but check if the tag helpers have changed, and skip if we can

* PR Feedback.
- Sealed, unreachable etc.
- Refactor tag helper static feature aquisition to make it clearer
- Remove unused tag helper methods

* Update src/Compiler/Microsoft.NET.Sdk.Razor.SourceGenerators/SourceGeneratorRazorCodeDocument.cs

Co-authored-by: Fred Silberberg <[email protected]>

* PR Feedback

---------

Co-authored-by: Fred Silberberg <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-compiler Umbrella for all compiler issues perf 📊

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants