-
Notifications
You must be signed in to change notification settings - Fork 10k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid doing unncecessary work when generating component declaration files #24445
Conversation
8053398
to
e1ce923
Compare
e1ce923
to
91363ef
Compare
…iles. The output of the declaration file for Razor components are unaffected by all inputs other than the input .razor file. Consequently we can avoid regenerating these files if the output is newer than the input. This is the same heuristic we apply to Blazor WebAsssembly's compression artifacts. This PR combines these two improvements for a ~90ms (10%) improvement in the inner loop. ``` 17 ms GenerateBlazorWebAssemblyBootJson 1 calls 22 ms Copy 8 calls 39 ms ProcessFrameworkReferences 1 calls 40 ms RazorTagHelper 1 calls 51 ms ResolveAssemblyReference 1 calls 70 ms GetFileHash 1 calls 80 ms RazorGenerate 2 calls 111 ms Csc 2 calls Time Elapsed 00:00:00.95 ``` ``` 17 ms GenerateBlazorWebAssemblyBootJson 1 calls 21 ms Copy 8 calls 37 ms ProcessFrameworkReferences 1 calls 51 ms ResolveAssemblyReference 1 calls 70 ms Csc 1 calls 72 ms GetFileHash 1 calls 79 ms RazorGenerate 2 calls Time Elapsed 00:00:00.86 ``` In after: Csc calls reduced to one, RazorTagHelper call removed.
91363ef
to
131f796
Compare
@@ -201,6 +201,7 @@ protected override bool ValidateArguments() | |||
if (GenerateDeclaration.HasValue()) | |||
{ | |||
b.Features.Add(new SetSuppressPrimaryMethodBodyOptionFeature()); | |||
b.Features.Add(new SuppressChecksumOptionsFeature()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This prevents the pragma checksum from being generated on declaration files. The checksum is used by debugger. Declaration files aren't used as part of debugging, they're only used for component discovery. Having the checksum is actively harmful since any changes to the input would result in the checksum being different.
@@ -85,7 +86,7 @@ Copyright (c) .NET Foundation. All rights reserved. | |||
Name="RazorGenerateComponentDeclaration" | |||
DependsOnTargets="$(RazorGenerateComponentDeclarationDependsOn)" | |||
Inputs="$(MSBuildAllProjects);@(RazorComponentWithTargetPath);$(_RazorComponentInputCacheFile)" | |||
Outputs="@(_RazorComponentDeclaration)" | |||
Outputs="$(_RazorComponentDeclarationOutputCacheFile)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prior to this, we relied on the declaration files being older than the inputs to determine if the targets need to be executed. This now uses an explicit marker file. This allows declaration files to not be updated, but for us to track if we at least tried regenerating the declaration file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do the contents of this declaration file look like? Does the RazorGenerateComponentDeclaration
task need to be changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The declaration files are the outline of the components with out any body (they kinda look like ref assemblies). It lets us know what components exist and what parameters are on them. Prior to this change, if you were editing Index.razor
to change some markup, here's what the sequence of events would look like:
RazorGenerateComponentDeclaration
Inputs: Index.razor
Output: obj\Debug\net5.0\RazorDeclaration\Index.razor.g.cs
MSBuild would evaluate this target because `Index.razor` is newer than `Index.razor.g.cs` (since it was just edited). At the end of this target, `Index.razor.g.cs` would have a new timestamp but the contents would be unchanged.
Next up is the compile and tag helper discovery targets that we use to find out what changed in the compnents:
RazorCompileComponentDeclaration
Inputs: obj\Debug\net5.0\RazorDeclaration\Index.razor.g.cs
Output: obj\Debug\net5.0\RazorDeclaration\Assembly.declaration.dll
Since `Index.razor.g.cs` is newer, MSBuild will evaluate this target and perform CSC. Next up, since the declaration assembly is newer, we'll perform a tag helper discovery on it.
RazorGenerateComponentDeclaration
Inputs: Index.razor
Output: SomeMarkerFile
MSBuild would evaluate this target because `Index.razor` is newer than `SomeMarkerFile`. At the end of this target, `Index.razor.g.cs` would have not a have newer timestamp. This allows the next two targets to be skipped. SomeMarkerFile helps us track if the target was evaluated since the inputs were last changed.
@@ -227,6 +228,7 @@ protected override bool ValidateArguments() | |||
}); | |||
|
|||
var results = GenerateCode(engine, sourceItems); | |||
var isGeneratingDeclaration = GenerateDeclaration.HasValue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a particular reason the GenerateDeclaration.HasValue
is cached here instead of called in the if-statement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does a linq Any()
call. It seems unnecessary to perform n-times (once for each code file we're code-generating).
Hello @pranavkm! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
…iles. (#24445) The output of the declaration file for Razor components are unaffected by all inputs other than the input .razor file. Consequently we can avoid regenerating these files if the output is newer than the input. This is the same heuristic we apply to Blazor WebAsssembly's compression artifacts. This PR combines these two improvements for a ~90ms (10%) improvement in the inner loop. ``` 17 ms GenerateBlazorWebAssemblyBootJson 1 calls 22 ms Copy 8 calls 39 ms ProcessFrameworkReferences 1 calls 40 ms RazorTagHelper 1 calls 51 ms ResolveAssemblyReference 1 calls 70 ms GetFileHash 1 calls 80 ms RazorGenerate 2 calls 111 ms Csc 2 calls Time Elapsed 00:00:00.95 ``` ``` 17 ms GenerateBlazorWebAssemblyBootJson 1 calls 21 ms Copy 8 calls 37 ms ProcessFrameworkReferences 1 calls 51 ms ResolveAssemblyReference 1 calls 70 ms Csc 1 calls 72 ms GetFileHash 1 calls 79 ms RazorGenerate 2 calls Time Elapsed 00:00:00.86 ``` In after: Csc calls reduced to one, RazorTagHelper call removed.
…iles. (dotnet/aspnetcore#24445) The output of the declaration file for Razor components are unaffected by all inputs other than the input .razor file. Consequently we can avoid regenerating these files if the output is newer than the input. This is the same heuristic we apply to Blazor WebAsssembly's compression artifacts. This PR combines these two improvements for a ~90ms (10%) improvement in the inner loop. ``` 17 ms GenerateBlazorWebAssemblyBootJson 1 calls 22 ms Copy 8 calls 39 ms ProcessFrameworkReferences 1 calls 40 ms RazorTagHelper 1 calls 51 ms ResolveAssemblyReference 1 calls 70 ms GetFileHash 1 calls 80 ms RazorGenerate 2 calls 111 ms Csc 2 calls Time Elapsed 00:00:00.95 ``` ``` 17 ms GenerateBlazorWebAssemblyBootJson 1 calls 21 ms Copy 8 calls 37 ms ProcessFrameworkReferences 1 calls 51 ms ResolveAssemblyReference 1 calls 70 ms Csc 1 calls 72 ms GetFileHash 1 calls 79 ms RazorGenerate 2 calls Time Elapsed 00:00:00.86 ``` In after: Csc calls reduced to one, RazorTagHelper call removed. Commit migrated from dotnet/aspnetcore@f495fcb151e9
The output of the declaration file for Razor components are unaffected by all inputs other than the input .razor file.
Consequently we can avoid regenerating these files if the output is newer than the input. This is the same heuristic we apply to Blazor WebAsssembly's
compression artifacts.
This PR combines these two improvements for a ~90ms (10%) improvement in the inner loop.
In after: Csc calls reduced to one, RazorTagHelper call removed.
Contributes to #22566