Slide 1

Slide 1 text

❤ INVOKEDYNAMIC

Slide 2

Slide 2 text

Me • Charles Oliver Nutter • @headius, headius@headius.com • JVM language developer at Red Hat • JRuby co-lead since 2005

Slide 3

Slide 3 text

Agenda • Brief intro to JRuby (and Ruby) • Overview of invokedynamic • Invokedynamic examples • JRuby + invokedynamic

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

+

Slide 6

Slide 6 text

2001: JRuby is Born

Slide 7

Slide 7 text

2005: JRuby on Rails

Slide 8

Slide 8 text

2015: JRuby 9000

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Ruby • Created in 1995 by Yukihiro Matsumoto • Dynamically typed (dynamic calls) • Object-oriented • Everything is an object • Tight integration with C, UNIX

Slide 11

Slide 11 text

Ruby • Created in 1995 by Yukihiro Matsumoto • Dynamically typed (dynamic calls) • Object-oriented • Everything is an object • Tight integration with C, UNIX

Slide 12

Slide 12 text

class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display

Slide 13

Slide 13 text

Ruby == Method Calls Thousands upon thousands of them.

Slide 14

Slide 14 text

Command Number of simple calls jruby -e 1 1k gem install rails 315k rails new testapp 606k rails simple CRUD 16k

Slide 15

Slide 15 text

def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo baz bar

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Invocation Target Object Object’s Class void foo() static void bar() instanceof obj.foo() JRuby void foo()

Slide 18

Slide 18 text

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);
 }
 }

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Invokedynamic

Slide 21

Slide 21 text

"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

Slide 22

Slide 22 text

Goals of InvokeDynamic • A new bytecode • Fast function pointers + adapters • Caching and invalidation • Flexible enough for future uses

Slide 23

Slide 23 text

How does it work?

Slide 24

Slide 24 text

Invoke

Slide 25

Slide 25 text

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)

Slide 26

Slide 26 text

// 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.:()V invokespecial java/lang/Object.equals:(java/lang/Object)Z invokestatic invokevirtual invokeinterface invokespecial

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

function pointers invokedynamic language logic target method JVM

Slide 29

Slide 29 text

method handles call site bootstrap target method JVM

Slide 30

Slide 30 text

Method Handles

Slide 31

Slide 31 text

Method Handles • Function/field/array pointers • Argument manipulation • Flow control • Optimizable by the JVM • This is very important

Slide 32

Slide 32 text

java.lang.invoke •MethodHandles • Utilities for acquiring, adapting handles •MethodType • Representation of args + return type •MethodHandle • An invokable target + adaptations

Slide 33

Slide 33 text

MethodHandles.Lookup • Used to get function pointers •MethodHandles.lookup() • Obeys visibility of lookup() location • Can expose private members

Slide 34

Slide 34 text

MethodHandle Targets • Methods, constructors • Fields • Array elements

Slide 35

Slide 35 text

MethodHandles.Lookup • Method pointers •findStatic, findVirtual,
 findSpecial, findConstructor • Field pointers •findGetter, findSetter,
 findStaticGetter, findStaticSetter

Slide 36

Slide 36 text

// can access everything visible from here MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); // can access only public fields and methods MethodHandles.Lookup PUBLOOKUP = MethodHandles.publicLookup();

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

// 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");

Slide 39

Slide 39 text

// 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");

Slide 40

Slide 40 text

Adapters • java.lang.invoke.MethodHandles.* • Argument manipulation, modification • Flow control and exception handling • Similar to writing your own command- pattern utility objects

Slide 41

Slide 41 text

Argument Juggling • insert, drop, permute • filter, fold • splat (varargs), spread (unbox varargs)

Slide 42

Slide 42 text

// insert is like partial application MethodHandle getJavaHomeMH = MethodHandles.insertArguments(getPropertyMH, 0, "java.home"); // same as getProperty("java.home") getJavaHomeMH.invokeWithArguments();

Slide 43

Slide 43 text

MethodHandle systemOutPrintlnMH = MethodHandles.insertArguments(printlnMH, 0, System.out); // same as System.out.println(... systemOutPrintlnMH.invokeWithArguments("Hello, world");

Slide 44

Slide 44 text

Flow Control • guardWithTest is a boolean branch • Three targets • Condition ("if") • True path ("then") • False path ("else")

Slide 45

Slide 45 text

// boolean branch // example Java class UpperDowner { public String call(String inputString) { if (randomBoolean()) { return inputString.toUpperCase(); } else { return inputString.toLowerCase(); } } }

