Bug 155371 - [CommonNavigator] NavigatorContentDescriptorManager.findDescriptorsForPossibleChild() is very slow
Summary: [CommonNavigator] NavigatorContentDescriptorManager.findDescriptorsForPossibl...
Status: RESOLVED INVALID
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P2 major (vote)
Target Milestone: 3.3   Edit
Assignee: Michael D. Elder CLA
QA Contact:
URL:
Whiteboard: awaitingfeedback
Keywords: performance
Depends on:
Blocks:
 
Reported: 2006-08-28 06:04 EDT by Naomi Miyamoto CLA
Modified: 2007-04-09 23:27 EDT (History)
7 users (show)

See Also:


Attachments
fireProblemChanged() sampling capture (YourKit3) (103.80 KB, image/png)
2006-08-29 04:12 EDT, Naomi Miyamoto CLA
no flags Details
fireProblemChanged tracing capture (YourKit3), aborted after 4 hours (138.40 KB, image/png)
2006-08-29 04:14 EDT, Naomi Miyamoto CLA
no flags Details
Adds caching to the declarative visibility bindings (2.98 KB, patch)
2006-08-30 10:03 EDT, Michael D. Elder CLA
no flags Details | Diff
EnablementExpression.evaluate() tracing (YourKit3) (21.56 KB, image/png)
2006-08-30 21:46 EDT, Naomi Miyamoto CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Naomi Miyamoto CLA 2006-08-28 06:04:27 EDT
For our test case (create a project which has about 4,000 files, when 50 NavigatorContentDescriptor exist), org.eclipse.jdt.ui.ProblemsLabelDecorator.fireProblemsChanged() method takes only 62 msec for previors version, but now (3.2.1) it takes over 10,000 msec.
Most of time is consumed in org.eclipse.ui.internal.navigator.NavigatorContentDescriptorManager.findDescriptorsForPossibleChild() method.

