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 a capturing local variables on the GetOptionsAsync fast path #23554

Merged
merged 2 commits into from
Dec 6, 2017

Conversation

sharwell
Copy link
Member

@sharwell sharwell commented Dec 4, 2017

Customer scenario

Running analyzer during a build is slower than it should be, with the analyzer driver contributing substantial overhead even when the analyzers themselves are lightweight.

Bugs this fixes

N/A

Workarounds, if any

None needed

Risk

Low. The performance constraint is documented, and I filed dotnet/roslyn-analyzers#1438 in hope of enforcing the constraint at build time in the future.

Performance impact

AnalyzerRunner indicates allocation savings of 1.5% (1.30GiB).

Is this a regression from a previous update?

No.

Root cause analysis

AnalyzerRunner is a new tool for helping us test analyzer performance in isolation.

How was the bug found?

AnalyzerRunner.

Test documentation updated?

No.

@sharwell sharwell requested a review from a team as a code owner December 4, 2017 14:22
@@ -475,17 +475,22 @@ internal Task<DocumentOptionSet> GetOptionsAsync(OptionSet solutionOptions, Canc
// snapshot model. once that is fixed, we can remove this workaround - https://github.com/dotnet/roslyn/issues/19284
if (_cachedOptions == null)
{
var newAsyncLazy = new AsyncLazy<DocumentOptionSet>(async c =>
Copy link
Contributor

@Therzok Therzok Dec 4, 2017

Choose a reason for hiding this comment

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

How does this prevent capturing a local? It's seems to me that it's moved to an instance method with the exact same code, which I don't think has any effect on lambda captures.

Copy link
Member Author

Choose a reason for hiding this comment

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

➡️ In the fast path, _cachedOptions is not null.

Copy link
Contributor

@Therzok Therzok Dec 4, 2017

Choose a reason for hiding this comment

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

Oh, so it completely avoids allocating the lambda capturing this and locals. I get it now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't codegen be actually handling this case?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure. @dotnet/roslyn-compiler do you have more information and/or a link to an existing issue for this?

Copy link
Member

Choose a reason for hiding this comment

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

@agocke for more insight here.

Copy link
Member

@agocke agocke Dec 5, 2017

Choose a reason for hiding this comment

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

Captured variable frames are created in the scope the variable is declared. This is an implementation detail of the compiler, so it is not documented and probably shouldn't be.

There is a feature request to use more advanced analysis to detect when the frame could be created late, but it's not something we've done before: #20777

}

return _cachedOptions.GetValueAsync(cancellationToken);
}

private void InitializeCachedOptions(OptionSet solutionOptions, CancellationToken cancellationToken)
Copy link
Member

Choose a reason for hiding this comment

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

Probably should add a comment that this is split into a separate function for the reason you're doing this.

Copy link
Member Author

Choose a reason for hiding this comment

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

➡️ Documented via an attribute which can be enforced in the future

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

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

LGTM (with added comment).

@sharwell
Copy link
Member Author

sharwell commented Dec 5, 2017

@Pilchie for ask mode

@sharwell sharwell self-assigned this Dec 5, 2017
@sharwell sharwell added this to the 15.6 milestone Dec 5, 2017
/// </remarks>
[Conditional("EMIT_CODE_ANALYSIS_ATTRIBUTES")]
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
internal sealed class PerformanceSensitiveAttribute : Attribute
Copy link
Contributor

Choose a reason for hiding this comment

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

This attribute should also be applied to this locations to avoid regressions:
PR #23490
SourceNamespaceSymbol
IsDefinedInSourceTree

Copy link
Member Author

Choose a reason for hiding this comment

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

➡️ Filed #23615

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

Successfully merging this pull request may close these issues.

7 participants