Revision: 8789
Author: [email protected]
Date: Wed Sep 15 11:45:41 2010
Log: Optimize ResourceOracle refresh by doing multiple oracles at the same time

Review at http://gwt-code-reviews.appspot.com/887801

http://code.google.com/p/google-web-toolkit/source/detail?r=8789

Modified:
 /trunk/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
 /trunk/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java
/trunk/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java /trunk/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java /trunk/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java /trunk/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java

=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java Tue Aug 17 08:14:13 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java Wed Sep 15 11:45:41 2010
@@ -340,7 +340,7 @@
             NON_JAVA_RESOURCES, pathPrefix.shouldReroot()));
       }
       lazyResourcesOracle.setPathPrefixes(newPathPrefixes);
-      lazyResourcesOracle.refresh(TreeLogger.NULL);
+      ResourceOracleImpl.refresh(TreeLogger.NULL, lazyResourcesOracle);
     }
     return lazyResourcesOracle;
   }
@@ -402,11 +402,11 @@
         + "'");

     // Refresh resource oracles.
-    lazyPublicOracle.refresh(logger);
-    lazySourceOracle.refresh(logger);
-
-    if (lazyResourcesOracle != null) {
-      lazyResourcesOracle.refresh(logger);
+    if (lazyResourcesOracle == null) {
+ ResourceOracleImpl.refresh(logger, lazyPublicOracle, lazySourceOracle);
+    } else {
+      ResourceOracleImpl.refresh(
+          logger, lazyPublicOracle, lazySourceOracle, lazyResourcesOracle);
     }
     moduleDefEvent.end();
   }
@@ -482,13 +482,13 @@
       lazyPublicOracle = new ResourceOracleImpl(branch);
       lazyPublicOracle.setPathPrefixes(publicPrefixSet);
     }
-    lazyPublicOracle.refresh(branch);

     // Create the source path.
     branch = Messages.SOURCE_PATH_LOCATIONS.branch(logger, null);
     lazySourceOracle = new ResourceOracleImpl(branch);
     lazySourceOracle.setPathPrefixes(sourcePrefixSet);
-    lazySourceOracle.refresh(branch);
+
+    ResourceOracleImpl.refresh(logger, lazyPublicOracle, lazySourceOracle);
     if (lazySourceOracle.getResources().isEmpty()) {
       branch.log(TreeLogger.WARN,
           "No source path entries; expect subsequent failures", null);
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java Fri Feb 6 13:06:24 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/resource/impl/ClassPathEntry.java Wed Sep 15 11:45:41 2010
@@ -17,6 +17,8 @@

 import com.google.gwt.core.ext.TreeLogger;

+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;

 /**
@@ -37,6 +39,22 @@
    */
public abstract Map<AbstractResource, PathPrefix> findApplicableResources(
       TreeLogger logger, PathPrefixSet pathPrefixSet);
+
+  /**
+   * Finds applicable resources for a list of pathPrefixSets, returning a
+   * distinct answer for each set.
+   *
+   * @see #findApplicableResources(TreeLogger, PathPrefixSet)
+   */
+  public List<Map<AbstractResource, PathPrefix>> findApplicableResources(
+      TreeLogger logger, List<PathPrefixSet> pathPrefixSets) {
+    List<Map<AbstractResource, PathPrefix>> results = new ArrayList<
+        Map<AbstractResource, PathPrefix>>(pathPrefixSets.size());
+    for (PathPrefixSet pathPrefixSet : pathPrefixSets) {
+      results.add(findApplicableResources(logger, pathPrefixSet));
+    }
+    return results;
+  }

   /**
    * Gets a URL string that describes this class path entry.
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java Wed Aug 4 09:54:49 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/resource/impl/DirectoryClassPathEntry.java Wed Sep 15 11:45:41 2010
@@ -16,10 +16,13 @@
 package com.google.gwt.dev.resource.impl;

 import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.util.collect.Lists;
 import com.google.gwt.dev.util.msg.Message1String;

 import java.io.File;
+import java.util.ArrayList;
 import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Map;

 /**
@@ -28,15 +31,9 @@
 public class DirectoryClassPathEntry extends ClassPathEntry {

   private static class Messages {
- static final Message1String NOT_DESCENDING_INTO_DIR = new Message1String(
-        TreeLogger.SPAM, "Prefix set does not include dir: $0");
-
     static final Message1String DESCENDING_INTO_DIR = new Message1String(
         TreeLogger.SPAM, "Descending into dir: $0");

-    static final Message1String EXCLUDING_FILE = new Message1String(
-        TreeLogger.DEBUG, "Filter excludes file: $0");
-
     static final Message1String INCLUDING_FILE = new Message1String(
         TreeLogger.DEBUG, "Including file: $0");
   }
@@ -56,12 +53,25 @@
     this.dir = dir;
     this.location = dir.toURI().toString();
   }
+
+  @Override
+  public List<Map<AbstractResource, PathPrefix>> findApplicableResources(
+      TreeLogger logger, List<PathPrefixSet> pathPrefixSets) {
+ List<Map<AbstractResource, PathPrefix>> results = new ArrayList<Map<AbstractResource, PathPrefix>>(
+        pathPrefixSets.size());
+    for (int i = 0, c = pathPrefixSets.size(); i < c; ++i) {
+      results.add(new IdentityHashMap<AbstractResource, PathPrefix>());
+    }
+    descendToFindResources(logger, pathPrefixSets, results, dir, "");
+    return results;
+  }

   @Override
   public Map<AbstractResource, PathPrefix> findApplicableResources(
       TreeLogger logger, PathPrefixSet pathPrefixSet) {
Map<AbstractResource, PathPrefix> results = new IdentityHashMap<AbstractResource, PathPrefix>();
-    descendToFindResources(logger, pathPrefixSet, results, dir, "");
+    descendToFindResources(logger, Lists.create(pathPrefixSet),
+        Lists.create(results), dir, "");
     return results;
   }

@@ -72,16 +82,19 @@

   /**
    * @param logger logs progress
-   * @param resources the accumulating set of resources (each with the
+ * @param pathPrefixSets the sets of path prefixes to determine what resources
+   *          are included
+   * @param results the accumulating sets of resources (each with the
    *          corresponding pathPrefix) found
    * @param dir the file or directory to consider
    * @param dirPath the abstract path name associated with 'parent', which
    *          explicitly does not include the classpath entry in its path
    */
   private void descendToFindResources(TreeLogger logger,
- PathPrefixSet pathPrefixSet, Map<AbstractResource, PathPrefix> resources,
-      File dir, String dirPath) {
+      List<PathPrefixSet> pathPrefixSets,
+ List<Map<AbstractResource, PathPrefix>> results, File dir, String dirPath) {
     assert (dir.isDirectory()) : dir + " is not a directory";
+    int len = pathPrefixSets.size();

     // Assert: this directory is included in the path prefix set.

@@ -90,26 +103,23 @@
       String childPath = dirPath + child.getName();
       if (child.isDirectory()) {
         String childDirPath = childPath + "/";
-        if (pathPrefixSet.includesDirectory(childDirPath)) {
-          Messages.DESCENDING_INTO_DIR.log(logger, child.getAbsolutePath(),
-              null);
-          descendToFindResources(logger, pathPrefixSet, resources, child,
-              childDirPath);
-        } else {
- Messages.NOT_DESCENDING_INTO_DIR.log(logger, child.getAbsolutePath(),
-              null);
+        for (int i = 0; i < len; ++i) {
+          if (pathPrefixSets.get(i).includesDirectory(childDirPath)) {
+ Messages.DESCENDING_INTO_DIR.log(logger, child.getPath(), null);
+            descendToFindResources(logger, pathPrefixSets, results, child,
+                childDirPath);
+            break;
+          }
         }
       } else if (child.isFile()) {
-        PathPrefix prefix = null;
-        if ((prefix = pathPrefixSet.includesResource(childPath)) != null) {
-          Messages.INCLUDING_FILE.log(logger, childPath, null);
-          FileResource r = new FileResource(this, childPath, child);
-          resources.put(r, prefix);
-        } else {
-          Messages.EXCLUDING_FILE.log(logger, childPath, null);
-        }
-      } else {
-        Messages.EXCLUDING_FILE.log(logger, childPath, null);
+        for (int i = 0; i < len; ++i) {
+          PathPrefix prefix = null;
+ if ((prefix = pathPrefixSets.get(i).includesResource(childPath)) != null) {
+            Messages.INCLUDING_FILE.log(logger, childPath, null);
+            FileResource r = new FileResource(this, childPath, child);
+            results.get(i).put(r, prefix);
+          }
+        }
       }
     }
   }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java Fri Aug 6 12:01:02 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/resource/impl/ResourceOracleImpl.java Wed Sep 15 11:45:41 2010
@@ -40,8 +40,8 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
+import java.util.Set;

 /**
  * The normal implementation of {...@link ResourceOracle}.
@@ -51,24 +51,11 @@
   private static class Messages {
     static final Message1String EXAMINING_PATH_ROOT = new Message1String(
         TreeLogger.DEBUG, "Searching for resources within $0");
-
static final Message1String IGNORING_SHADOWED_RESOURCE = new Message1String(
         TreeLogger.DEBUG,
"Resource '$0' is being shadowed by another resource higher in the classpath having the same name; this one will not be used");
-
-    static final Message1String NEW_RESOURCE_FOUND = new Message1String(
-        TreeLogger.DEBUG, "Found new resource: $0");
-
static final Message0 REFRESHING_RESOURCES = new Message0(TreeLogger.TRACE,
         "Refreshing resources");
-
- static final Message1String RESOURCE_BECAME_INVALID_BECAUSE_IT_IS_STALE = new Message1String(
-        TreeLogger.SPAM,
- "Resource '$0' has been modified since it was last loaded and needs to be reloaded");
-
- static final Message1String RESOURCE_BECAME_INVALID_BECAUSE_IT_MOVED = new Message1String(
-        TreeLogger.DEBUG,
- "Resource '$0' was found on a different classpath entry and needs to be reloaded");
   }

   /**
@@ -183,6 +170,98 @@
       return null;
     }
   }
+
+  /**
+   * Rescans the associated paths to recompute the available resources.
+   *
+   * @param logger status and error details are written here
+   */
+ public static void refresh(TreeLogger logger, ResourceOracleImpl... oracles) {
+    int len = oracles.length;
+    if (len == 0) {
+      return;
+    }
+
+    Event resourceOracle =
+ SpeedTracerLogger.start(CompilerEventType.RESOURCE_ORACLE, "phase", "refresh");
+    TreeLogger refreshBranch = Messages.REFRESHING_RESOURCES.branch(logger,
+        null);
+
+    /*
+ * Allocate fresh data structures in anticipation of needing to honor the + * "new identity for the collections if anything changes" guarantee. Use a
+     * LinkedHashMap because we do not want the order to change.
+     */
+    List<Map<String, ResourceData>> resourceDataMaps = new ArrayList<
+        Map<String, ResourceData>>();
+
+    List<PathPrefixSet> pathPrefixSets = new ArrayList<PathPrefixSet>();
+    for (ResourceOracleImpl oracle : oracles) {
+      if (oracle.classPath != oracles[0].classPath) {
+        throw new IllegalArgumentException();
+      }
+      resourceDataMaps.add(new LinkedHashMap<String, ResourceData>());
+      pathPrefixSets.add(oracle.pathPrefixSet);
+    }
+
+    /*
+ * Walk across path roots (i.e. classpath entries) in priority order. This + * is a "reverse painter's algorithm", relying on being careful never to add + * a resource that has already been added to the new map under construction + * to create the effect that resources founder earlier on the classpath take
+     * precedence.
+     *
+ * Exceptions: super has priority over non-super; and if there are two super
+     * resources with the same path, the one with the higher-priority path
+     * prefix wins.
+     */
+    for (ClassPathEntry pathRoot : oracles[0].classPath) {
+ TreeLogger branchForClassPathEntry = Messages.EXAMINING_PATH_ROOT.branch(
+          refreshBranch, pathRoot.getLocation(), null);
+
+ List<Map<AbstractResource, PathPrefix>> resourceToPrefixMaps = pathRoot.findApplicableResources(
+          branchForClassPathEntry, pathPrefixSets);
+      for (int i = 0; i < len; ++i) {
+ Map<String, ResourceData> resourceDataMap = resourceDataMaps.get(i);
+        Map<AbstractResource, PathPrefix> resourceToPrefixMap =
+          resourceToPrefixMaps.get(i);
+        for (Entry<AbstractResource, PathPrefix> entry :
+            resourceToPrefixMap.entrySet()) {
+          ResourceData newCpeData = new ResourceData(entry.getKey(),
+              entry.getValue());
+          String resourcePath = newCpeData.resource.getPath();
+          ResourceData oldCpeData = resourceDataMap.get(resourcePath);
+          // Old wins unless the new resource has higher priority.
+          if (oldCpeData == null || oldCpeData.compareTo(newCpeData) < 0) {
+            resourceDataMap.put(resourcePath, newCpeData);
+          } else {
+ Messages.IGNORING_SHADOWED_RESOURCE.log(branchForClassPathEntry,
+                resourcePath, null);
+          }
+        }
+      }
+    }
+
+    for (int i = 0; i < len; ++i) {
+      Map<String, ResourceData> resourceDataMap = resourceDataMaps.get(i);
+      Map<String, Resource> externalMap = new HashMap<String, Resource>();
+      Set<Resource> externalSet = new HashSet<Resource>();
+ for (Entry<String, ResourceData> entry : resourceDataMap.entrySet()) {
+        String path = entry.getKey();
+        ResourceData data = entry.getValue();
+        externalMap.put(path, data.resource);
+        externalSet.add(data.resource);
+      }
+
+ // Update exposed collections with new (unmodifiable) data structures. + oracles[i].exposedResources = Collections.unmodifiableSet(externalSet); + oracles[i].exposedResourceMap = Collections.unmodifiableMap(externalMap);
+      oracles[i].exposedPathNames = Collections.unmodifiableSet(
+          externalMap.keySet());
+    }
+
+    resourceOracle.end();
+  }

   private static void addAllClassPathEntries(TreeLogger logger,
       ClassLoader classLoader, List<ClassPathEntry> classPath) {
@@ -240,7 +319,7 @@
     return classPath;
   }

- private final List<ClassPathEntry> classPath = new ArrayList<ClassPathEntry>();
+  private final List<ClassPathEntry> classPath;

   private Set<String> exposedPathNames = Collections.emptySet();

@@ -252,13 +331,11 @@

   /**
    * Constructs a {...@link ResourceOracleImpl} from a set of
- * {...@link ClassPathEntry ClassPathEntries}. The passed-in list is copied, but
-   * the underlying entries in the list are not. Those entries must be
-   * effectively immutable except for reflecting actual changes to the
-   * underlying resources.
+ * {...@link ClassPathEntry ClassPathEntries}. The list is held by reference and
+   * must not be modified.
    */
   public ResourceOracleImpl(List<ClassPathEntry> classPath) {
-    this.classPath.addAll(classPath);
+    this.classPath = classPath;
   }

   /**
@@ -300,72 +377,6 @@
   public Set<Resource> getResources() {
     return exposedResources;
   }
-
-  /**
-   * Rescans the associated paths to recompute the available resources.
-   *
-   * @param logger status and error details are written here
-   */
-  public void refresh(TreeLogger logger) {
-    Event resourceOracle =
- SpeedTracerLogger.start(CompilerEventType.RESOURCE_ORACLE, "phase", "refresh");
-    TreeLogger refreshBranch = Messages.REFRESHING_RESOURCES.branch(logger,
-        null);
-
-    /*
- * Allocate fresh data structures in anticipation of needing to honor the - * "new identity for the collections if anything changes" guarantee. Use a
-     * LinkedHashMap because we do not want the order to change.
-     */
- Map<String, ResourceData> newInternalMap = new LinkedHashMap<String, ResourceData>();
-
-    /*
- * Walk across path roots (i.e. classpath entries) in priority order. This - * is a "reverse painter's algorithm", relying on being careful never to add - * a resource that has already been added to the new map under construction - * to create the effect that resources founder earlier on the classpath take
-     * precedence.
-     *
- * Exceptions: super has priority over non-super; and if there are two super
-     * resources with the same path, the one with the higher-priority path
-     * prefix wins.
-     */
-    for (ClassPathEntry pathRoot : classPath) {
- TreeLogger branchForClassPathEntry = Messages.EXAMINING_PATH_ROOT.branch(
-          refreshBranch, pathRoot.getLocation(), null);
-
- Map<AbstractResource, PathPrefix> resourceToPrefixMap = pathRoot.findApplicableResources(
-          branchForClassPathEntry, pathPrefixSet);
- for (Entry<AbstractResource, PathPrefix> entry : resourceToPrefixMap.entrySet()) {
-        ResourceData newCpeData = new ResourceData(entry.getKey(),
-            entry.getValue());
-        String resourcePath = newCpeData.resource.getPath();
-        ResourceData oldCpeData = newInternalMap.get(resourcePath);
-        // Old wins unless the new resource has higher priority.
-        if (oldCpeData == null || oldCpeData.compareTo(newCpeData) < 0) {
-          newInternalMap.put(resourcePath, newCpeData);
-        } else {
-          Messages.IGNORING_SHADOWED_RESOURCE.log(branchForClassPathEntry,
-              resourcePath, null);
-        }
-      }
-    }
-
-    Map<String, Resource> externalMap = new HashMap<String, Resource>();
-    Set<Resource> externalSet = new HashSet<Resource>();
-    for (Entry<String, ResourceData> entry : newInternalMap.entrySet()) {
-      String path = entry.getKey();
-      ResourceData data = entry.getValue();
-      externalMap.put(path, data.resource);
-      externalSet.add(data.resource);
-    }
-
-    // Update exposed collections with new (unmodifiable) data structures.
-    exposedResources = Collections.unmodifiableSet(externalSet);
-    exposedResourceMap = Collections.unmodifiableMap(externalMap);
-    exposedPathNames = Collections.unmodifiableSet(externalMap.keySet());
-    resourceOracle.end();
-  }

   public void setPathPrefixes(PathPrefixSet pathPrefixSet) {
     this.pathPrefixSet = pathPrefixSet;
=======================================
--- /trunk/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java Tue Aug 31 15:23:10 2010 +++ /trunk/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplRealClasspathTest.java Wed Sep 15 11:45:41 2010
@@ -66,7 +66,7 @@
     pathPrefixSet.add(makeJunitPrefix());
     pathPrefixSet.add(makeThisClassPrefix());
     resourceOracle.setPathPrefixes(pathPrefixSet);
-    resourceOracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, resourceOracle);
     Map<String, Resource> resourceMap = resourceOracle.getResourceMap();
     assertEquals(2, resourceMap.size());
   }
@@ -76,17 +76,17 @@
     pathPrefixSet.add(makeJunitPrefix());
     pathPrefixSet.add(makeThisClassPrefix());
     resourceOracle.setPathPrefixes(pathPrefixSet);
-    resourceOracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, resourceOracle);
     Map<String, Resource> resourceMap = resourceOracle.getResourceMap();
     assertEquals(2, resourceMap.size());

     // Plain refresh should have no effect.
-    resourceOracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, resourceOracle);
     assertResourcesEqual(resourceMap, resourceOracle.getResourceMap());

     // Setting same path entries should have no effect.
     resourceOracle.setPathPrefixes(pathPrefixSet);
-    resourceOracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, resourceOracle);
     assertResourcesEqual(resourceMap, resourceOracle.getResourceMap());

     // Setting identical path entries should have no effect.
@@ -94,17 +94,17 @@
     pathPrefixSet.add(makeJunitPrefix());
     pathPrefixSet.add(makeThisClassPrefix());
     resourceOracle.setPathPrefixes(pathPrefixSet);
-    resourceOracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, resourceOracle);
     assertResourcesEqual(resourceMap, resourceOracle.getResourceMap());

     // Setting identical result should have no effect.
     pathPrefixSet.add(makeJunitPrefix());
-    resourceOracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, resourceOracle);
     assertResourcesEqual(resourceMap, resourceOracle.getResourceMap());

     // Actually change the working set.
     pathPrefixSet.add(makeThisClassPrefixPlus());
-    resourceOracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, resourceOracle);
     assertEquals(3, resourceOracle.getResourceMap().size());
   }
 }
=======================================
--- /trunk/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java Tue Aug 31 15:23:10 2010 +++ /trunk/dev/core/test/com/google/gwt/dev/resource/impl/ResourceOracleImplTest.java Wed Sep 15 11:45:41 2010
@@ -511,10 +511,6 @@
         makeJavaLangPrefix());
   }

-  /**
-   * @param classPathEntry
-   * @param string
-   */
private void assertJarEntry(ClassPathEntry classPathEntry, String expected) {
     assertTrue("Should be instance of ZipFileClassPathEntry",
         classPathEntry instanceof ZipFileClassPathEntry);
@@ -545,9 +541,9 @@

   private ResourceOracleSnapshot refreshAndSnapshot(TreeLogger logger,
       ResourceOracleImpl oracle) {
-    oracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, oracle);
     ResourceOracleSnapshot snapshot1 = new ResourceOracleSnapshot(oracle);
-    oracle.refresh(logger);
+    ResourceOracleImpl.refresh(logger, oracle);
     ResourceOracleSnapshot snapshot2 = new ResourceOracleSnapshot(oracle);
     snapshot1.assertSameCollections(snapshot2);
     return snapshot1;

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to