Bug 161175 - JarPackageFragmentRoot slow to initialize
Summary: JarPackageFragmentRoot slow to initialize
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.2.1   Edit
Hardware: PC Windows XP
: P2 normal (vote)
Target Milestone: 3.3 M7   Edit
Assignee: Jerome Lanneluc CLA
QA Contact:
URL:
Whiteboard:
Keywords: performance
: 182899 (view as bug list)
Depends on:
Blocks:
 
Reported: 2006-10-16 23:03 EDT by Kelvin Cheung CLA
Modified: 2007-04-27 13:42 EDT (History)
3 users (show)

See Also:


Attachments
Screen capture on the call stack for my pop up action (112.52 KB, image/pjpeg)
2006-10-18 15:05 EDT, Kelvin Cheung CLA
no flags Details
Proposed fix and performance test (3.61 KB, patch)
2007-04-18 07:55 EDT, Jerome Lanneluc CLA
no flags Details | Diff
Improved fix and performance test (6.45 KB, patch)
2007-04-18 13:04 EDT, Jerome Lanneluc CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Kelvin Cheung CLA 2006-10-16 23:03:34 EDT
We noticed a performance regression in WTP that when a J2EE project contains a server container classpath which has large jars on the classpath, the project cache seems to take long to initialize and each project takes around the same time to initialize although they share the same container classpath.

To give some numbers, I noticed that per project, JarPackageFragmentRoot.initPackageFragToTypes is being invoked 60,000 times (usually triggered by the first IJavaProject.findType or even some validators in WTP).  This calls String.charAt 3.5million times and 300,000 times String.subString.  This converts to around 800ms to 1s per project on my machine.

A thought which I am not sure whether it’s feasible or not is that since in WTP, most of the J2EE projects get the classpath entries from the same container (server), is it possible to have some kind of cache based on server container?  As a result, this expensive operation does not need to be carried out on a per project basis.
Comment 1 Jerome Lanneluc CLA 2006-10-17 04:36:10 EDT
1. What is the Eclipse build ID where you saw the regression ?
2. What is the last Eclipse build ID you saw it was 'fast' ?
3. What is the exact scenario you got the numbers from ?
4. What are the steps to reproduce the slowness ?
5. Can you elaborate on the caching stategy ? What values do you suggest to cache ?
6. Have you tried giving more memory to Eclipse (-vmargs -Xmx1024m) ? This should increase the internal Java model cache.
Comment 2 Kelvin Cheung CLA 2006-10-18 15:02:48 EDT
1) This is a more general observation I had based on Eclipse 3.2, I even tried this with a patch that is supposed to fix 159325 but still see the same behaviour.

2) My comparison is based on a proprietary product on top of Eclipse 3.0.

3) I am running some Java code generation into WTP EJB project, but I was able to reproduce the same problem when running some WTP validators.

4) To reproduce this problem, I have created my own pop up action that calls IJavaProject.findType against 5 existing projects in my workspace:
    private void testSimpleAction(String projectName) {
      IWorkspaceRoot myWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
      IProject myProject = myWorkspaceRoot.getProject(projectName);
      if(myProject.exists()) {
        IJavaProject myJavaProject = JavaCore.create(myProject);
        try {
          long myTimer = System.currentTimeMillis();
          IType myType = myJavaProject.findType("abc", "Test");
          System.out.println("taken " + new Long(System.currentTimeMillis()-myTimer));
          System.out.println("abc.Test exists in project " + projectName +": " + myType.exists());
        }catch(Exception e) {
          e.printStackTrace(System.err);
        }
      }
    }
   on Eclipse 3.0:
   taken 1250
   abc.Test exists in project myProject: true
   taken 47
   abc.Test exists in project myProject1: true
   taken 31
   abc.Test exists in project myProject2: true
   taken 31
   abc.Test exists in project myProject3: true
   taken 47
   abc.Test exists in project myProject4: true
   on Eclipse 3.2:
   taken 1125
   abc.Test exists in project myProject: true
   taken 1015
   abc.Test exists in project myProject1: true
   taken 938
   abc.Test exists in project myProject2: true
   taken 2515
   abc.Test exists in project myProject3: true
   taken 922
   abc.Test exists in project myProject4: true

5) I am not sure whether this will work or not since I am not too familiar with JDT.  From a WTP developer perspective, most of the J2EE projects in the workspace would get a classpath container from the server (the place where most of these large jars come from) and this container is shared between multiple projects.  Is it possible to cache on the container first while each project has its own project specific cache on top?
   Typically, the .classpath files for the project look like this:
   <?xml version="1.0" encoding="UTF-8"?>
     <classpath>
	<classpathentry kind="src" path="ejbModule"/>
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/My server JRE container"/>
	<classpathentry kind="con" path="org.eclipse.jst.server.core.container/My Server jar container"/>
	<classpathentry exported="true" kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/>
	<classpathentry kind="output" path="build/classes"/>
      </classpath>

6) My test is based on -Xms40m -Xmx768m.

I will also attach a screen capture that shows my 5 IJavaProject.findType calls expand to 300,000 JarPackageFragmentRoot.initPackageFragToTypes calls (which call the String millions times).
Comment 3 Kelvin Cheung CLA 2006-10-18 15:05:31 EDT
Created attachment 52263 [details]
Screen capture on the call stack for my pop up action
Comment 4 Jerome Lanneluc CLA 2007-04-02 11:46:03 EDT
To be investigated during M7. 

One possibility is to find an already initialized project that uses the same external jar and use its cache to calculate the package fragments of the project being initialized.
Comment 5 Jerome Lanneluc CLA 2007-04-18 05:20:05 EDT
(In reply to comment #4)
> One possibility is to find an already initialized project that uses the same
> external jar and use its cache to calculate the package fragments of the
> project being initialized.
Unfortunately, if another project's cache has already been initialized, the package fragments information is not easily available and thus would cost more to retrieve (would have to walk the other project's cache and for each package fragment find out if it is of the same root).

Another option is to force the caching of the root if it is shared by more than one project. In this case chances are that it will be reused.

Comment 6 Jerome Lanneluc CLA 2007-04-18 05:21:30 EDT
*** Bug 182899 has been marked as a duplicate of this bug. ***
Comment 7 Jerome Lanneluc CLA 2007-04-18 07:55:20 EDT
Created attachment 64166 [details]
Proposed fix and performance test
Comment 8 Jerome Lanneluc CLA 2007-04-18 13:04:15 EDT
Created attachment 64214 [details]
Improved fix and performance test
Comment 9 Jerome Lanneluc CLA 2007-04-19 04:28:40 EDT
Fix and test released for 3.3M7 in HEAD.
Performance test released in perf_32x branch.
Comment 10 Frederic Fusier CLA 2007-04-27 13:42:23 EDT
Verified for 3.3M7 using perf results of I20070427-0010: testFindType() results are between +93.5% and +94.5%...
Note that to verify this bug, perf results were got directly from releng DB as the generation of HTML pages are not finished yet...