-
Notifications
You must be signed in to change notification settings - Fork 131
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
Updates in performance highlightings. #878
Conversation
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.
please correct some minor issues related to threading and locks assertions
public static class PerformanceCriticalCodeStageUtil | ||
{ | ||
public static bool IsInvocationExpensive(IInvocationExpression invocationExpression) | ||
{ |
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 is a public API.
threading?
locks?
Please use Shell.Instance.Locks.assertBlaBlaBla for all those purposes
namespace JetBrains.ReSharper.Plugins.Unity.CSharp.Feature.Services.QuickFixes.MoveQuickFixes | ||
{ | ||
public static class MonoBehaviourMoveUtil | ||
{ |
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.
seems it can be accessed from UI thread only. Please assert that.
@@ -1,10 +1,10 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.42000 | |||
// Этот код создан программой. |
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.
please exclude this file from the merge
@@ -170,13 +170,15 @@ private void InitLanguageLevelSettings(IProject project, SettingsStorageMountPoi | |||
// * Unity3dRider can set `TargetFrameworkVersion` to `v4.5` on non-Windows machines to fix | |||
// an issue resolving System.Linq | |||
|
|||
var languageLevel = ReSharperSettingsCSharpLanguageLevel.Default; | |||
var languageLevel = ReSharperSettingsCSharpLanguageLevel.CSharp40; |
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.
why is it so?
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.
Out-of-date 183's fixes. Fixed.
[NotNull] | ||
public static string GetUniqueName<T>([NotNull] T node, [NotNull] string baseName, | ||
NamedElementKinds elementKind, Func<IDeclaredElement, bool> isConflictingElement = null) where T : ICSharpTreeNode | ||
{ |
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.
assertions
511d1c8
to
dd2190b
Compare
…in inspection into performance analysis, add context actions.
…performance analysis.
dd2190b
to
b74fdfc
Compare
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 looks like QFs assume they are moving code between methods of the same type.
The analyzer on the other hand seems to check methods from the same file.
Please add tests for code like this:
class C : MonoBehaviour
{
public void Update()
{
var t = new MyType();
t.Test();
}
}
class MyType : MonoBehaviour
{
public void Test()
{
GetComponent("test");
}
}
Is there a reason why a call Stack
is not sufficient for the performance inspector?
It looks like there's a tremendous amout of work done for keeping CurrentElement
and CurrentDeclaration
consistent, reassignments and caching those properties just to check if the element is already processed, maintaining a call graph to propagate costly invocation mark backwards. It also seems like there are still possible pitfalls like visiting method declaration with incorrect values in .Current*
properties or missing processed
mark for local functions.
@@ -41,7 +42,11 @@ protected override void Analyze(IInvocationExpression expression, ElementProblem | |||
if (info.ResolveErrorType != ResolveErrorType.OK) | |||
return; | |||
|
|||
var method = (info.DeclaredElement as IMethod).NotNull("info.DeclaredElement as IMethod != null"); | |||
|
|||
var method = info.DeclaredElement as IMethod; // e.g. localfunction declared element, (when removing `}` and method below is parsed like local function) |
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 comment looks strange to me.
IInvocationExpression
can be just a local function invocation in a correctly parsed code.
The reference expression can also resolve to almost anything e.g. fields, properties, local variables, parameters etc if they are delegates.
E.g.
Action a = () => { };
a();
Why mention parsing recover strategy here?
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.
Out-of-date comment, I have removed it :)
@@ -182,9 +222,12 @@ private static HashSet<IMethodDeclaration> FindHotRootMethods([NotNull]ICSharpFi | |||
|
|||
break; | |||
case IMethodDeclaration methodDeclaration: | |||
// check that method is hot and add it to container |
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 looks like the code above skips types nested in classlike declarations not inherited from MonoBehaviour
which doesn't seem intended
e.g.
class C
{
class NotProcessed : MonoBehaviour { ... }
}
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 is not possible for Unity. MonoScript should be top-level class with same name as file name.
myMaxDepth = maxDepth; | ||
} | ||
|
||
public override void VisitMethodDeclaration(IMethodDeclaration methodDeclarationParam, HotMethodAnalyzerContext context) | ||
{ | ||
context.MarkCurrentAsVisited(); |
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 looks like partial methods will have one random declaration processed (probably not even containing the actual code)
|
||
// find all declarations in current file | ||
var declarations = declaredElement.GetDeclarationsIn(mySourceFile).Where(t => t.GetSourceFile() == mySourceFile); | ||
var declaration = declaredElement.GetDeclarationsIn(mySourceFile).FirstOrDefault(t => t.GetSourceFile() == mySourceFile); |
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.
find all declarations in current file
.FirstOrDefault(...);
It looks like the comment is outdated.
I wonder why only the first declaration is processed though. It's possible to have multiple declarations of the same method with partial types. E.g.
partial class P
{
partial void M();
}
partial class P
{
partial void M() => Console.WriteLine();
}
Current code will process one random possibly empty declaration from the code above.
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.
Regarding GetDeclarationsIn(mySourceFile)
call which returns declarations from other files - it looks like a copy-paste mistake in the source code for CSharpMethod
. I'd recommend fixing the root problem instead of hacking it multiple times here.
@@ -359,7 +412,10 @@ public override void VisitEqualityExpression(IEqualityExpression equalityExpress | |||
|
|||
public bool InteriorShouldBeProcessed(ITreeNode element, HotMethodAnalyzerContext context) | |||
{ | |||
return !(element is ICSharpClosure); | |||
if (element is ICSharpClosure closure) |
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 looks like this code should process local functions when their invocations are found.
Is it intended to ignore costly calls within lambda / anonymous method / query expressions?
e.g.
void M()
{
Action a = () => CostlyInvocation();
a();
}
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.
I haven't seen any code that will mark local function invocations (or any closures, altough it seems that any othe forms of closures aren't processed) as visited as the MarkCurrentAsVisited
method is only called within VisitMethodDeclaration
. Therefore they'll probably be analyzed multiple times. While it won't cause stackoverflow exception due to the depth limit it can cause a huge performance hit especially in case of large recursive functions since they will be analyzed MaxDepth
times for each call. Please check performance for code like this:
void M()
{
Trigger(); // start inspecting the local function
void Trigger()
{
Local(); // inspect the second local function
Trigger(); // inspect Trigger and consequently Local again MaxDepth times
}
void Local()
{
// 1000+ lines of code here, inspect them all
Local(); // inspect Local MaxDepth times recursively
}
}
Will this analyze the Local
source MaxDepth
^2 times?
classDeclaration.AddClassMemberDeclaration(field); | ||
} | ||
|
||
var initialization = isVoid ? factory.CreateStatement("$1;", name, expression.Copy()) : |
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.
.Copy()
var initialization = isVoid ? factory.CreateStatement("$1;", name, expression.Copy()) : | ||
factory.CreateStatement("$0 = $1;", name, expression.Copy()); | ||
|
||
var body = methodDeclaration.EnsureStatementMemberBody(); |
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.
I wonder how this would/should work with code like this:
class MonoBehaviour
{
public virtual extern void Start();
}
abstract class C : MonoBehaviour
{
public abstract override void Start(); // move something to this method
}
|
||
var localVariableDeclaration = | ||
LocalVariableDeclarationNavigator.GetByInitial( | ||
expression.GetContainingParenthesizedExpression()?.Parent as IVariableInitializer); |
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.
var localVariableDeclaration =
LocalVariableDeclarationNavigator.GetByInitial(
ExpressionInitializerNavigator.GetByValue(expression.GetContainingParenthesizedExpression()))
if (type.IsUnknown) | ||
type = TypeFactory.CreateTypeByCLRName("System.Object", toMove.GetPsiModule()); | ||
|
||
string baseName = type.GetPresentableName(CSharpLanguage.Instance); |
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.
.GetPresentableName()
returns a string which should represent a human-readable type name in hints/warnings. It might not be a valid identifier e.g. it could return int[]
or MyType<T>
.
Does naming service handle such base names
? Please, add tests for such scenarios.
} | ||
|
||
return baseName; | ||
} |
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 looks like this method tries to emulate the following:
INamesCollection namesCollection;
namesCollection.Add(GetNameRootFromStringArgument(), EntryOptions { Emphasis = Good ... });
namesCollection.Add(expression, EntryOptions { ... }); // this will add roots based on the expression's type and text
INamesCollection
already has the capability to suggest names based on the expression's type.
It also can suggest names based on the expression's code. It's ignored here altough I'm not sure if it's intended.
a. Currently not solution-wide (only for opened file)
b. This operations can break semantics, so they are just only context actions, not quickfixes