Projects, which use JSR292

Charles Oliver Nutter headius at headius.com
Fri Feb 18 12:06:28 PST 2011


On Fri, Feb 18, 2011 at 7:19 AM, Christian Thalinger
<christian.thalinger at oracle.com> wrote:
> What I can tell so far is that setting rubyDirect=true inlines a whole lot more stuff than without.  I looks like it wants to inline all recursive calls of fib_ruby into a single method, which doesn't work very well, hitting two limits: NodeCountInliningCutoff and DesiredMethodLimit.
>
> I tried to bump these limits but that is a dead end for the compiler since at one point it skips the compile because it gets too many nodes:
>
>  43   COMPILE SKIPPED: out of nodes parsing method (not retryable)
>
> I don't know why it wants to inline all these recursive calls.  Is that something JRuby tries to do?

Nope, we're not doing anything special there. Here's the basic layout
(review for you perhaps):

Prior to this work, invokedynamic connected the call sites up to our
existing DynamicMethod objects via the "call" method. In most cases,
those objects were a unique class per method, to allow things to
inline, but ultimately they added one or two intermediate frames to
every indy call path. You can see that in my previous email.

In between then and now, I worked on "dynopt". Dynopt in JRuby
basically takes the last-seen method at a call site while interpreting
and inserts a guard, direct call to that method (*really*
direct...invokevirtual or invokestatic), and fallback to the old call
site logic. This worked for native core methods with fixed arities and
no closure, a few "intrinsics", self-recursing calls (fib, tak), and
Float and Fixnum dispatches with or without a boxed argument. It also
inlined directly any trivial leaf methods (constant return value,
simple state lookups, etc). This work improved the performance of
small benchmarks substantially, and the largest part of that
improvement came from being able to directly make recursive calls.

The new indy work in JRuby basically takes the direct-binding part of
that work and uses it to patch invokedynamic all the way through,
skipping DynamicMethod. It works for the cases I listed in my email to
Rémi: basically any fixed-arity, no-block, core class or Ruby-to-Ruby
calls. Next I was going to explore patching Ruby to Java calls all the
way through as well.

At *best* this logic does nothing more than remove as many
intermediate frames between the caller and target. We do no manual
inlining (except for the trivial leaf cases), and are simply
connecting A with B.

On the one hand I'm thrilled to see that a lot of stuff is inlining.
With only a tiny amount of work, Ruby calls can inline into Ruby
calls, with very little intervening overhead.

But on the other hand, I'm not sure what I should expect to see from a
performance perspective. "dynopt" inlines Ruby into Ruby as well, at
the cost of a lot of bytecode in each Ruby method body, eating up that
precious budget. I would have expected the Ruby-to-Ruby inlining via
invokedynamic to perform as well or better than dynopt.

I'm not grousing about performance, mind you :) I think it's just
awesome how cleanly this is working so far with not a whole lot of
effort. I'm champing at the bit to make all of JRuby
invokedynamic-aware. But obviously the perf is not as good as we'd
expect it, and I'm standing by to help provide information or
experiment to improve it.

FWIW, you can specify jruby.compile.dynopt=true to turn on dynopt for
benchmarks, if you'd like to compare with indy. It can take longer to
ramp up, since it forces 100% JIT (no force-compiling the target
script immediately), which is then subject to a hard 50-invocation
threshold.

- Charlie


More information about the mlvm-dev mailing list