Slide 46

Slide 46 text

// 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

Slide 47

Slide 47 text

Bootstrap

Slide 48

Slide 48 text

Bootstrap • First time JVM sees invokedynamic • Call your bootstrap code with name, type • Install resulting CallSite • Subsequent times • Just invoke call site contents

Slide 49

Slide 49 text

CallSite • Holds a MethodHandle • Returned to JVM by bootstrap method • Replaces invokedynamic bytecode • JVM watches it for changes

Slide 50

Slide 50 text

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)); }

Slide 51

Slide 51 text

Mutable Call Sites • Target can be changed • Trivial example of late binding

Slide 52

Slide 52 text

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!"

Slide 53

Slide 53 text

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 = // add MutableCallSite into args target = insertArguments(target, 0, mcs); mcs.setTarget(target); return mcs; }

Slide 54

Slide 54 text

public static void first( MethodHandles.Lookup lookup, MutableCallSite mcs) throws Exception { MethodHandle second = mcs.setTarget(second); System.out.println("first!"); }

Slide 55

Slide 55 text

public static void second( MethodHandles.Lookup lookup, MutableCallSite mcs) throws Exception { MethodHandle first = mcs.setTarget(first); System.out.println("second!"); }

Slide 56

Slide 56 text

All Together • Bytecode gets call site • Method handle points at target method • Call site caches it • Guard ensures it's the right method

Slide 57

Slide 57 text

Invokedynamic in JRuby

Slide 58

Slide 58 text

Dynamic Invocation • The obvious one • Method lookup based on runtime types • Potentially mutable types • Type check specific to language

Slide 59

Slide 59 text

class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Dynamic Invocation Target Object Method Table def foo ... def bar ... associated with obj.foo() JVM def foo ... Call Site

Slide 63

Slide 63 text

def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bar baz Straight through dispatch path JRuby on Java 7

Slide 64

Slide 64 text

def foo
 bar
 end
 
 def bar
 baz
 end
 
 def baz
 # ...
 end foo bar baz Optimizations (like inlining) can happen! JRuby on Java 7

Slide 65

Slide 65 text

Empty Method Call 10M calls to empty method 0 1.25 2.5 3.75 5 Time in seconds

Slide 66

Slide 66 text

Empty Method Call 10M calls to empty method 0.32 0.328 0.335 0.343 0.35 Time in seconds

Slide 67

Slide 67 text

Lazy Constants • Call site just produces a value • Value calculated once • Subsequent access is direct, optimizable • Used for numbers, strings, regexp

Slide 68

Slide 68 text

class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display

Slide 69

Slide 69 text

Lazy Constants Lazy Computation LAZY_CONST JVM Call Site value

Slide 70

Slide 70 text

Ruby Constants • Ruby's constants can be modified • ...but it is very rare • Look up value • Guard against it changing

Slide 71

Slide 71 text

class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display

Slide 72

Slide 72 text

Constant Lookup Look up "Foo" constant 10m times 0 0.3 0.6 0.9 1.2 Time in seconds MRI JRuby Control

Slide 73

Slide 73 text

Instance Variables • Ruby objects grow as needed • Look up offset for @var in object • Cache offset, guard against other types • Direct access to variable table

Slide 74

Slide 74 text

class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display

Slide 75

Slide 75 text

MyObject @foo @bar @baz puts @foo

Slide 76

Slide 76 text

MyObject @foo @bar @baz puts @foo Offset:1

Slide 77

Slide 77 text

MyObject @foo @bar @baz puts @foo Offset:1

Slide 78

Slide 78 text

Instance Variables Look up @foo variable 10M times 0 0.15 0.3 0.45 0.6 Time in seconds MRI JRuby Control

Slide 79

Slide 79 text

class Hello
 def initialize(name)
 @name = name
 end
 
 def display
 puts "Hello, #{@name}"
 end
 end
 
 hello = Hello.new
 hello.display

Slide 80

Slide 80 text

Real World

Slide 81

Slide 81 text

bench_fractal bench_flipflop_fractal • Mandelbrot generator • Integer loops • Floating-point math • Julia generator using flip-flops • I don’t really understand it.

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

Future Work • More creative use of invokedynamic in JRuby • Smarter compiler above JVM byte code • Continued JVM optimization

Slide 89

Slide 89 text

Thank You! • Charles Oliver Nutter • @headius • headius@headius.com • https://github.com/jruby/jruby