Projects, which use JSR292
Charles Oliver Nutter
headius at headius.com
Fri Feb 18 03:52:14 PST 2011
On Fri, Feb 18, 2011 at 5:24 AM, Rémi Forax <forax at univ-mlv.fr> wrote:
> Good news !
> Charles does it mean that JRuby's invokedynamic now works without
> using the previously existing logic.
> I mean, you use only method handles from the callsite to the target method.
More and more, but definitely not completely. I'd estimate that *most*
calls are dispatched with indy, and of those about *half* are direct
with recent commits.
Methods that can bind all the way from caller to target must fit these criteria:
* Fixed arity
* No block passed (block passing tends to require extra structures my
direct indy logic doesn't set up yet)
* One of a handful of standard signatures (just having wired all up yet)
And note that this only applies to plain calls like foo.bar; calls
that perform dynamic dispatch but combined with other logic, like a.b
+= c, do not yet use indy at all.
I did just land another revision that allows Ruby to Ruby calls
meeting the above criteria to bind all the way through, and
performance dropped precipitously:
Stock JRuby:
~/projects/jruby ➔
JAVA_HOME=../mlvm/sources/build/bsd-i586/j2sdk-image/ jruby -X+C
-J-XX:MaxInlineSize=150 -J-XX:InlineSmallCode=5000
bench/bench_fib_recursive.rb 10832040
0.354000 0.000000 0.354000 ( 0.284000)
832040
0.174000 0.000000 0.174000 ( 0.174000)
832040
0.173000 0.000000 0.173000 ( 0.173000)
832040
0.169000 0.000000 0.169000 ( 0.169000)
832040
0.173000 0.000000 0.173000 ( 0.173000)
JRuby + indy, no ruby-to-ruby calls binding directly:
~/projects/jruby ➔
JAVA_HOME=../mlvm/sources/build/bsd-i586/j2sdk-image/ jruby -X+C
-Xcompile.invokedynamic=true -J-XX:+UnlockExperimentalVMOptions
-J-XX:+EnableInvokeDynamic -J-XX:MaxInlineSize=150
-J-XX:InlineSmallCode=5000 bench/bench_fib_recursive.rb 10
832040
0.340000 0.000000 0.340000 ( 0.274000)
832040
0.150000 0.000000 0.150000 ( 0.150000)
832040
0.148000 0.000000 0.148000 ( 0.148000)
832040
0.147000 0.000000 0.147000 ( 0.147000)
832040
0.147000 0.000000 0.147000 ( 0.147000)
JRuby + indy, ruby calls binding directly:
~/projects/jruby ➔
JAVA_HOME=../mlvm/sources/build/bsd-i586/j2sdk-image/ jruby -X+C
-Xcompile.invokedynamic=true -J-XX:+UnlockExperimentalVMOptions
-J-XX:+EnableInvokeDynamic -J-XX:MaxInlineSize=150
-J-XX:InlineSmallCode=5000 bench/bench_fib_recursive.rb 10
832040
0.883000 0.000000 0.883000 ( 0.818000)
832040
0.463000 0.000000 0.463000 ( 0.463000)
832040
0.463000 0.000000 0.463000 ( 0.463000)
832040
0.472000 0.000000 0.472000 ( 0.473000)
832040
0.459000 0.000000 0.459000 ( 0.459000)
I have not investigated asm output, but it seems pretty clear that
something very bad happens when I bind directly rather than binding
via JRuby DynamicMethod stubs. Indeed, in the Java trace, I do see GWT
should up:
at sun.dyn.MethodHandleImpl$GuardWithTest.invoke_L4(MethodHandleImpl.java:982)
at ruby.__dash_e__.method__0$RUBY$foo(-e:1)
at sun.dyn.MethodHandleImpl$GuardWithTest.invoke_L4(MethodHandleImpl.java:982)
at ruby.__dash_e__.method__0$RUBY$foo(-e:1)
at sun.dyn.MethodHandleImpl$GuardWithTest.invoke_L4(MethodHandleImpl.java:982)
at ruby.__dash_e__.method__0$RUBY$foo(-e:1)
at sun.dyn.MethodHandleImpl$GuardWithTest.invoke_L4(MethodHandleImpl.java:982)
But the non-direct logic includes *even more* sun.dyn and JRuby calls
in it. How can it be so much faster? A recursive "foo" call looks like
this:
at ruby.__dash_e__.method__0$RUBY$foo(-e:1)
at ruby___dash_e__Invokermethod__0$RUBY$fooFixed0.call(ruby___dash_e__Invokermethod__0$RUBY$fooFixed0#foo:65535)
at ruby___dash_e__Invokermethod__0$RUBY$fooFixed0.call(ruby___dash_e__Invokermethod__0$RUBY$fooFixed0#foo:65535)
at sun.dyn.FilterGeneric$F6.invoke_F6(FilterGeneric.java:754)
at sun.dyn.FilterGeneric$F5.invoke_F5(FilterGeneric.java:678)
at sun.dyn.MethodHandleImpl$GuardWithTest.invoke_L4(MethodHandleImpl.java:982)
at ruby.__dash_e__.method__0$RUBY$foo(-e:1)
With JRuby's call-site caching logic:
at ruby.__dash_e__.method__0$RUBY$foo(-e:1)
at ruby___dash_e__Invokermethod__0$RUBY$fooFixed0.call(ruby___dash_e__Invokermethod__0$RUBY$fooFixed0#foo:65535)
at ruby___dash_e__Invokermethod__0$RUBY$fooFixed0.call(ruby___dash_e__Invokermethod__0$RUBY$fooFixed0#foo:65535)
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:103)
at ruby.__dash_e__.method__0$RUBY$foo(-e:1)
And with JRuby's "dynopt" mode, which emits guard, target, and
fallback logic directly into the compiled body:
at rubyjit.foo_7F1E71544C0BFF52B6020F56F3C0D1A11E173AF5.__file__(-e:1)
at rubyjit.foo_7F1E71544C0BFF52B6020F56F3C0D1A11E173AF5.__file__(-e:1)
at rubyjit.foo_7F1E71544C0BFF52B6020F56F3C0D1A11E173AF5.__file__(-e:1)
at rubyjit.foo_7F1E71544C0BFF52B6020F56F3C0D1A11E173AF5.__file__(-e:1)
Needless to day, "dynopt" (which can only do this for recursive calls)
is still fastest on the recursive fib and tak benchmarks, but I'm a
little confused by the degradation from directly binding ruby-to-ruby
calls with indy. Any flags I can toss in to get you JVM guys more
info?
And yes...this is all on JRuby master. Should be able to see the same
effect right now.
- Charlie
More information about the mlvm-dev
mailing list