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

Analyzer Proposal: Recommend use of concrete types to maximize devirtualization potential #51193

Closed
geeknoid opened this issue Apr 13, 2021 · 12 comments
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-Meta code-analyzer Marks an issue that suggests a Roslyn analyzer code-fixer Marks an issue that suggests a Roslyn code fixer partner-impact This issue impacts a partner who needs to be kept updated
Milestone

Comments

@geeknoid
Copy link
Member

Enabling the JIT to devirtualize has a number of benefits as outlined in #49944. To aid in that process, it is preferable to use concrete types rather than interfaces. In particular, private fields, local variables, and private and/or internal method parameters all benefit from being concrete types.

So for example:

private readonly List<string> _myStuff = new ();

is preferable (perf-wise) to:

private readonly IList<string> _myStuff = new List<string>();

By reporting warnings only on private fields, variables, private+internal method parameters, the analyzer would not induce any change in the API surface of a component, where interfaces are often desirable. But it can help speed up the inner-workings of components by fostering devirtualization and hence inlining.

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Apr 13, 2021
@stephentoub stephentoub added code-analyzer Marks an issue that suggests a Roslyn analyzer code-fixer Marks an issue that suggests a Roslyn code fixer labels Apr 13, 2021
@aalmada
Copy link

aalmada commented Apr 13, 2021

@geeknoid
Copy link
Member Author

I already implemented the rule in my analyzer https://github.com/NetFabric/NetFabric.Hyperlinq.Analyzer/blob/master/docs/reference/HLQ001_AssignmentBoxing.md

Yeah, that's the general idea. Although your analysis is specialized for enumerables, I'm proposing something more general.

Basically, if a field, variable or private method parameter is only ever assigned a concrete type, then that element should be declared of that type rather than be declared using an interface type or a base type.

PS: I like your analyzer, I'm going to look at using it in our projects :-)

@jeffschwMSFT jeffschwMSFT added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Apr 14, 2021
@JulieLeeMSFT
Copy link
Member

CC @AndyAyersMS @dotnet/jit-contrib

@JulieLeeMSFT JulieLeeMSFT added this to the Future milestone Apr 15, 2021
@JulieLeeMSFT JulieLeeMSFT removed the untriaged New issue has not been triaged by the area owner label Apr 15, 2021
@AndyAyersMS
Copy link
Member

This doesn't seem like a codegen issue, though having stronger type info definitely will help the jit generate better code.

@stephentoub perhaps you can suggest a more appropriate area label?

@stephentoub stephentoub added area-Meta and removed area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Apr 15, 2021
@stephentoub stephentoub added the api-ready-for-review API is ready for review, it is NOT ready for implementation label Apr 15, 2021
@stephentoub
Copy link
Member

stephentoub commented Apr 15, 2021

Technically such a change can introduce behavioral changes, if the public API on the concrete substituted type differs in behavior from the interface it's replacing, e.g. if the field was IDictionary and it was replaced by Dictionary<object, object>, an access site like object value = _dict[key] with a key that didn't exist will start throwing rather than returning null.

But, such an impact should be relatively rare, and it probably just impacts what level we choose to default the rule to, e.g. hidden.

There are other non-devirtualization benefits to this as well, e.g. if you have:

private IList<int> _list = ...;

and then code that enumerates it:

foreach (int i in _list) { ... }

that enumeration will be much more efficient if the field is:

private List<int> _list = ...;

avoiding enumerator allocation, interface dispatch in the enumeration, etc.

@bartonjs
Copy link
Member

bartonjs commented Jul 27, 2021

Video

As far as performance is concerned, the best results happen when this goes from an interface to a sealed type, but there's still goodness in moving up to a more-derived type (and less so for a more-derived interface).

The main identified complication is when a field, auto-property or variable is declared of an interface and changing it to a concrete type causes a compile failure (or runtime behavior) because of explicit interface implementations.

  • Look at all assignments into the field or variable
  • Find the most specific common type for all of those assignments
  • If that type is not the current declaration type, trigger the diagnostic. (And the fixer does that substitution)

Do not run on public or protected fields (or auto-properties) -- or internal if the assembly has any InternalsVisibleTo.

This may, in the case of explicit interface implementations, cause code to change or fail to compile, but we think we're OK with that.

  • Category: Performance
  • Severity: Information

@ghost ghost added the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label Jul 27, 2021
@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jul 27, 2021
@buyaa-n buyaa-n removed the needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration label Jul 28, 2021
@carlossanlop carlossanlop added the help wanted [up-for-grabs] Good issue for external contributors label Mar 17, 2022
@carlossanlop
Copy link
Member

@dotnet/jit-contrib I marked this as up for grabs, if no one is currently working on this.

@aalmada you mentioned you already had an implementation of this analyzer. Would you like to work on this? (f the members of @dotnet/jit-contrib are ok with that).

@jeffhandley jeffhandley added the partner-impact This issue impacts a partner who needs to be kept updated label Nov 14, 2022
@stephentoub
Copy link
Member

@geeknoid, do you want to open a PR to dotnet/roslyn-analyzers for this?

@buyaa-n buyaa-n modified the milestones: Future, 8.0.0 Nov 16, 2022
@buyaa-n buyaa-n removed the help wanted [up-for-grabs] Good issue for external contributors label Nov 16, 2022
@geeknoid
Copy link
Member Author

@geeknoid, do you want to open a PR to dotnet/roslyn-analyzers for this?

Yep, on my todo list :-)

@stephentoub
Copy link
Member

Great

buyaa-n pushed a commit to dotnet/roslyn-analyzers that referenced this issue Jan 5, 2023
* Add the UseConreteType analyzer

As per dotnet/runtime#51193. This recommends using concrete types for
fields, local, parameters, and method returns instead of
interface/abstract types when possible and when it doesn't affect the
API surface of a class. The idea is to help eliminate
virtual/interface dispatch, while also making available potentially
richer APIs exposed by implementations relative to their interface.

* Updates

Co-authored-by: Martin Taillefer <mataille@microsoft.com>
dotnet-bot pushed a commit to dotnet/dotnet that referenced this issue Jan 5, 2023
* Add the UseConreteType analyzer

As per dotnet/runtime#51193. This recommends using concrete types for
fields, local, parameters, and method returns instead of
interface/abstract types when possible and when it doesn't affect the
API surface of a class. The idea is to help eliminate
virtual/interface dispatch, while also making available potentially
richer APIs exposed by implementations relative to their interface.

* Updates

Co-authored-by: Martin Taillefer <mataille@microsoft.com>

Original commit: dotnet/roslyn-analyzers@2f82379

[[ commit created by automation ]]
@stephentoub
Copy link
Member

Implemented in dotnet/roslyn-analyzers#6370

@ghost ghost locked as resolved and limited conversation to collaborators Feb 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-Meta code-analyzer Marks an issue that suggests a Roslyn analyzer code-fixer Marks an issue that suggests a Roslyn code fixer partner-impact This issue impacts a partner who needs to be kept updated
Projects
No open projects
Development

No branches or pull requests

10 participants