Wednesday, July 8, 2009

Block Assignment Refactoring Fun...

I have been refactoring some of our block dispatch code recently. These changes are currently only for the interpreter, but they can easily be adapted for the compiler later. In fact, I think I can adapt this for our interpreters method parameter assignment and also unify it with our Ruby 1.9 assignment code (for both blocks and methods). But I am getting a head of myself. Why refactor block assignment code?

Block assignment code is one of the ickiest pieces of logic in JRuby and for that matter in Ruby. Lots and lots of conditional logic to pad things out and even weirder logic when your block has one parameter and it is a RubyArray (perhaps not as weird as when it isn't a RubyArray). So there were three goals in the refactoring:
  1. Make each type of block parameter signature it's own code path with little or no conditional logic
  2. Try and make the logic for the strange cases more readable
  3. Try and speed up block invocation

The refactoring is to have InterpretedBlock (and by extension SharedBlock) contain a new attribute called assigner of base type Assigner. A new class Assigner has split-arity call paths and a boxed catch-all assign method. Also, a special RubyArray version assignArray for the most ugly block assignment case:

public abstract class Assigner {
public abstract void assign(Ruby runtime, ThreadContext context, IRubyObject self, Block block);
public abstract void assign(Ruby runtime, ThreadContext context, IRubyObject self, IRubyObject value1, Block block);
public abstract void assign(Ruby runtime, ThreadContext context, IRubyObject self, IRubyObject value1, IRubyObject value2, Block block);
public abstract void assign(Ruby runtime, ThreadContext context, IRubyObject self, IRubyObject value1, IRubyObject value2, IRubyObject value3, Block block);
public abstract void assign(Ruby runtime, ThreadContext context, IRubyObject self, IRubyObject[] values, Block block);
public abstract void assignArray(Ruby runtime, ThreadContext context, IRubyObject self, IRubyObject values, Block block);
// ...
}
For each type of block parameter signature we have a subclass of Assigner which implements these assign methods with the proper minimalist code to do assignment in that case. So for a block which has a single required argument and a rest argument (Pre1Rest1Assigner) we have an unboxed 2 argument assign method which looks like:

public void assign(Ruby runtime, ThreadContext context, IRubyObject self, IRubyObject value1,
IRubyObject value2, Block block) {
parameter1.assign(runtime, context, self, value1, block, false);

rest.assign(runtime, context, self, runtime.newArrayNoCopyLight(value2), block, true);
}
All in all I think the code reads much nicer than it did. There are still some uses of the old code which need to be eliminated in a future refactoring, but I think the code is in better shape than it was.

This along with creating split-arity versions of YieldNode ended up yielding some good results (both running jruby -X-C --server bench/language/bench_yield.rb):

Before After Improvement
1m x10 yield 1 to { } 1.304000 1.139000 ~12%
1m x10 yield to { } 1.253000 1.174000 ~6%
1m x10 yield 1 to {|j| j} 1.572000 1.474000 ~0.6%
1m x10 yield 1,2 to {|j,k| k} 2.228000 1.570000 ~30%
1m x10 yield 1,2,3 to {|j,k,l| k} 2.607000 1.731000 ~33%
1m x10 yield to {|j,k,l| k} 2.921000 1.710000 ~41%
1m x10 yield 1,2,3 to {|*j| j} 3.009000 2.492000 ~17%
1m x10 yield to {1} 1.313000 1.273000 ~0.3%

It is always good when things get faster after a refactoring.... :)

4 comments:

  1. Gemini could use lots of block love. Thanks!
    How do you do the formatted code stuff with blogger?

    ReplyDelete
  2. Very nice post, and excellent perf results. And congrats on a new blog with a reasonable address :) You may want to darken the text or lighten the background a bit though...dark grey on light grey is a little hard to read.

    ReplyDelete
  3. @Logan

    I installed this at a url I could trust:

    http://code.google.com/p/syntaxhighlighter/

    Then added stylesheet mojo to include it.

    ReplyDelete
  4. @Charles

    Ok ok...yeah I will be tweaking this here and there to improve how it looks. Not too much time spent yet...I mostly wanted to make you happy with the short blog url :)

    ReplyDelete