Community
Participate
Working Groups
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.
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.
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).
Created attachment 52263 [details] Screen capture on the call stack for my pop up action
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.
(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.
*** Bug 182899 has been marked as a duplicate of this bug. ***
Created attachment 64166 [details] Proposed fix and performance test
Created attachment 64214 [details] Improved fix and performance test
Fix and test released for 3.3M7 in HEAD. Performance test released in perf_32x branch.
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...