Uploaded image for project: 'Ruby Driver'
  1. Ruby Driver
  2. RUBY-155

Performance of JRuby BSON could be improved by avoiding _runtime.getClassFromPath() calls

    • Type: Icon: Improvement Improvement
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • 12_01_17
    • Affects Version/s: None
    • Component/s: None
    • None
    • Environment:
      osx 10.6.4, jruby 1.5.1, jruby branch

      I ran the profiler against the new Java BSON code used in the JRuby branch. A few things popped out that I thought I would share.

      In the RubyBSONCallback constructor, it calls _runtime.getClassFromPath 3 times every time this class is instantiated. Believe it or not, that code is near the top of the profile for time consumed.

      The RubyBSONEncoder constructor does the same thing only for more classes.

      I think a cheap optimization would be to make those calls once in the class object and memoize the results so future calls would be nearly free. I think these things need to be cached per JRuby runtime.

      (I'm not great at java, so pardon the pseudo java code... I don't know if this is feasible so that's why I didn't fork the repository and submit this as a patch)

      public class RubyBSONEncoder {

      static final Map _runtimeCache = new HashMap();

      static final Map _getRuntimeCache(Ruby runtime) {
      // each JRuby runtime may have different objects for these constants,
      // so cache them separately for each runtime
      Map cache = _runtimeCache.get(runtime);

      if(cache == null)

      { cache = new HashMap(); _runtimeCache.put(runtime, cache); }

      return cache;
      }

      static final RubyModule lookupConstant(Ruby runtime, String name)
      {
      Map cache = _getRuntimeCache(runtime);
      RubyModule module = cache.get(name);

      if(module == null && !cache.containsKey(name))

      { module = runtime.getClassFromPath(name); cache.put(name, module); }
      return module;
      }

      static final RubySymbol lookupSymbol(Ruby runtime, String name)
      {
      Map cache = _getRuntimeCache(runtime);
      RubyModule module = cache.get(name);

      if(module == null && !cache.containsKey(name)) { module = runtime.getClassFromPath(name); cache.put(name, module); }

      return module;
      }

      public RubyBSONEncoder(Ruby runtime)

      { _runtime = runtime; _rbclsByteBuffer = lookupConstant(_runtime, "BSON::ByteBuffer" ); _rbclsDBRef = lookupConstant(_runtime, "BSON::DBRef" ); _rbclsInvalidDocument = lookupConstant(_runtime, "BSON::InvalidDocument" ); _rbclsRangeError = lookupConstant(_runtime, "RangeError" ); _idAsSym = lookupSymbol(_runtime, "_id" ); // should cache this too! }

      }

      This could probably be made more generic so that it can cache other things like the :_id symbol. Just googled for it and found a pretty decent article that talks about generic memoization for Java.

      http://onjava.com/pub/a/onjava/2003/08/20/memoization.html

            Assignee:
            kbanker Kyle Banker
            Reporter:
            cremes Chuck Remes
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: