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

Investigate CSharpAvoidUninstantiatedInternalClasses performance #6301

Open
Youssef1313 opened this issue Dec 6, 2022 · 3 comments
Open
Assignees
Labels

Comments

@Youssef1313
Copy link
Member

In a binlog I was looking at, noticed:

image

It might be caused by SemanticModel usage below?

context.RegisterSyntaxNodeAction(context =>
{
var usingDirective = (UsingDirectiveSyntax)context.Node;
if (usingDirective.Alias != null &&
usingDirective.DescendantNodes().OfType<GenericNameSyntax>().Any() &&
context.SemanticModel.GetDeclaredSymbol(usingDirective) is IAliasSymbol aliasSymbol &&
aliasSymbol.Target is INamedTypeSymbol namedTypeSymbol &&
namedTypeSymbol.IsGenericType)
{
var generics = namedTypeSymbol.TypeParameters.Zip(namedTypeSymbol.TypeArguments, (parameter, argument) => (parameter, argument));
ProcessGenericTypes(generics, instantiatedTypes);
}
}, SyntaxKind.UsingDirective);

Can this be improved?

cc @sharwell @mavasani

@mavasani mavasani self-assigned this Dec 7, 2022
@mavasani
Copy link
Contributor

mavasani commented Dec 7, 2022

Note that this rule is disabled by default though:

RuleLevel.Disabled, // Code coverage tools provide superior results when done correctly.

@Youssef1313
Copy link
Member Author

Note that this rule is disabled by default though:

RuleLevel.Disabled, // Code coverage tools provide superior results when done correctly.

For the repo I got the binlog from, I have aggressively enabled all performance analyzers.

The code snippet I posted above is probably not the cause of performance issues. The semantic model is used only if there is a using directive of the form using X = C<..>. While this pattern exists in the repo, there are not too many occurrences.

#6309 is a possible improvement.

Another thing worth looking at is:

if (type.BaseType != null)
{
instantiatedTypes.TryAdd(type.BaseType, null);
}

Here we try to add type.BaseType in the instantiatedTypes ConcurrentDictionary. I'm guessing we could have a lock contention here (or any other expensive work in TryAdd)?

I know that TryAdd internally tries to avoid contention by using a different lock for each bucket, but if we are passing the same type.BaseType, the same lock could be used.

For example:

public class C { }
public class C1 : C { }
public class C2 : C { }
public class C3 : C { }

If we analyze C1, C2, C3 concurrently, we'll call TryAdd for C and have a possible lock contention.

While I can't think of a way to avoid this for the above case, it can be avoided if the base type doesn't belong to compilation.Assembly. Adding such types to instantiatedTypes is unnecessary. We'll never report an error for such types and it's not important to know whether they are instantiated or not.

The very common scenario is when the base type is System.Object.

NOTE: All this is just guessing. I haven't profiled.

@Youssef1313
Copy link
Member Author

                startContext.RegisterOperationAction(context =>
                {
                    var expr = (IObjectCreationOperation)context.Operation;
                    if (expr.Type is INamedTypeSymbol namedType)
                    {
                        instantiatedTypes.TryAdd(namedType, null);
                    }
                }, OperationKind.ObjectCreation);

This also might be a problem, if there are too many object creations of types not in current assembly, TryAdd might slow us down for the same reason.

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

No branches or pull requests

2 participants