Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 325393

Summary: 100% CPU utilization while decorating large repository
Product: [Technology] EGit Reporter: Jean-Philippe Gariepy <jpgariepy>
Component: UIAssignee: Philipp Thun <philipp.thun>
Status: RESOLVED FIXED QA Contact:
Severity: critical    
Priority: P3 CC: caniszczyk, christian.halstrick, jamesblackburn+eclipse, matthias.sohn, robin.rosenberg, stefan.lay
Version: 0.8.1   
Target Milestone: 0.10.0   
Hardware: PC   
OS: Linux-GTK   
Whiteboard:
Bug Depends on: 331273, 333204    
Bug Blocks:    
Attachments:
Description Flags
call tree report for non-optimized code
none
call tree report for optimized code containing changes 2245,2283,2290 none

Description Jean-Philippe Gariepy CLA 2010-09-15 16:35:21 EDT
See stack trace from thread dump:

java.lang.Thread.State: RUNNABLE
	at sun.security.provider.DigestBase.engineUpdate(DigestBase.java:116)
	at java.security.MessageDigest$Delegate.engineUpdate(MessageDigest.java:538)
	at java.security.MessageDigest.update(MessageDigest.java:283)
	at org.eclipse.jgit.dircache.DirCacheEntry.<init>(DirCacheEntry.java:139)
	at org.eclipse.jgit.dircache.DirCache.readFrom(DirCache.java:381)
	at org.eclipse.jgit.dircache.DirCache.read(DirCache.java:332)
	at org.eclipse.jgit.dircache.DirCache.read(DirCache.java:156)
	at org.eclipse.jgit.dircache.DirCache.read(DirCache.java:179)
	at org.eclipse.egit.ui.internal.decorators.DecoratableResourceAdapter.createThreeWayTreeWalk(DecoratableResourceAdapter.java:349)
	at org.eclipse.egit.ui.internal.decorators.DecoratableResourceAdapter.<init>(DecoratableResourceAdapter.java:98)
	at org.eclipse.egit.ui.internal.decorators.GitLightweightDecorator.decorate(GitLightweightDecorator.java:211)
	at org.eclipse.ui.internal.decorators.LightweightDecoratorDefinition.decorate(LightweightDecoratorDefinition.java:263)
	at org.eclipse.ui.internal.decorators.LightweightDecoratorManager$LightweightRunnable.run(LightweightDecoratorManager.java:81)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
	at org.eclipse.ui.internal.decorators.LightweightDecoratorManager.decorate(LightweightDecoratorManager.java:365)
	at org.eclipse.ui.internal.decorators.LightweightDecoratorManager.getDecorations(LightweightDecoratorManager.java:347)
	at org.eclipse.ui.internal.decorators.DecorationScheduler$1.ensureResultCached(DecorationScheduler.java:371)
	at org.eclipse.ui.internal.decorators.DecorationScheduler$1.run(DecorationScheduler.java:331)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)

This thread never seems to terminate its jobs.  Note: We have a large repository with 25000 files (1.6G) in the tree.

Version is 0.8.4
Comment 1 Christian Halstrick CLA 2011-01-12 04:35:00 EST
Maybe this change http://egit.eclipse.org/r/#change,1927 will help to fix this issue as well.
It would be nice to have access to your big repo. Is this a public repo and can you share the URL?
Comment 2 Jean-Philippe Gariepy CLA 2011-01-12 11:58:48 EST
Sadly, this is a private repo.  I can't share a URL.
Comment 3 Philipp Thun CLA 2011-01-12 12:22:08 EST
I did some further analyses and can reproduce (very) long decoration runs when there are many files (1000+) in a single directory which is opened in Eclipse (project explorer). Furthermore the performance of the decoration process seems to be non-linear with regards to the number of files shown.

Can you provide some information on the structure of your repo, Jean-Philippe? Are there many files and only few directories? Are there some very big files? When do you see the 100% CPU utilization - when opening a folder within the project explorer?
Comment 4 Jean-Philippe Gariepy CLA 2011-01-12 14:51:38 EST
The average number of files per directory is 6.9 (max: 265), so no big directories.  The average directory depth of files is 10 (range: [1-18]).

Some files are rather large: about 300 files are bigger than 1 Mb (total is 1Gb).

I've checked the option "Re-decorate ancestors when decorating changed resources"
Maximum number of levels: Inf.

