8189747: JDK9 javax.lang.model.util.Elements#getTypeElement regressed 1000x in performance.
authorjlahoda
Mon, 16 Jul 2018 12:35:25 +0200
changeset 51094 a49d106e9b7c
parent 51093 4db6e8715e35
child 51095 a8ee31fb99e1
8189747: JDK9 javax.lang.model.util.Elements#getTypeElement regressed 1000x in performance. Summary: Caching the results of Elements.getTypeElement/getPackageElement Reviewed-by: darcy
src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java
src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
test/langtools/tools/javac/modules/AnnotationProcessing.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java	Sun Jul 15 18:16:55 2018 +0300
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java	Mon Jul 16 12:35:25 2018 +0200
@@ -26,9 +26,11 @@
 package com.sun.tools.javac.model;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -184,7 +186,9 @@
     }
 
     private final Set<String> alreadyWarnedDuplicates = new HashSet<>();
+    private final Map<Pair<String, String>, Optional<Symbol>> resultCache = new HashMap<>();
 
+    @SuppressWarnings("unchecked")
     private <S extends Symbol> S unboundNameToSymbol(String methodName,
                                                      String nameStr,
                                                      Class<S> clazz) {
@@ -192,44 +196,46 @@
             return nameToSymbol(syms.noModule, nameStr, clazz);
         }
 
-        Set<S> found = new LinkedHashSet<>();
+        return (S) resultCache.computeIfAbsent(Pair.of(methodName, nameStr), p -> {
+            Set<S> found = new LinkedHashSet<>();
 
-        for (ModuleSymbol msym : modules.allModules()) {
-            S sym = nameToSymbol(msym, nameStr, clazz);
+            for (ModuleSymbol msym : modules.allModules()) {
+                S sym = nameToSymbol(msym, nameStr, clazz);
 
-            if (sym == null)
-                continue;
+                if (sym == null)
+                    continue;
 
-            if (clazz == ClassSymbol.class) {
-                // Always include classes
-                found.add(sym);
-            } else if (clazz == PackageSymbol.class) {
-                // In module mode, ignore the "spurious" empty packages that "enclose" module-specific packages.
-                // For example, if a module contains classes or package info in package p.q.r, it will also appear
-                // to have additional packages p.q and p, even though these packages have no content other
-                // than the subpackage.  We don't want those empty packages showing up in searches for p or p.q.
-                if (!sym.members().isEmpty() || ((PackageSymbol) sym).package_info != null) {
+                if (clazz == ClassSymbol.class) {
+                    // Always include classes
                     found.add(sym);
+                } else if (clazz == PackageSymbol.class) {
+                    // In module mode, ignore the "spurious" empty packages that "enclose" module-specific packages.
+                    // For example, if a module contains classes or package info in package p.q.r, it will also appear
+                    // to have additional packages p.q and p, even though these packages have no content other
+                    // than the subpackage.  We don't want those empty packages showing up in searches for p or p.q.
+                    if (!sym.members().isEmpty() || ((PackageSymbol) sym).package_info != null) {
+                        found.add(sym);
+                    }
                 }
             }
-        }
 
-        if (found.size() == 1) {
-            return found.iterator().next();
-        } else if (found.size() > 1) {
-            //more than one element found, produce a note:
-            if (alreadyWarnedDuplicates.add(methodName + ":" + nameStr)) {
-                String moduleNames = found.stream()
-                                          .map(s -> s.packge().modle)
-                                          .map(m -> m.toString())
-                                          .collect(Collectors.joining(", "));
-                log.note(Notes.MultipleElements(methodName, nameStr, moduleNames));
+            if (found.size() == 1) {
+                return Optional.of(found.iterator().next());
+            } else if (found.size() > 1) {
+                //more than one element found, produce a note:
+                if (alreadyWarnedDuplicates.add(methodName + ":" + nameStr)) {
+                    String moduleNames = found.stream()
+                                              .map(s -> s.packge().modle)
+                                              .map(m -> m.toString())
+                                              .collect(Collectors.joining(", "));
+                    log.note(Notes.MultipleElements(methodName, nameStr, moduleNames));
+                }
+                return Optional.empty();
+            } else {
+                //not found:
+                return Optional.empty();
             }
-            return null;
-        } else {
-            //not found, or more than one element found:
-            return null;
-        }
+        }).orElse(null);
     }
 
     /**
@@ -787,4 +793,8 @@
             throw new IllegalArgumentException(o.toString());
         return clazz.cast(o);
     }
+
+    public void newRound() {
+        resultCache.clear();
+    }
 }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Sun Jul 15 18:16:55 2018 +0300
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Mon Jul 16 12:35:25 2018 +0200
@@ -1270,6 +1270,7 @@
             modules.newRound();
             types.newRound();
             annotate.newRound();
+            elementUtils.newRound();
 
             boolean foundError = false;
 
--- a/test/langtools/tools/javac/modules/AnnotationProcessing.java	Sun Jul 15 18:16:55 2018 +0300
+++ b/test/langtools/tools/javac/modules/AnnotationProcessing.java	Mon Jul 16 12:35:25 2018 +0200
@@ -23,7 +23,7 @@
 
 /**
  * @test
- * @bug 8133884 8162711 8133896 8172158 8172262 8173636 8175119
+ * @bug 8133884 8162711 8133896 8172158 8172262 8173636 8175119 8189747
  * @summary Verify that annotation processing works.
  * @library /tools/lib
  * @modules
@@ -1389,6 +1389,19 @@
             .run()
             .writeAll();
 
+        //from source:
+        new JavacTask(tb)
+            .options("--module-source-path", moduleSrc.toString(),
+                     "--source-path", src.toString(),
+                     "-processorpath", System.getProperty("test.class.path"),
+                     "-processor", UnboundLookupGenerate.class.getName(),
+                     "-XDrawDiagnostics")
+            .outdir(classes)
+            .files(findJavaFiles(moduleSrc))
+            .run()
+            .writeAll()
+            .getOutput(OutputKind.DIRECT);
+
     }
 
     @SupportedAnnotationTypes("*")
@@ -1486,6 +1499,29 @@
 
     }
 
+    @SupportedAnnotationTypes("*")
+    public static final class UnboundLookupGenerate extends AbstractProcessor {
+
+        @Override
+        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+            if (processingEnv.getElementUtils().getTypeElement("nue.Nue") == null) {
+                try (Writer w = processingEnv.getFiler().createSourceFile("m1x/nue.Nue").openWriter()) {
+                    w.write("package nue; public class Nue {}");
+                } catch (IOException ex) {
+                    throw new IllegalStateException(ex);
+                }
+            }
+
+            return false;
+        }
+
+        @Override
+        public SourceVersion getSupportedSourceVersion() {
+            return SourceVersion.latest();
+        }
+
+    }
+
     @Test
     public void testWrongDefaultTargetModule(Path base) throws Exception {
         Path src = base.resolve("src");