O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

JRuby and Invokedynamic - Japan JUG 2015

A talk on how JRuby uses invokedynamic to optimize Ruby, delivered at the Japan Java User's Group hosted by Oracle on December 8, 2015.

  • Seja o primeiro a comentar

JRuby and Invokedynamic - Japan JUG 2015

  1. 1. ❤ INVOKEDYNAMIC
  2. 2. Me • Charles Oliver Nutter • @headius, headius@headius.com • JVM language developer at Red Hat • JRuby co-lead since 2005
  3. 3. Agenda • Brief intro to JRuby (and Ruby) • Overview of invokedynamic • Invokedynamic examples • JRuby + invokedynamic
  4. 4. +
  5. 5. 2001: JRuby is Born
  6. 6. 2005: JRuby on Rails
  7. 7. 2015: JRuby 9000
  8. 8. Ruby • Created in 1995 byYukihiro Matsumoto • Dynamically typed (dynamic calls) • Object-oriented • Everything is an object • Tight integration with C, UNIX
  9. 9. Ruby • Created in 1995 byYukihiro Matsumoto • Dynamically typed (dynamic calls) • Object-oriented • Everything is an object • Tight integration with C, UNIX
  10. 10. class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display
  11. 11. Ruby == Method Calls Thousands upon thousands of them.
  12. 12. Command Number of simple calls jruby -e 1 1k gem install rails 315k rails new testapp 606k rails simple CRUD 16k
  13. 13. def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bazbar
  14. 14. def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bar baz JRuby call logic JRuby call logic Stops many JVM optimizations JRuby before invokedynamic JRuby call logic
  15. 15. Invocation Target Object Object’s Class void foo() static void bar() instanceof obj.foo() JRuby void foo()
  16. 16. public abstract class CachingCallSite extends CallSite {
 
 protected CacheEntry cache = CacheEntry.NULL_CACHE;
 
 private final String methodName = ... 
 public IRubyObject call(...) {
 RubyClass selfType = getClass(self);
 
 CacheEntry cache = this.cache;
 
 if (CacheEntry.typeOk(cache, selfType)) {
 return cache.method.call(...);
 }
 
 return cacheAndCall(...);
 }
 
 protected IRubyObject cacheAndCall(...) {
 CacheEntry entry = selfType.searchWithCache(methodName);
 
 DynamicMethod method = entry.method;
 
 if (methodMissing(method, caller)) {
 return callMethodMissing(context, self, method);
 }
 
 updateCache(entry);
 
 return method.call(context, self, selfType, methodName);
 }
 }
  17. 17. def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bar baz JRuby call logic JRuby call logic Stops many JVM optimizations JRuby before invokedynamic JRuby call logic
  18. 18. Invokedynamic
  19. 19. "In the future, we will consider bounded extensions to the Java Virtual Machine to provide better support for other languages." - JVM Specification First Edition (1997), preface
  20. 20. Goals of InvokeDynamic • A new bytecode • Fast function pointers + adapters • Caching and invalidation • Flexible enough for future uses
  21. 21. How does it work?
  22. 22. Invoke
  23. 23. Invoke// Static System.currentTimeMillis() Math.log(1.0)   // Virtual "hello".toUpperCase() System.out.println()   // Interface myList.add("happy happy") myRunnable.run()   // Constructor and super new ArrayList() super.equals(other)
  24. 24. // Static invokestatic java/lang/System.currentTimeMillis:()J invokestatic java/lang/Math.log:(D)D // Virtual invokevirtual java/lang/String.toUpperCase:()Ljava/lang/String; invokevirtual java/io/PrintStream.println:()V // Interface invokeinterface java/util/List.add:(Ljava/lang/Object;)Z invokeinterface java/lang/Runnable.add:()V // Special invokespecial java/util/ArrayList.<init>:()V invokespecial java/lang/Object.equals:(java/lang/Object)Z invokestatic invokevirtual invokeinterface invokespecial
  25. 25. invokestatic 1. Confirm arguments are of correct type 2. Look up method on Java class 3. Cache method 4. Invoke method invokevirtual 1. Confirm object is of correct type 2. Confirm arguments are of correct type 3. Look up method on Java class 4. Cache method 5. Invoke method invokeinterface 1. Confirm object’s type implements interface 2. Confirm arguments are of correct type 3. Look up method on Java class 4. Cache method 5. Invoke method invokespecial 1. Confirm object is of correct type 2. Confirm arguments are of correct type 3. Confirm target method is visible 4. Look up method on Java class 5. Cache method 6. Invoke method invokedynamic 1. Call your language's logic 2. Install target function 3.Target function invoked directly until you change it
  26. 26. function pointers invokedynamic language logic target method JVM
  27. 27. method handles call site bootstrap target method JVM
  28. 28. Method Handles
  29. 29. Method Handles • Function/field/array pointers • Argument manipulation • Flow control • Optimizable by the JVM • This is very important
  30. 30. java.lang.invoke •MethodHandles • Utilities for acquiring, adapting handles •MethodType • Representation of args + return type •MethodHandle • An invokable target + adaptations
  31. 31. MethodHandles.Lookup • Used to get function pointers •MethodHandles.lookup() • Obeys visibility of lookup() location • Can expose private members
  32. 32. MethodHandle Targets • Methods, constructors • Fields • Array elements
  33. 33. MethodHandles.Lookup • Method pointers •findStatic, findVirtual,
 findSpecial, findConstructor • Field pointers •findGetter, findSetter,
 findStaticGetter, findStaticSetter
  34. 34. // can access everything visible from here MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); // can access only public fields and methods MethodHandles.Lookup PUBLOOKUP = MethodHandles.publicLookup();
  35. 35. String value1 = System.getProperty("foo"); MethodHandle m1 = lookup         .findStatic(System.class, "getProperty", MethodType.methodType(String.class, String.class)); String value2 = (String)m2.invoke("foo"); Static Method
  36. 36. // example Java String value1 = System.getProperty("java.home");   // getProperty signature MethodType type1 = MethodType.methodType(String.class, String.class);   MethodHandle getPropertyMH = LOOKUP .findStatic(System.class, "getProperty", type1);   // invoke String value2 = (String) getPropertyMH.invoke("java.home");
  37. 37. // example Java System.out.println("Hello, world");   // println signature MethodType type2 = MethodType.methodType(void.class, Object.class);   MethodHandle printlnMH = LOOKUP .findVirtual(PrintStream.class, "println", type2);   // invoke printlnMH.invoke(System.out, (Object) "Hello, world");
  38. 38. Adapters • java.lang.invoke.MethodHandles.* • Argument manipulation, modification • Flow control and exception handling • Similar to writing your own command- pattern utility objects
  39. 39. Argument Juggling • insert, drop, permute • filter, fold • splat (varargs), spread (unbox varargs)
  40. 40. // insert is like partial application MethodHandle getJavaHomeMH = MethodHandles.insertArguments(getPropertyMH, 0, "java.home");   // same as getProperty("java.home") getJavaHomeMH.invokeWithArguments();
  41. 41. MethodHandle systemOutPrintlnMH = MethodHandles.insertArguments(printlnMH, 0, System.out);   // same as System.out.println(... systemOutPrintlnMH.invokeWithArguments("Hello, world");
  42. 42. Flow Control • guardWithTest is a boolean branch • Three targets • Condition ("if") • True path ("then") • False path ("else")
  43. 43. // boolean branch   // example Java class UpperDowner { public String call(String inputString) { if (randomBoolean()) { return inputString.toUpperCase(); } else { return inputString.toLowerCase(); } } }
  44. 44. // randomly return true or false MethodHandle upOrDown = LOOKUP.findStatic( BasicHandles.class, "randomBoolean", methodType(boolean.class)); // guardWithTest calls boolean handle and branches MethodHandle upperDowner = guardWithTest( upOrDown, toUpperCaseMH, toLowerCaseMH); upperDowner.invoke("Hello, world"); // HELLO, WORLD upperDowner.invoke("Hello, world"); // hello, world upperDowner.invoke("Hello, world"); // HELLO, WORLD upperDowner.invoke("Hello, world"); // HELLO, WORLD upperDowner.invoke("Hello, world"); // hello, world
  45. 45. Bootstrap
  46. 46. Bootstrap • First time JVM sees invokedynamic • Call your bootstrap code with name, type • Install resulting CallSite • Subsequent times • Just invoke call site contents
  47. 47. CallSite • Holds a MethodHandle • Returned to JVM by bootstrap method • Replaces invokedynamic bytecode • JVM watches it for changes
  48. 48. public static CallSite simpleBootstrap( MethodHandles.Lookup lookup, String name, MethodType type) throws Exception {   // Create and bind a constant site, pointing at the named method return new ConstantCallSite( lookup.findStatic(SimpleBinding.class, name, type));   }
  49. 49. Mutable Call Sites • Target can be changed • Trivial example of late binding
  50. 50. public static void first(...) { ... System.out.println("first!"); }   public static void second(...) { ... System.out.println("second!"); }   callable.call(); // => "first!" callable.call(); // => "second!" callable.call(); // => "first!" callable.call(); // => "second!"
  51. 51. public static CallSite mutableCallSiteBootstrap( MethodHandles.Lookup lookup, String name, MethodType type) throws Exception { MutableCallSite mcs = new MutableCallSite(type);   // look up the first method to call MethodHandle target = <get handle to "first" method>   // add MutableCallSite into args target = insertArguments(target, 0, mcs);   mcs.setTarget(target);   return mcs; }
  52. 52. public static void first( MethodHandles.Lookup lookup, MutableCallSite mcs) throws Exception { MethodHandle second = <get handle to "second" method>   mcs.setTarget(second);   System.out.println("first!"); }
  53. 53. public static void second( MethodHandles.Lookup lookup, MutableCallSite mcs) throws Exception { MethodHandle first = <get handle to "first" method>   mcs.setTarget(first);   System.out.println("second!"); }
  54. 54. All Together • Bytecode gets call site • Method handle points at target method • Call site caches it • Guard ensures it's the right method
  55. 55. Invokedynamic in JRuby
  56. 56. Dynamic Invocation • The obvious one • Method lookup based on runtime types • Potentially mutable types • Type check specific to language
  57. 57. class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display
  58. 58. def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bar baz JRuby call logic JRuby call logic Stops many JVM optimizations JRuby before invokedynamic JRuby call logic
  59. 59. def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bar baz JRuby call logic JRuby call logic Dynamic call logic built into JVM JRuby on Java 7 JRuby call logic X X
  60. 60. Dynamic Invocation Target Object Method Table def foo ... def bar ... associated with obj.foo() JVM def foo ... Call Site
  61. 61. def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bar baz Straight through dispatch path JRuby on Java 7
  62. 62. def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bar baz Optimizations (like inlining) can happen! JRuby on Java 7
  63. 63. Empty Method Call 10M calls to empty method 0 1.25 2.5 3.75 5 Time in seconds
  64. 64. Empty Method Call 10M calls to empty method 0.32 0.328 0.335 0.343 0.35 Time in seconds
  65. 65. Lazy Constants • Call site just produces a value • Value calculated once • Subsequent access is direct, optimizable • Used for numbers, strings, regexp
  66. 66. class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display
  67. 67. Lazy Constants Lazy Computation LAZY_CONST JVM Call Site value
  68. 68. Ruby Constants • Ruby's constants can be modified • ...but it is very rare • Look up value • Guard against it changing
  69. 69. class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display
  70. 70. Constant Lookup Look up "Foo" constant 10m times 0 0.3 0.6 0.9 1.2 Time in seconds MRI JRuby Control
  71. 71. InstanceVariables • Ruby objects grow as needed • Look up offset for @var in object • Cache offset, guard against other types • Direct access to variable table
  72. 72. class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display
  73. 73. MyObject @foo @bar @baz puts @foo
  74. 74. MyObject @foo @bar @baz puts @foo Offset:1
  75. 75. MyObject @foo @bar @baz puts @foo Offset:1
  76. 76. InstanceVariables Look up @foo variable 10M times 0 0.15 0.3 0.45 0.6 Time in seconds MRI JRuby Control
  77. 77. class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display
  78. 78. Real World
  79. 79. bench_fractal bench_flipflop_fractal • Mandelbrot generator • Integer loops • Floating-point math • Julia generator using flip-flops • I don’t really understand it.
  80. 80. bench_fractal 0s 0.4s 0.8s 1.2s 1.6s Iteration 0 1 2 3 4 5 6 7 Ruby 2.2.2 JRuby + indy
  81. 81. bench_flipflop_fractal 0s 0.375s 0.75s 1.125s 1.5s Category Title 0 1 2 3 4 5 6 7 8 9 Ruby 2.2.2 JRuby + indy
  82. 82. Times Faster than Ruby 2.2.2 0 1 2 3 4 base64 richards neural 3.66 3.44 2.658 1.914 1.538 1.346 JRuby/Java 6 JRuby/Java 7
  83. 83. red/black tree, pure Ruby versus C ext ruby-2.2.2 + Ruby ruby-2.2.2 + C ext jruby + Ruby Runtime per iteration 0 0.75 1.5 2.25 3 0.29s 0.51s 2.48s
  84. 84. Future Work • More creative use of invokedynamic in JRuby • Smarter compiler above JVM byte code • Continued JVM optimization
  85. 85. ThankYou! • Charles Oliver Nutter • @headius • headius@headius.com • https://github.com/jruby/jruby

×