I'm not sure what is the condition that triggers this behaviour.  I'm trying to reproduce the problem with 0.9.1 but it doesn't happen.  However, there are lots of GUI operations in the Package Explorer (expand a project, refresh)  that will trigger the  org.eclipse.ui.internal.decorators.LightweightDecoratorDefinition.decorate() method.  This translates to high CPU usage for a few seconds (2-20).
Comment 5 Philipp Thun CLA 2011-01-13 10:00:30 EST
Thanks for providing this addtional information, Jean-Philippe.

I have continued my analyses and did some optimizations.

Here is the baseline - i.e. performance results for the following scenario:
1. Open a (Git shared) folder in Eclipse (project explorer) containing n files.
2. Wait until all resources have been decorated (time = t).

| n | 1000       | 2000       | 3000        |
---------------------------------------------
| t | 28 seconds | 66 seconds | 117 seconds |

First optimization: http://egit.eclipse.org/r/2245

| n | 1000       | 2000       | 3000        |
---------------------------------------------
| t | 14 seconds | 37 seconds | 74 seconds  |
|   | (-14)      | (-29)      | (-43)       |
Comment 6 Philipp Thun CLA 2011-01-13 11:12:36 EST
Second optimization: http://egit.eclipse.org/r/2247

| n | 1000      | 2000       | 3000        |
--------------------------------------------
| t | 9 seconds | 19 seconds | 32 seconds  |
|   | (-5)      | (-18)      | (-42)       |
Comment 7 Robin Rosenberg CLA 2011-01-14 03:28:18 EST
This a dup of Bug 333204 which also relates to 331273.

This proposed fix, while improving performance a little bit (and ok for that reason), does not resolve the issue. We can improve performance about ten times once we agree on the API changes in 331273.
Comment 8 Philipp Thun CLA 2011-01-14 07:15:11 EST
I found another issue related to decorations: when creating a new Eclipse workspace and then importing a Git project, all resources are decorated once even though they are not visible. When closing and re-opening Eclipse, this does not happen again.
Comment 9 Philipp Thun CLA 2011-01-19 05:03:46 EST
Re-measured performance for patch set 2 of change 2245 (immutable DirCache instance): same results
Comment 10 Philipp Thun CLA 2011-01-19 17:56:53 EST
Third optimization: http://egit.eclipse.org/r/2283