I think at least org.eclipse.ui.internal.navigator.extensions.NavigatorContentDescriptor.isPossibleChild() method should be optimized(This method takes about 6,000 msec). This method is called for the same kind of instance (e.g "a.gif" file and "b.gif" file), and it always evaluates its expressions. (I also noticed one of the descriptors has over 40 expressions to check. It checks all the expressions to just return false each time target it does not want to handle is passed.
Comment 1 Boris Bokowski CLA 2006-08-28 10:58:08 EDT
Naomi, what are the consequences of this performance problem for the end user?  Does it lead to slow label decoration only, or does it block the UI?

Could you please provide more details, such as (for example) a screenshot of the profiler output showing call frequencies, and the call path from ProblemsLabelDecorator.fireProblemsChanged() to NavigatorContentDescriptor.isPossibleChild()?
Comment 2 Naomi Miyamoto CLA 2006-08-29 04:12:05 EDT
Created attachment 48945 [details]
fireProblemChanged() sampling capture (YourKit3)
Comment 3 Naomi Miyamoto CLA 2006-08-29 04:14:03 EDT
Created attachment 48946 [details]
fireProblemChanged tracing capture (YourKit3), aborted after 4 hours
Comment 4 Naomi Miyamoto CLA 2006-08-29 04:29:09 EDT
(In reply to comment #1)
> Naomi, what are the consequences of this performance problem for the end user? 
It blocks UI. The wizard does not disappear until this labeling is finished.

> Does it lead to slow label decoration only, or does it block the UI?
> Could you please provide more details, such as (for example) a screenshot of
> the profiler output showing call frequencies, and the call path from
> ProblemsLabelDecorator.fireProblemsChanged() to
> NavigatorContentDescriptor.isPossibleChild()?

I attatched two capture images, sampling and tracing.
But the "tracing" is not complete one, because the process does not finished in 4 hours and eclipse seems hung up. So I captured and aborted.
Expected invocation count for CommonViewer.update() is 4097, but the data includes only 2514 invocations.
I will try again tomorrow, but I am not sure I can succeed.
Comment 5 Boris Bokowski CLA 2006-08-29 09:20:06 EDT
I believe we need to investigate if there is a safe fix or a workaround (on our side or theirs) for this for 3.2.1.
Comment 6 Michael D. Elder CLA 2006-08-29 10:54:11 EDT
The core issue here is the performance of the Eclipse Core Expressions framework. An evaluation context has to be created for every evaluation, and each object must be evaluated since (even though two objects may have the same time), they may evaluate differently against a core expression based on property testers. 

We do cache the evaluation once it's made though, at which point we refer to the cache afterwards. 
Comment 7 Dirk Baeumer CLA 2006-08-30 05:30:42 EDT
Michael, I disagree that this is a core expression problem. If you look at the trace than creating the evalution context takes 1% of the overall time. Even Java expressions take time when you call them thousands of times (as you can see with VisibilityAssistant#isVisible which accounts for 15% of the time since it is call 120550 times).

Caching wise the expression framework could only cache the result of an instanceof expression since its value is fixed over time. Caching this value might help in this particular problem since the instanceof expression accounts for 8% of the time. All other values can't be cache (like you can't cache the value of a global expression in Java).

What wonders me a little bit is the huge number of times certain calls take place. If we test all files against all descriptors we end up with 200000 tests. 

If this is necessary the extension point should be structured in a way that a most qualifying test can be evaluated at the beginning (for example refactoring participants first test for a project nature before doing expensive property tests of instanceof test). Of even better that we have group of tests. For example for files we could define more static tests which depend on the file extension. For a Navigator I guess the content descriptor/provider is most of the time the same for all *.gif or all *.java files. If we have such tests than we only have to evaluate them once instead of for every instance. 

In addition we can turn the collections into arrays, however I guess that will only be a cosmetic change.
Comment 8 Dirk Baeumer CLA 2006-08-30 05:32:54 EDT
Naomi, the trace attached in #3 contains an EnablementExpression#evaluate node which takes 14% of the time. Can you provide a screenshot showing its children.
Comment 9 Michael D. Elder CLA 2006-08-30 10:03:05 EDT
Dirk -- that's a good point. 

Caching in VisibilityAssistant.isVisible() should be viable, since the declarative bindings won't change over the life of the Eclipse session. 
Comment 10 Michael D. Elder CLA 2006-08-30 10:03:37 EDT
Created attachment 49054 [details]
Adds caching to the declarative visibility bindings
Comment 11 Boris Bokowski CLA 2006-08-30 10:23:41 EDT
(In reply to comment #10)

This will shave off 15%, correct?

I'm a probably a little naive here, but why is it not possible to cache the result of findDescriptorsForPossibleChild()?
Comment 12 Boris Bokowski CLA 2006-08-30 10:38:15 EDT
For a TreeItem that has been realized, is there a chance that the set of descriptorsWithPossibleChild for the associated element changes over time?
Comment 13 Michael D. Elder CLA 2006-08-30 13:21:17 EDT
findDescriptorsForPossibleChild() does cache its result (as does findDescriptorsForTriggerPoint). 

The set can change sometimes (like as extensions are turned on and off) or if the characteristics of the objects change (like adding or removing facets). For now, clients are required to clear the cache if something like that does take place though. 
Comment 14 Boris Bokowski CLA 2006-08-30 14:39:46 EDT
So in the YourKit trace, what we are seeing is that there is nothing in the cache?  Could the cache be eagerly populated at the time the tree items are created (assuming that at item creation time, you know the set)?
Comment 15 Naomi Miyamoto CLA 2006-08-30 21:46:25 EDT
Created attachment 49117 [details]
EnablementExpression.evaluate() tracing (YourKit3)
Comment 16 Naomi Miyamoto CLA 2006-08-31 03:03:20 EDT
org.eclipse.core.internal.resource.Workspace.run() method.
There are 2 run() methods. The one which has 2 parameters is called 3 times from WTPWorkingCopyManager. The other is called over 60 times. (DataModelPausibleOperation calls it recursively.)

org.eclipe.jdt.internal.ui.viewsupport.ProblemMarkerManager use resouce change event as trigger. When our project is created, 4097 IRosource change is sent. So all the resources are set as the target of LabelProviderChangeEvent.)
Comment 17 Dirk Baeumer CLA 2006-08-31 03:56:36 EDT
Naomi, the trace you attached in #15 is interesting as well. The enablement expression is called 5023 times which results in 111527 instance of expression calls. This means that we have ~20 instanceof checks per enablement expression and  since they are inside of an or expression all of them expect for 5023 in the best case evaluate to false (which are quite a lot).

Can you please attach your 40 XML content descriptors so that I can have a look at them. May be this helps to optimize some code.
Comment 18 Naomi Miyamoto CLA 2006-08-31 07:31:47 EDT
(In reply to comment #17)
> Can you please attach your 40 XML content descriptors so that I can have a look
> at them. May be this helps to optimize some code.

Sent plugin.xml to dirk_baeumer@ch.ibm.com via mail.
Comment 19 Dirk Baeumer CLA 2006-08-31 12:50:03 EDT
Naomi, I have looked at the pluin.xml files an you are doing indeed lots of instanceof checks. One idea to speed things up is to put those instanceof checks which are most likely to return true at the beginning. Since all the checks are in or expressions this could speed up things quite noticably. 

Additionally could you do instanceof checks on some commong types instead of checking concrete types? This could reduce the number of checks as well.
Comment 20 Naomi Miyamoto CLA 2006-09-01 02:41:15 EDT
(In reply to comment #19)
> Additionally could you do instanceof checks on some commong types instead of
> checking concrete types? This could reduce the number of checks as well.

I will ask the owner of that plugin.xml.
Comment 21 Boris Bokowski CLA 2006-09-05 23:11:56 EDT
Moving to 3.3 and lowering the severity since we could not confirm that the common navigator is the culprit.  Still, it does not seem right to make >100,000 calls as a result of an update where most of the elements are not realized in the tree.
Comment 22 Raj Mandayam CLA 2006-09-11 11:29:17 EDT
Hi,

   I found this bug when searching for bugs with terms NavigatorContentDescriptorManager, I had to open one about the cache using TreeSets and after importing a large workspace/projects, this cache grew to 12mb, anyways please read about it in bug 156395
Comment 23 Michael D. Elder CLA 2007-04-09 23:27:33 EDT
I have emailed Naomi and Dirk and it appears that the problem was resolved by re-structuring the enablement expressions in the client extension. Without further feedback to the contrary, I am going to close this bug as INVALID.