diff options
author | Sven Strohschein | 2016-05-05 05:26:10 +0000 |
---|---|---|
committer | Manoj Palat | 2016-05-05 05:26:10 +0000 |
commit | 62f0009f204d08fcdbe82b163b7a48ac082dedc9 (patch) | |
tree | 41a9cf3ccfcd64e8d3622886d8d295e771198f97 | |
parent | e011cc03c880280f3916961950a745666f15125c (diff) | |
download | eclipse.jdt.core-62f0009f204d08fcdbe82b163b7a48ac082dedc9.tar.gz eclipse.jdt.core-62f0009f204d08fcdbe82b163b7a48ac082dedc9.tar.xz eclipse.jdt.core-62f0009f204d08fcdbe82b163b7a48ac082dedc9.zip |
Bug 484361 Huge performance slowdown between 4.6 M3 and M4
- 5 unit tests added to check if the performance optimizations have
(still) an effect (checks if searching for secondary types is the last
option to check).
- 1 integrative performance test added which triggered many searches for
secondary types before the performance optimizations were realized
Signed-off-by: Sven Strohschein <novanic@gmx.de>
5 files changed, 378 insertions, 3 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java index dfb1b92890..c2376593a0 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java @@ -54,9 +54,14 @@ import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.CompilationProgress; import org.eclipse.jdt.core.compiler.batch.BatchCompiler; import org.eclipse.jdt.core.tests.util.Util; +import org.eclipse.jdt.internal.compiler.batch.ClasspathDirectory; import org.eclipse.jdt.internal.compiler.batch.ClasspathJar; import org.eclipse.jdt.internal.compiler.batch.ClasspathLocation; +import org.eclipse.jdt.internal.compiler.batch.FileSystem; import org.eclipse.jdt.internal.compiler.batch.Main; +import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.util.ManifestAnalyzer; @SuppressWarnings({ "unchecked", "rawtypes" }) @@ -14556,4 +14561,42 @@ public void test439750() { "1 problem (1 warning)\n", true); } + +/** + * A fast exit/result is expected when secondary types are searched with the reserved class name "package-info", + * because there can not exist a secondary type with the name "package-info", because it is a reserved class name. + * This fast exit improves the performance, because the search for secondary types is very expensive regarding performance + * (all classes of a package have to get loaded, parsed and analyzed). + */ +public void testFileSystem_findSecondaryInClass() { + final String testScratchArea = "fileSystemTestScratchArea"; + + File testScratchAreaFile = new File(Util.getOutputDirectory(), testScratchArea); + try { + if(!testScratchAreaFile.exists()) { + testScratchAreaFile.mkdirs(); + } + + assertTrue(testScratchAreaFile.exists()); + + Classpath classpath = FileSystem.getClasspath(testScratchAreaFile.getPath(), null, null); + assertNotNull(classpath); + assertTrue(classpath instanceof ClasspathDirectory); + + ClasspathDirectory classpathDirectory = (ClasspathDirectory)classpath; + NameEnvironmentAnswer answer = classpathDirectory.findSecondaryInClass(TypeConstants.PACKAGE_INFO_NAME, null, null); + assertNull(answer); //No answer is expected, because "package-info" isn't a secondary type. + + try { + //When there is a call with another name like "package-info", an exception is expected, because the search can not get executed successfully + // when no value for qualifiedPackageName is provided. + classpathDirectory.findSecondaryInClass("X".toCharArray(), null, null); + fail("An exception is expected, because the parameter qualifiedPackageName can not be NULL!"); + } catch(Exception e) {} + } finally { + if(testScratchAreaFile.exists()) { + Util.delete(testScratchAreaFile); + } + } +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PackageBindingTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PackageBindingTest.java new file mode 100644 index 0000000000..827e53d393 --- /dev/null +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PackageBindingTest.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2016 Sven Strohschein and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sven Strohschein - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.compiler.regression; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.tests.util.AbstractCompilerTest; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; +import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress; + +public class PackageBindingTest extends AbstractCompilerTest +{ + public PackageBindingTest(String name) { + super(name); + } + + /** + * This test checks if it is searched for packages before searching for types. + * The search for packages is much faster than searching for types, therefore it should get executed before searching for types. + */ + public void test01() { + NameEnvironmentDummy nameEnv = new NameEnvironmentDummy(true); + + PackageBinding packageBinding = new PackageBinding(new LookupEnvironment(null, new CompilerOptions(), null, nameEnv)); + Binding resultBinding = packageBinding.getTypeOrPackage("java.lang".toCharArray()); + assertNotNull(resultBinding); + + assertTrue(nameEnv.isPackageSearchExecuted); + assertFalse(nameEnv.isTypeSearchExecuted); + } + + /** + * This test checks if it is searched for types when no package was found. + * The test {@link #test01()} checks if the package search is executed before the type search. + * The search for packages is much faster than searching for types, therefore it should get executed before searching for types. + */ + public void test02() { + NameEnvironmentDummy nameEnv = new NameEnvironmentDummy(false); + + PackageBinding packageBinding = new PackageBinding(new LookupEnvironment(null, new CompilerOptions(), null, nameEnv)); + Binding resultBinding = packageBinding.getTypeOrPackage("java.lang.String".toCharArray()); + assertNull(resultBinding); // (not implemented) + + assertTrue(nameEnv.isPackageSearchExecuted); + assertTrue(nameEnv.isTypeSearchExecuted); + } + + /** + * This test checks if {@link INameEnvironment#findType(char[], char[][])} is executed. + * INameEnvironment has no option to avoid the search for secondary types, therefore the search for secondary types is executed (when available). + */ + public void test03() { + NameEnvironmentDummy nameEnv = new NameEnvironmentDummy(false); + + LookupEnvironment lookupEnv = new LookupEnvironment(null, new CompilerOptions(), null, nameEnv); + PackageBinding packageBinding = lookupEnv.createPackage(new char[][]{"org/eclipse/jdt".toCharArray(), "org/eclipse/jdt/internal".toCharArray()}); + assertNotNull(packageBinding); + + assertTrue(nameEnv.isTypeSearchExecuted); //the method findType(char[], char[][]) should got executed (without an option to avoid the search for secondary types) + } + + /** + * This test checks if types are searched on creating a package, but without the search for secondary types. + * It isn't necessary to search for secondary types when creating a package, because a package name can not collide with a secondary type. + * The search for secondary types should not get executed, because the search for secondary types is very expensive regarding performance + * (all classes of a package have to get loaded, parsed and analyzed). + */ + public void test04() { + NameEnvironmentWithProgressDummy nameEnvWithProgress = new NameEnvironmentWithProgressDummy(); + + LookupEnvironment lookupEnv = new LookupEnvironment(null, new CompilerOptions(), null, nameEnvWithProgress); + PackageBinding packageBinding = lookupEnv.createPackage(new char[][]{"org/eclipse/jdt".toCharArray(), "org/eclipse/jdt/internal".toCharArray()}); + assertNotNull(packageBinding); + + assertTrue(nameEnvWithProgress.isTypeSearchExecutedWithSearchWithSecondaryTypes); //the method findType(char[], char[][], boolean) should got executed ... + assertFalse(nameEnvWithProgress.isTypeSearchWithSearchWithSecondaryTypes); //... but without the search for secondary types + } + + private class NameEnvironmentDummy implements INameEnvironment + { + private final boolean isPackage; + boolean isPackageSearchExecuted; + boolean isTypeSearchExecuted; + + NameEnvironmentDummy(boolean isPackage) { + this.isPackage = isPackage; + } + + @Override + public NameEnvironmentAnswer findType(char[][] compoundTypeName) { + this.isTypeSearchExecuted = true; + return null; + } + + @Override + public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) { + this.isTypeSearchExecuted = true; + return null; + } + + @Override + public boolean isPackage(char[][] parentPackageName, char[] packageName) { + this.isPackageSearchExecuted = true; + return this.isPackage; + } + + @Override + public void cleanup() { + } + } + + private class NameEnvironmentWithProgressDummy implements INameEnvironmentWithProgress + { + boolean isTypeSearchWithSearchWithSecondaryTypes; + boolean isTypeSearchExecutedWithSearchWithSecondaryTypes; + + NameEnvironmentWithProgressDummy() {} + + @Override + public NameEnvironmentAnswer findType(char[][] compoundTypeName) { + return null; + } + + @Override + public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) { + return null; + } + + @Override + public boolean isPackage(char[][] parentPackageName, char[] packageName) { + return false; + } + + @Override + public void cleanup() { + } + + @Override + public void setMonitor(IProgressMonitor monitor) { + } + + @Override + public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName, boolean searchWithSecondaryTypes) { + this.isTypeSearchExecutedWithSearchWithSecondaryTypes = true; + this.isTypeSearchWithSearchWithSecondaryTypes = searchWithSecondaryTypes; + return null; + } + } +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java index 4e78c1fe14..69a99e5419 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -86,6 +86,7 @@ public static Test suite() { standardTests.add(ManifestAnalyzerTest.class); standardTests.add(InitializationTests.class); standardTests.add(ResourceLeakTests.class); + standardTests.add(PackageBindingTest.class); // add all javadoc tests for (int i=0, l=JavadocTest.ALL_CLASSES.size(); i<l; i++) { diff --git a/org.eclipse.jdt.core.tests.performance/src/org/eclipse/jdt/core/tests/performance/AllPerformanceTests.java b/org.eclipse.jdt.core.tests.performance/src/org/eclipse/jdt/core/tests/performance/AllPerformanceTests.java index 18a41e942a..0178650dc7 100644 --- a/org.eclipse.jdt.core.tests.performance/src/org/eclipse/jdt/core/tests/performance/AllPerformanceTests.java +++ b/org.eclipse.jdt.core.tests.performance/src/org/eclipse/jdt/core/tests/performance/AllPerformanceTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -47,13 +47,14 @@ public class AllPerformanceTests extends junit.framework.TestCase { /** * Additional test class(es). * - * Classes put in this list will be run only if "add" VM parameter is added + * Classes put in this list will be run only if "add" VM parameter (-Dadd=true) is added * while running JUnit test suite. * * @see #ADD */ public static Class[] getAdditionalTestClasses() { return new Class[] { + SecondaryTypesPerformanceTest.class }; } diff --git a/org.eclipse.jdt.core.tests.performance/src/org/eclipse/jdt/core/tests/performance/SecondaryTypesPerformanceTest.java b/org.eclipse.jdt.core.tests.performance/src/org/eclipse/jdt/core/tests/performance/SecondaryTypesPerformanceTest.java new file mode 100644 index 0000000000..c42b8c8af6 --- /dev/null +++ b/org.eclipse.jdt.core.tests.performance/src/org/eclipse/jdt/core/tests/performance/SecondaryTypesPerformanceTest.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2016 Sven Strohschein and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sven Strohschein - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.performance; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.tests.util.Util; +import org.eclipse.test.performance.PerformanceTestCase; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class SecondaryTypesPerformanceTest extends PerformanceTestCase { + + private static final String testScratchArea = Util.getOutputDirectory() + File.separator + "enumPerformanceTestScratchArea"; + private static final int numberOfClasspathsToCreate = 10; //can get set higher, but not lower than 3 (the first 3 packages are used within the test case) + + public static Test suite() { + TestSuite suite = new TestSuite(SecondaryTypesPerformanceTest.class.getName()); + suite.addTestSuite(SecondaryTypesPerformanceTest.class); + return suite; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + File testScratchAreaFile = new File(testScratchArea); + if(!testScratchAreaFile.exists()) { + testScratchAreaFile.mkdir(); + + File packageFile = new File(testScratchAreaFile, "test" + File.separator + "performance"); + packageFile.mkdirs(); + writeDummySourceClasses(packageFile, "test.performance"); + + Set<String> paths = createClasspathStrings(); + for(String path: paths) { + File pathFile = new File(path); + pathFile.mkdir(); + + writeDummySourceClasses(pathFile, "test.performance." + pathFile.getName()); + } + } + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + + File testScratchAreaFile = new File(testScratchArea); + if(testScratchAreaFile.exists()) { + Util.delete(testScratchAreaFile); + } + } + + public void test01() { + List<String> classpathList = new ArrayList<>(); + classpathList.addAll(createClasspathStrings()); + classpathList.add(testScratchArea); + + for (int i = 0; i<10; ++i) { + ASTParser parser = ASTParser.newParser(AST.JLS8); + parser.setResolveBindings(true); + parser.setStatementsRecovery(true); + parser.setBindingsRecovery(true); + parser.setCompilerOptions(createCompilerOptions()); + parser.setUnitName("X"); + parser.setKind(ASTParser.K_COMPILATION_UNIT); + parser.setEnvironment(classpathList.toArray(new String[classpathList.size()]), null, null, true); + parser.setSource(("" + + "import test.performance.test1.*;\n" + + "public enum X\n" + + "{\n" + + " TEST;\n" + + "\n" + + " public void method() {\n" + + " new Z().toString();\n" + + " new test.performance.test2.Z().toString();\n" + + " new test.performance.test3.Z().toString();\n" + + "\n" + + " }\n" + + "}").toCharArray()); + + startMeasuring(); + ASTNode theAST = parser.createAST(null); + theAST.accept(new ASTVisitor() {}); + stopMeasuring(); + } + + //Commit measure + commitMeasurements(); + assertPerformance(); + } + + private Map<String, String> createCompilerOptions() { + Map<String, String> compilerOptions = new HashMap<>(10); + compilerOptions.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_8); + compilerOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_8); + compilerOptions.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_8); + compilerOptions.put(JavaCore.COMPILER_TASK_PRIORITIES, JavaCore.COMPILER_TASK_PRIORITY_HIGH); + compilerOptions.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED); + return compilerOptions; + } + + private Set<String> createClasspathStrings() { + Set<String> paths = new LinkedHashSet<>(numberOfClasspathsToCreate); + for(int i = 1; i <= numberOfClasspathsToCreate; i++) { + paths.add(testScratchArea + File.separator + "test" + File.separator + "performance" + File.separator + "test" + i); + } + return paths; + } + + private void writeDummySourceClasses(File aPath, String packageName) throws IOException { + writeFile(new File(aPath, "Z.java"), "public class Z {}"); + + "ABC".toLowerCase(Locale.ENGLISH); + + for(int i = 1; i <= 100; i++) { + String sourceContent = "package " + packageName + ";\n" + + "\n" + + "import java.util.Locale;\n" + + "\n" + + "public class X" + i + " {\n" + + "\tpublic X" + i + "() {\n" + + "\t\t\"ABC\".toLowerCase(Locale.ENGLISH);\n" + + "new Object() {\n" + + " @Override\n" + + " public String toString() {\n" + + " return \"Test\";\n" + + " }\n" + + " }.toString();" + + "\t}\n" + + "}"; + + writeFile(new File(aPath, "X" + i + ".java"), sourceContent); + } + } + + private void writeFile(File aFile, String aSource) throws IOException { + FileWriter fileWriter = new FileWriter(aFile); + try { + fileWriter.write(aSource); + } finally { + fileWriter.close(); + } + } +}
\ No newline at end of file |