| n | 1000      | 2000      | 3000       | 4000       | 5000       |
--------------------------------------------------------------------
| t | 5 seconds | 8 seconds | 11 seconds | 15 seconds | 19 seconds |
|   | (-4)      | (-11)     | (-22)      |            |            |
Comment 11 Robin Rosenberg CLA 2011-01-20 02:41:23 EST
(In reply to comment #10)
> Third optimization: http://egit.eclipse.org/r/2283
> 
> | n | 1000      | 2000      | 3000       | 4000       | 5000       |
> --------------------------------------------------------------------
> | t | 5 seconds | 8 seconds | 11 seconds | 15 seconds | 19 seconds |
> |   | (-4)      | (-11)     | (-22)      |            |            |

These numbers are still dreadful. It cannot be that is has to take this
long. I've never seen these kind of delays, so maybe some layout cause
more slowdown than others.

Some more profiling might help, including counting the number attempt
to check for dirtyness.
Comment 12 Christian Halstrick CLA 2011-01-20 03:48:23 EST
I had some yourkit sessions with our decoration code. Although a complete tracing
of all egit/jgit code increases the runtime for such use-cases by factor 10 it still gives some nices hints. First hint: we still loose a lot of time because we
are computing hashes on every file.

I'll attach some yourkit reports if there is the first free timeslot during todays workshop
Comment 13 Philipp Thun CLA 2011-01-20 04:58:53 EST
I think we are pretty close to what is possible. To get a better estimation of the current timings, I deactivated the Git decorator and measured how long it takes to simply open a folder with n files in eclipse. This shows that the decoration time (t3 = t1 - t2) is down to just 2 to 5 seconds for 1000 to 5000 resources.

| n  | 1000      | 2000      | 3000       | 4000       | 5000       |
--------------------------------------------------------------------
| t1 | 5 seconds | 8 seconds | 11 seconds | 15 seconds | 19 seconds |
| t2 | 3 seconds | 6 seconds |  8 seconds | 11 seconds | 14 seconds |
---------------------------------------------------------------------
| t3 | 2 seconds | 2 seconds |  3 seconds |  4 seconds |  5 seconds |
Comment 14 Philipp Thun CLA 2011-01-20 12:17:54 EST
Patch for third optimization (2283)
Another small enhancement: http://egit.eclipse.org/r/2290

Re-measured t1 (time until decorations become visible). Re-calculated t3 (time difference compared to deactivated Git decorator).

| n  | 1000      | 2000      | 3000       | 4000       | 5000       |
---------------------------------------------------------------------
| t1 | 5 seconds | 8 seconds | 11 seconds | 14 seconds | 18 seconds |
|    |           |           |            | (-1)       | (-1)       |
| t2 | 3 seconds | 6 seconds |  8 seconds | 11 seconds | 14 seconds |
---------------------------------------------------------------------
| t3 | 2 seconds | 2 seconds |  3 seconds |  3 seconds |  4 seconds |

Here is a breakdown for t3:

t3a = GitDecoratorJob.run
t3b = GitLightweightDecorator.decorate

| n   | 1000   | 2000   | 3000    | 4000    | 5000    |
-------------------------------------------------------
| t3a | 400 ms | 700 ms | 1100 ms | 1500 ms | 1900 ms |
| t3b | 300 ms | 400 ms |  500 ms |  600 ms |  700 ms |
Comment 15 Robin Rosenberg CLA 2011-01-20 14:29:36 EST
Ok, the breakdown explains it. Just get it through code review also.
Comment 16 Christian Halstrick CLA 2011-01-20 18:46:47 EST
Created attachment 187251 [details]
call tree report for non-optimized code
Comment 17 Christian Halstrick CLA 2011-01-20 18:48:22 EST
I attach some Yourkit performance analysis files. They explain the huge improvement. 

I analyzed the usecase Philipp described for a folder with 1000 files. I used yourkit to create a calltree for all egit/jgit classes with cpu-times and invocation counts for all our methods. In the reports I expanded every part of the call-tree which used at least 5% to the overall cpu-time. I compared our current EGit/JGit master branches against a merged version containg the gerrit changes 2245/2283/2290.

If you look at the report for the non-optimized code you see that for decorating a folder with 1000 files which lies in a repo with 15000 files we constructed DirCacheEntries 15.000.000 times (yes, 15 million times). If for every of the 1000 files we construct a new dircache which contains entries for all of our 15.000 files in the repo then you reach 15mio instantiations.

Since we now use a single dircache which is in our case already read and cached before we trigger the decoration of the 1000-files-folder we are down from 15mio constructor calls down to 0. 

I attached the call-tree reports as text files and also the original yourkit snapshots.
Comment 18 Christian Halstrick CLA 2011-01-20 18:49:40 EST
Created attachment 187252 [details]
call tree report for optimized code containing changes 2245,2283,2290
Comment 19 Christian Halstrick CLA 2011-01-20 19:04:45 EST
Yourkit (www.yourkit.com) snapshots for

- non-optimized code:
http://dl.dropbox.com/u/15639867/Decorate1000_master.snapshot

-optimized code:
http://dl.dropbox.com/u/15639867/decorate1000_2245_2283_2290.snapshot
Comment 20 Philipp Thun CLA 2011-01-21 12:19:54 EST
Next change: http://egit.eclipse.org/r/2306

This change optimizes the following use cases:
1. Expand project node
2. Open folder
3. Refresh (project)

With the big test repository expanding the project node or refreshing everything took about 4 seconds. With this change this time is reduced to 0,3 seconds.

The initial time when opening eclipse or importing a project is not changed by this optimization.
Comment 21 Matthias Sohn CLA 2011-02-03 07:49:28 EST
(In reply to comment #20)
> Next change: http://egit.eclipse.org/r/2306
> 
> This change optimizes the following use cases:
> 1. Expand project node
> 2. Open folder
> 3. Refresh (project)
> 
> With the big test repository expanding the project node or refreshing
> everything took about 4 seconds. With this change this time is reduced to 0,3
> seconds.
> 
> The initial time when opening eclipse or importing a project is not changed by
> this optimization.

this improvement has been merged as c03562f2202231d55e84eb761bb1462fd69a584a
Comment 22 Philipp Thun CLA 2011-02-24 09:16:48 EST
EGit 0.11 includes many performance improvements.