1 of 20

Patterns and Performance of InvokeDynamic in JRuby

Japan JRuby User Group

Hiroshi Nakamura

@nahi nahi@ruby-lang.org

This slides: http://bit.ly/jjug-indy-jruby-en

Code: http://bit.ly/jjug-indy-jruby-code

2 of 20

Self introduction

Custom system development around network security: C/C++ (18 years), Java (13 years), Ruby (13 years)

OSS development at free-time: CRuby (8 years), JRuby (2 years), soap4r, httpclient, etc.

3 of 20

What's JRuby - http://jruby.org/

1.6.6 is the latest (1.6.7 coming soon)

Ruby runs atop JVM: Dynamic typed language

Open Source (CPL, GPL, LGPL)

Started 10 years ago

5 years from full-time developer

4 of 20

Goal of this talk

Showing InvokeDynamic usages on JRuby

What I want to tell you today;

  • It's hot - JVM is awesome
  • Game changer for JVM languages
  • Too complex to control (for me)... ATM

5 of 20

Revisit InvokeDynamic features

Link a caller (invokedynamic) with method (MH) at runtime

  • Search target MH at the first invocation
  • JVM caches the linked MH and handles this as same as other invocations
  • Features for lightweight link invalidation

Common optimization with Java for dynamic typed languages

6 of 20

Patterns of InvokeDynamic

MH juggling in JRuby

7 of 20

InvokeDynamic optimization recipe

  • Reduce MH re-link (reduce cache invalidation)
  • Do MH combining with MethodHandle API so that caller's signature type (MethodType) accords with callee's signature type. Do not call Java method as match as possible.

Signature example;

2 params of Object and long, returns Object

(Object self, long value)Object

8 of 20

InvokeDynamic Patterns in JRuby

1. String literal

2. Other literals

3. Quasi-constant

4. Instance variable

5. Method invocation

6. Math operation

9 of 20

1. String literal

Where to apply: String literals

  • Generates bytecode to pass "Hello" to bootstrap
  • To link (ctx)Object with (ctx, str)Object, combines a function to insert str arg

message = "Hello"message << name << "!"

Call signature: (ThreadContext ctx)Object

&jrNewString(ctx, str): target

&insert(str = "Hello"):insert 1 arg

combine

ThreadContext is for the Object containing runtime environment. All operations needs it.

10 of 20

2. Other literals

Where to apply: Immutable literals

  • Generates bytecode to pass a const value to bootstrap
  • Creates MH which returns the constant
  • To link (ctx)Object with ()Object, combines a function to drop ctx arg

times = 10000matcher = /[A-Z][a-z]*/

Call signature: (ThreadContext ctx)Object

&constant[10000]: MH returns const

&drop(ctx): drop 1 arg

11 of 20

3. Quasi-constant

Where to apply: Constant reference in Ruby

  • Constant in Ruby is overwritable so treats it as "Variable which rarely overwritten"
  • Uses SwitchPoint to detect (possible) overwrite

&thisMethod(ctx): re-link this MH to update value

&constant[obj]: MH for current value as const

&drop(ctx): drop 1 arg

&SwitchPoint(&,&): invalidated if any const is defined

DEFAULT = Container.new.freezecomtainer = DEFAULT�DEFAULT = nil

Call signature: (ThreadContext ctx)Object

12 of 20

4. Instance variable

Where to apply: Instance variable access

  • Refers variable table of "self"
  • Table is vary according to the class
  • Must see Foo's table or Bar's table�for "@cache" in Cache module
  • Uses GuardWithTest to detect cached class invalidation

module Cache� def cache(value)� @cache = value� end�end�class Foo� include Cache�end�class Bar� include Cache�end

13 of 20

4. Instance variable (contd.)

&jrGetVariable(obj, index): target

&thisMethod(self): re-link this MH to update self

Call signature: (Object self)Object

&insert(obj = self, index = 2): insert

&filterRetval(&nullToNil): combine retval filter

&jrTestClass(self): check if class is the same

&GuardWithTest(&,&,&): invalidate if class is different

14 of 20

5. Method invo-� cation

Where to apply: Any method invocation

  • Callee depends on receiver's class
  • Linked method can be overwritten
  • 5 signature types according to number of args

$name = [:JRuby, "Ruby", 42]def process(router)router.say_hello($names.sample)�end

Call signature:

(ctx, self)Object

(ctx, self, arg1)Object

(ctx, self, arg1, arg2)Object

(ctx, self, arg1, arg2, arg3)Object

(ctx, self, arg[])Object

15 of 20

5. Method invocation (contd.)

&jrTestClass(self): check the class

&drop(ctx, self): drop 2 args

&GuardWithTest: invalidate if class is different

&SwitchPoint: invalidated if a method added to class

Call signature: (ctx, self, arg1)Object

&jrTarget(ctx, self, arg1): target

&thisMethod(ctx, self, arg1): re-link

&thisMethod(ctx, self, arg1): re-link

16 of 20

6. Math operation

Where to apply: arithmetic op for int or float as RHS

  • To shortcut for avoiding RHS unboxing

fib(n - 2) + fib(n - 1)display if (x >= 5)�elapsed = msec * 1000.0

Call signature: (ctx, self)Object

&jrInvoke(ctx, self, value): common call

&jrTestClass(self): is LHS an Integer

&jrFixnumMinusTwo(ctx, self): shortcut

&drop(ctx): drop 1 arg

&GuardWithTest: shortcut if LHS is an Integer

17 of 20

InvokeDynamic Performance in JRuby

Hooray JVM!

18 of 20

Micro benchmark

String literal

Other literals

Quasi-const

Instance variable

Method invocation

Math operation

All

- Java 7 w/o indy == 1. Higher is faster.

- Measured loop of single line for each pattern.

- "All" contains cascading invocation of all.

19 of 20

Binary-tree benchmark

AVL tree (recursive)

Red-black tree (loop)

- Java 7 w/o indy == 1. Higher is faster.

20 of 20

Conclusion

Fast (if you use this right)

Complex (I could not master it yet)

Optimization is easier than doing it by language runtime -> Let JVM do it

Other application scopes?

  • Logic reuse by method combination
  • Profiler, Debugger
  • etc.