Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid doing unncecessary work when generating component declaration files #24445

Merged
1 commit merged into from
Jul 31, 2020

Conversation

pranavkm
Copy link
Contributor

@pranavkm pranavkm commented Jul 30, 2020

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.

Contributes to #22566

@ghost ghost added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Jul 30, 2020
@pranavkm pranavkm added area-blazor Includes: Blazor, Razor Components and removed area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates labels Jul 30, 2020
@pranavkm pranavkm requested a review from a team July 30, 2020 17:18
@pranavkm pranavkm added this to the 5.0.0-rc1 milestone Jul 30, 2020
…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.
@@ -201,6 +201,7 @@ protected override bool ValidateArguments()
if (GenerateDeclaration.HasValue())
{
b.Features.Add(new SetSuppressPrimaryMethodBodyOptionFeature());
b.Features.Add(new SuppressChecksumOptionsFeature());
Copy link
Contributor Author

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)"
Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

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();
Copy link
Member

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?

Copy link
Contributor Author

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).

@pranavkm pranavkm requested a review from a team July 31, 2020 17:03
@ghost
Copy link

ghost commented Jul 31, 2020

Hello @pranavkm!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

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 (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit f495fcb into master Jul 31, 2020
@ghost ghost deleted the prkrishn/sdk-improvements branch July 31, 2020 20:40
HaoK pushed a commit that referenced this pull request Aug 7, 2020
…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.
dougbu pushed a commit to dougbu/razor-compiler that referenced this pull request Nov 17, 2021
…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
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants