Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement loaded feature caching to reduce load path searching.

This is an idea borrowed from Ruby 2.0.0, whereby the short names
of loaded features are held in an internal structure and used as
a first-line cache to avoid re-searching for re-required files.
Before this patch, in both JRuby and MRI, re-required files had to
do a full load path search every time because the loaded features
list only held the full canonical paths, which are only available
after searching for the proper load path entry under which the
file exists. With the patch in place, re-required files will
usually be immediately found in the cache.

The population and invalidation of the cache proceeds as follows:

* A reference is held to a dup of the loaded features array. When
this reference is captured.
* When adding a new feature to loaded features, we re-capture
the dup'ed features array and add the short name to a separate
internal cache.
* As long as the dup'ed features array is non-null and matches
the current loaded features array, we know we can safely use the
loaded features index.
* When the dup'ed features array does not match, we clear the
loaded features index and start populating it anew. Since most
typical Ruby code does not modify the loaded features array
directly, this should be rare.

Numbers with and without this change show the performance gained.
The following benchmark populates load path with 1000 entries and
requires the same file 1000 times.

Before:

system ~/projects/jruby $ rvm jruby do jruby bench_loaded_features.rb 1000 1000
  8.110000   3.880000  11.990000 (  9.708000)

After:

system ~/projects/jruby $ jruby bench_loaded_features.rb 1000 1000
  0.990000   0.050000   1.040000 (  0.324000)
  • Loading branch information...
commit daba4d3c0ceafebcadca3eead61d4aaf12ed015b 1 parent 43983c1
@headius headius authored
View
6 src/org/jruby/RubyArray.java
@@ -416,7 +416,11 @@ public int getLength() {
public IRubyObject[] toJavaArrayMaybeUnsafe() {
return (!isShared && begin == 0 && values.length == realLength) ? values : toJavaArray();
- }
+ }
+
+ public IRubyObject[] getValues() {
+ return values;
+ }
/** rb_ary_make_shared
*
View
34 src/org/jruby/runtime/load/LoadService.java
@@ -43,10 +43,12 @@
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.JarFile;
@@ -204,6 +206,8 @@
protected RubyArray loadPath;
protected StringArraySet loadedFeatures;
+ protected RubyArray loadedFeaturesDup;
+ private final Map<String, String> loadedFeaturesIndex = new ConcurrentHashMap<String, String>();
protected final Map<String, Library> builtinLibraries = new HashMap<String, Library>();
protected final Map<String, JarFile> jarFiles = new HashMap<String, JarFile>();
@@ -296,9 +300,20 @@ public void addPaths(String... additionalDirectories) {
addPath(dir);
}
}
+
+ protected boolean isFeatureInIndex(String shortName) {
+ return loadedFeaturesIndex.containsKey(shortName);
+ }
- protected void addLoadedFeature(String name) {
+ protected void addLoadedFeature(String shortName, String name) {
loadedFeatures.append(RubyString.newString(runtime, name));
+
+ addFeatureToIndex(shortName, name);
+ }
+
+ protected void addFeatureToIndex(String shortName, String name) {
+ loadedFeaturesDup = (RubyArray)loadedFeatures.dup();
+ loadedFeaturesIndex.put(shortName, name);
}
protected void addPath(String path) {
@@ -536,7 +551,7 @@ private boolean smartLoadInternal(String file) {
boolean loaded = tryLoadingLibraryOrScript(runtime, state);
if (loaded) {
- addLoadedFeature(state.loadName);
+ addLoadedFeature(file, state.loadName);
}
return loaded;
}
@@ -629,7 +644,20 @@ public void removeInternalLoadedFeature(String name) {
}
protected boolean featureAlreadyLoaded(String name) {
- return loadedFeatures.containsString(name);
+ if (loadedFeatures.containsString(name)) {
+ return true;
+ }
+
+ // also check index
+ if (loadedFeaturesDup != null && loadedFeaturesDup.eql(loadedFeatures)) {
+ if (isFeatureInIndex(name)) {
+ return true;
+ }
+ } else {
+ loadedFeaturesIndex.clear();
+ }
+
+ return false;
}
protected boolean isJarfileLibrary(SearchState state, final String file) {
Please sign in to comment.
Something went wrong with that request. Please try again.