Beyond JVM Charles Oliver Nutter @headius Me + 2001: JRuby is - - PowerPoint PPT Presentation

beyond jvm
SMART_READER_LITE
LIVE PREVIEW

Beyond JVM Charles Oliver Nutter @headius Me + 2001: JRuby is - - PowerPoint PPT Presentation

Beyond JVM Charles Oliver Nutter @headius Me + 2001: JRuby is Born 2006: JRuby on Rails 2015: JRuby 9000 $ git log --oneline | wc -l 29435 Why do this for 9 years? We like the JVM Magical black box that runs our code Except when it


slide-1
SLIDE 1

Beyond JVM

Charles Oliver Nutter @headius

slide-2
SLIDE 2

Me

slide-3
SLIDE 3
slide-4
SLIDE 4
slide-5
SLIDE 5

+

slide-6
SLIDE 6

2001: JRuby is Born

slide-7
SLIDE 7

2006: JRuby on Rails

slide-8
SLIDE 8

2015: JRuby 9000

slide-9
SLIDE 9

$ git log --oneline | wc -l 29435

slide-10
SLIDE 10

Why do this for 9 years?

slide-11
SLIDE 11

We like the JVM

slide-12
SLIDE 12

Magical black box that runs our code

slide-13
SLIDE 13

Except when it doesn’t

slide-14
SLIDE 14
slide-15
SLIDE 15

Lots and lots and lots and lots...

Ruby == Method Calls

slide-16
SLIDE 16

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

slide-17
SLIDE 17

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

foo baz bar

slide-18
SLIDE 18

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

foo bar baz

JRuby call logic JRuby call logic

Kills many JVM optimizations JRuby on Java 5/6

JRuby call logic

slide-19
SLIDE 19

Invokedynamic

slide-20
SLIDE 20

History

  • JVM authors mentioned non-Java languages
  • Language authors have targeted JVM
  • Hundreds of JVM languages now
  • But JVM was a mismatch for many of them
  • Usually required tricks that defeated JVM
  • ptimizations
  • Or required features JDK could not provide
slide-21
SLIDE 21

JVM Opcodes

Invocation

invokevirtual invokeinterface invokestatic invokespecial

Field Access

getfield setfield getstatic setstatic

Array Access

*aload *astore b,s,c,i,l,d,f,a

Stack Local Vars Flow Control Allocation Boolean and Numeric

slide-22
SLIDE 22

"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-23
SLIDE 23

Goals of JSR 292

  • A user-definable bytecode
  • Full freedom to define

VM behavior

  • Fast method pointers + adapters
  • Caching and invalidation
  • Avoid future modifications
slide-24
SLIDE 24

Indy Users

  • Languages
  • Grew into it: JRuby, Groovy
  • Grew up with it: Dyn.js and Nashorn (JS)
  • Java 8 lambdas
  • Java 9 varhandles, specialied generics
slide-25
SLIDE 25

How does it work?

slide-26
SLIDE 26

Invoke

slide-27
SLIDE 27

Invoke

// Static System.currentTimeMillis() Math.log(1.0)

slide-28
SLIDE 28

Invoke

// Static System.currentTimeMillis() Math.log(1.0) // Virtual "hello".toUpperCase() System.out.println()

slide-29
SLIDE 29

Invoke

// Static System.currentTimeMillis() Math.log(1.0) // Virtual "hello".toUpperCase() System.out.println() // Interface myList.add("happy happy") myRunnable.run()

slide-30
SLIDE 30

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-31
SLIDE 31

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

slide-32
SLIDE 32

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

invokestatic invokevirtual invokeinterface invokespecial invokedynamic

  • 1. Call your bootstrap code
  • 2. Bootstrap wires up a target function
  • 3. Target function invoked directly until you change it
slide-33
SLIDE 33

method handles invokedynamic bytecode bootstrap method target method

slide-34
SLIDE 34

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-35
SLIDE 35

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

foo bar baz

Straight through dispatch path JRuby on Java 7

slide-36
SLIDE 36

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

foo bar baz

Optimizations (like inlining) can happen! JRuby on Java 7

slide-37
SLIDE 37

Indy in JRuby

  • Method dispatch
  • Look up method, cache, invalidate if class

changes or a new type is seen

  • Constant lookup
  • Retrieve constant, cache, invalidate if

redefined

slide-38
SLIDE 38

Times Faster than Ruby 1.9.3 1.25 2.5 3.75 5 base64 richards neural redblack

4.32 3.66 3.44 2.658 1.565 1.914 1.538 1.346

JRuby/Java 6 JRuby/Java 7

slide-39
SLIDE 39

red/black tree, pure Ruby versus native ruby-2.0.0 + Ruby ruby-2.0.0 + C ext jruby + Ruby Runtime per iteration 0.75 1.5 2.25 3

0.29s 0.51s 2.48s

slide-40
SLIDE 40

Nashorn

  • Built on indy from day 1
  • Speculative optimization
  • Fall back on fail and reoptimize
  • Performance comparable to

V8

  • Could become an indy language framework
slide-41
SLIDE 41

Lambdas (8)

  • Long-awaited closure support for Java
  • Similar to inner classes, but…
  • Lazily generate function class
  • Single object constructed, cached
  • All shared state passed on call stack
slide-42
SLIDE 42

Collections.sort(input, (a,b)->Integer.compare(a.length(), b.length()));

0: aload_0 1: invokedynamic #36, 0 6: invokestatic #37 // Comparator.sort:(…) 9: return 0: aload_0 1: invokevirtual #53 // String.length:()I 4: aload_1 5: invokevirtual #53 // String.length:()I 8: invokestatic #54 // Integer.compare:(II)I 11: ireturn

slide-43
SLIDE 43

Generic Specialization (9)

  • ArrayList<int> that actually uses int
  • Indy call site requests int-specialization
  • int version of class generated lazily
  • Working prototypes now in

Valhalla

slide-44
SLIDE 44

Ruby == UNIX

Unfortunately, Java == Java everywhere

slide-45
SLIDE 45

$ ruby -e "p Errno.constants” [:ENOTRECOVERABLE, :ENAMETOOLONG, :ENOLCK, :ENOSYS, :ENOTE MPTY, :ELOOP, :FFI, :EWOULDBLOCK, :ENOMSG, :EIDRM, :ENOSTR , :ENODATA, :ETIME, :ENOSR, :EREMOTE, :ENOLINK, :EPROTO, : EMULTIHOP, :EBADMSG, :EOVERFLOW, :EILSEQ, :EUSERS, :ENOTSO CK, :EDESTADDRREQ, :EMSGSIZE, :EPROTOTYPE, :ENOPROTOOPT, : EPROTONOSUPPORT, :ESOCKTNOSUPPORT, :EOPNOTSUPP, :EPFNOSUPP ORT, :Mapping, :EPERM, :EAFNOSUPPORT, :EADDRINUSE, :ENOENT , :EADDRNOTAVAIL, :ESRCH, :ENETDOWN, :EINTR, :ENETUNREACH, :EIO, :ENETRESET, :ENXIO, :ECONNABORTED, :E2BIG, :ECONNRES ET, :ENOEXEC, :ENOBUFS, :EBADF, :EISCONN, :ECHILD, :ENOTCO NN, :EAGAIN, :ESHUTDOWN, :ENOMEM, :ETOOMANYREFS, :EACCES, :ETIMEDOUT, :EFAULT, :ECONNREFUSED, :ENOTBLK, :EHOSTDOWN, :EBUSY, :EHOSTUNREACH, :EEXIST, :EALREADY, :EXDEV, :EINPRO GRESS, :ENODEV, :ESTALE, :ENOTDIR, :EDQUOT, :EISDIR, :EBAD RPC, :EINVAL, :ERPCMISMATCH, :ENFILE, :EPROGUNAVAIL, :EMFI LE, :EPROGMISMATCH, :ENOTTY, :EPROCUNAVAIL, :ETXTBSY, :EFT YPE, :EFBIG, :EAUTH, :ENOSPC, :ENEEDAUTH, :ESPIPE, :ECANCE

slide-46
SLIDE 46

$ ruby -retc -e "p Etc.public_methods.sort - Module.public_methods” [:endgrent, :endpwent, :getgrent, :getgrgid, :getgrnam, :getlogin, :getpwent, :getpwnam, : getpwuid, :group, :initialize, :passwd, :setg rent, :setpwent]

slide-47
SLIDE 47

Native Interop

slide-48
SLIDE 48

????

JVM World Native World

slide-49
SLIDE 49

“If non-Java programmers find some library useful and easy to access, it should be similarly accessible to Java programmers.”

  • John Rose
slide-50
SLIDE 50
slide-51
SLIDE 51

JNI

slide-52
SLIDE 52

User Code JNI call JNI impl Target Library

Java C/native

slide-53
SLIDE 53

public class GetPidJNI { public static native long getpid(); public static void main( String[] args ) { getpid(); } static { System.load( System.getProperty("user.dir") + "/getpidjni.dylib"); } }

JNI

slide-54
SLIDE 54

/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_headius_jnr_presentation_GetPidJNI */ #ifndef _Included_com_headius_jnr_presentation_GetPidJNI #define _Included_com_headius_jnr_presentation_GetPidJNI #ifdef __cplusplus extern "C" { #endif /* * Class: com_headius_jnr_presentation_GetPidJNI * Method: getpid * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_headius_jnr_1presentation_GetPidJNI_getpid (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif

JNI

slide-55
SLIDE 55

#include "com_headius_jnr_presentation_GetPidJNI.h" jlong JNICALL Java_com_headius_jnr_1presentation_GetPidJNI_getpid (JNIEnv *env, jclass c) { return getpid(); }

JNI

slide-56
SLIDE 56

$ gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -L $JAVA_HOME/jre/lib/ -dynamiclib -ljava

  • o getpidjni.dylib

com_headius_jnr_presentation_GetPidJNI. c $ java -Djava.library.path=`pwd` -cp target/jnr_presentation-1.0- SNAPSHOT.jar com.headius.jnr_presentation.GetPidJNI

JNI

slide-57
SLIDE 57

There Must Be A Better Way

slide-58
SLIDE 58

Java Native Runtime

  • Java API
  • for calling Native code
  • supported by a rich Runtime library
  • You may be familiar with JNA
  • Foreign Function Interface (FFI)
  • https://github.com/jnr
slide-59
SLIDE 59

User Code JNI call JNI impl Target Library

Java C/native

slide-60
SLIDE 60

User Code JNR stub JNI call JNI impl libffi Target Library

Java C/native

slide-61
SLIDE 61

import jnr.ffi.LibraryLoader; import jnr.ffi.annotations.IgnoreError; public class GetPidJNRExample { public interface GetPid { long getpid(); } public static void main( String[] args ) { GetPid getpid = LibraryLoader .create(GetPid.class) .load("c"); getpid.getpid(); } }

JNR

slide-62
SLIDE 62

Layered Runtime

jffi jnr-ffi libffi jnr-posix jnr-constants jnr-enxio jnr-x86asm jnr-unixsocket etc etc

slide-63
SLIDE 63

Platforms

  • Darwin (OS X): universal (+ppc?)
  • Linux: i386, x86_64, arm, ppc, ppc64, ppc64le, s390x
  • Windows: i386, x86_64
  • FreeBSD, OpenBSD: i386, x86_64
  • SunOS: i386, x86_64, sparc, sparcv9
  • AIX: ppc
  • OpenVMS, AS/400: builds out there somewhere
  • If your platform isn't here, contribute a build
slide-64
SLIDE 64

jnr-ffi

  • User-oriented API
  • Roughly equivalent to what JNA gives you
  • Functions, structs, callbacks, memory
  • https://github.com/jnr/jnr-ffi
slide-65
SLIDE 65

import jnr.ffi.LibraryLoader; import jnr.ffi.annotations.IgnoreError; public class GetPidJNRExample { public interface GetPid { long getpid(); } public static void main( String[] args ) { GetPid getpid = LibraryLoader .create(GetPid.class) .load("c"); getpid.getpid(); } }

jnr-ffi

slide-66
SLIDE 66

jnr-posix

  • Pre-bound set of POSIX functions
  • Mostly driven by what JRuby, Jython use
  • Goal: 100% of POSIX bound to Java
slide-67
SLIDE 67

public int chmod(String string, int i); public int chown(String string, int i, int i1); public int execv(String string, String[] strings); public int execve(String string, String[] strings, String[] strings1); public int fork(); public int seteuid(int i); public int getgid(); public String getlogin(); public int getpgid(); public int getpgid(int i); public int getpgrp(); public int getpid(); public int getppid(); public Passwd getpwent(); public Passwd getpwuid(int i); public Passwd getpwnam(String string); public Group getgrgid(int i); public Group getgrnam(String string); public int getuid(); public boolean isatty(FileDescriptor fd); public int kill(int i, int i1); public int symlink(String string, String string1); public int link(String string, String string1); public String readlink(String string) throws IOException; public String getenv(String string); public int setenv(String string, String string1, int i); public int unsetenv(String string); public int getpriority(int i, int i1); public int setpriority(int i, int i1, int i2); public int setuid(int i); public FileStat stat(String string); public int stat(String string, FileStat fs); public int umask(int i); public Times times(); public int utimes(String string, long[] longs, long[] longs1); public int waitpid(int i, int[] ints, int i1); public int wait(int[] ints); public int errno(); public void errno(int i); public int posix_spawnp(String string, List<? extends SpawnFileAction> list, List<? extends CharSequence> list1, List<? extends CharSequence> list2);

slide-68
SLIDE 68

POSIX posix = POSIXFactory.getPOSIX( new MyPOSIXHandler(this), isNativeEnabled); int pid = posix.getpid();

slide-69
SLIDE 69

jnr-enxio

  • Extended Native X-platform IO
  • NIO-compatible JNR-backed IO library
  • Read, write, select (kqueue, epoll, etc)
  • Low-level fcntl control
  • https://github.com/jnr/jnr-enxio
slide-70
SLIDE 70

public class NativeSocketChannel extends AbstractSelectableChannel implements ByteChannel, NativeSelectableChannel { public NativeSocketChannel(int fd); public NativeSocketChannel(int fd, int ops); public final int validOps(); public final int getFD(); public int read(ByteBuffer dst) throws IOException; public int write(ByteBuffer src) throws IOException public void shutdownInput() throws IOException; public void shutdownOutput() throws IOException; }

slide-71
SLIDE 71

jnr-unixsocket

  • UNIX sockets for NIO
  • Built atop jnr-enxio
  • Fully selectable, etc
  • https://github.com/jnr/jnr-unixsocket
slide-72
SLIDE 72

What Else?

  • NIO, NIO.2
  • Native IO, symlinks, FS-walking,
  • Unmanaged memory
  • Selectable stdio, process IO
  • Low-level or other sockets (UNIX, ICMP

, ...)

  • New APIs (graphics, crypto, OS, ...)
slide-73
SLIDE 73

Performance

  • Generated code leading to JNI call
  • Generated assembly version of native part
  • jnr-x86asm: Generate and link ASM
  • Used internally by jnr
  • https://github.com/jnr/jnr-x86asm
slide-74
SLIDE 74

getpid calls, 100M times

1ms 10ms 100ms 1000ms 10000ms 100000ms

JNA getpid JNR getpid

slide-75
SLIDE 75

import jnr.ffi.LibraryLoader; import jnr.ffi.annotations.IgnoreError; public class GetPidJNRExample { public interface GetPid { @IgnoreError long getpid(); } public static void main( String[] args ) { GetPid getpid = LibraryLoader .create(GetPid.class) .load("c"); getpid.getpid(); } }

@IgnoreError

slide-76
SLIDE 76

getpid calls, 100M times

0ms 500ms 1000ms 1500ms 2000ms

JNR getpid JNR getpid @IgnoreError

slide-77
SLIDE 77

getpid calls, 100M times

0ms 500ms 1000ms 1500ms 2000ms

JNR getpid JNI JNR @IgnoreError GCC -O3

But There's More to Do

slide-78
SLIDE 78

Project Panama

  • JEP-191: FFI for the JVM
  • Native interop at platform level
  • JIT intelligence
  • Security
  • Native method handles
  • Native memory layout and manipulation
slide-79
SLIDE 79

Ruby == Objects

Lots and lots and lots…

slide-80
SLIDE 80

Allocation is the root of all evil

slide-81
SLIDE 81

JRuby Over Time

2 4 6 8 1.0.3 1.1.6 1.4.0 1.5.6 1.6.8 1.7.0

bm_red_black_tree.rb

slide-82
SLIDE 82

[GC (Allocation Failure) 223608K->96408K(330752K), 0.0159780 secs] [GC (Allocation Failure) 208920K->100792K(335168K), 0.0157550 secs] [GC (Allocation Failure) 213304K->105144K(332160K), 0.0181010 secs] [GC (Allocation Failure) 205112K->108920K(334400K), 0.0187580 secs] [GC (Allocation Failure) 208888K->112712K(329152K), 0.0154440 secs]

8.1s

  • J-verbose:gc

[GC (Allocation Failure) 313780K->199892K(339072K), 0.0142010 secs] [GC (Allocation Failure) 318420K->204420K(331520K), 0.0175690 secs] [GC (Allocation Failure) 306948K->208316K(335680K), 0.0188120 secs] [Full GC (Ergonomics) 208316K->54991K(352256K), 0.2709750 secs] [GC (Allocation Failure) 157519K->58959K(349248K), 0.0120840 secs]

8.4s Around 1.8GB/s

+28 more +28 more +28 more +28 more

slide-83
SLIDE 83

+28 more [GC (Allocation Failure) 155729K->39697K(207296K), 0.0072730 secs]

0.963s

[GC (Allocation Failure) 160785K->40657K(208320K), 0.0108620 secs]

0.968s

[GC (Allocation Failure) 161745K->41649K(210112K), 0.0083760 secs]

0.968s

[GC (Allocation Failure) 166193K->39729K(210688K), 0.0070670 secs]

0.99s

  • J-verbose:gc
slide-84
SLIDE 84

Hard to Fix

  • Closures/blocks
  • Need to put local vars in heap structure
  • Numerics
  • All call paths require references
  • Transient data structures
  • [foo,bar,baz].map(&:to_s).sort.first
slide-85
SLIDE 85

Hard to Fix

  • Closures/blocks
  • Need to put local vars in heap structure
  • Numerics
  • All call paths require references
  • Transient data structures
  • [foo,bar,baz].lazy.map(&:to_s).sort.first
slide-86
SLIDE 86

Help Me, JVM

  • OpenJDK/Hotspot escape analysis
  • If an object never leaves a piece of code,

and is never visible across threads, modify the code to just use the object contents.

  • Fails to work in 99% of our cases
  • Improvements on the way, we hope
slide-87
SLIDE 87

Truffle and Graal

slide-88
SLIDE 88

Out of our control Written in C++

JVM Bytecode JVM Language Bytecode Interpreter Bytecode JIT Native Code

slide-89
SLIDE 89

What If…

  • The JVM’s JIT optimizer were written in Java
  • You could customize how the JIT works for

your language or library

  • JITed code could directly make native calls
slide-90
SLIDE 90

Graal

  • A 100% Java-based JIT framework
  • Grew out of the 100% Java “Maxine” JVM
  • Emits assembly or HotSpot IR
  • Directly control code generation
  • Route around JVM bytecode
  • http://openjdk.java.net/projects/graal/
slide-91
SLIDE 91

Plain Java APIs Under your control

Graal Intermediate Representation JVM Language Graal Optimizer Native Code Your Optimizations

slide-92
SLIDE 92

However…

  • Not everyone is a compiler writer
  • Graal’s IR is low-level and nontrivial
  • Need to understand JVM internals
  • Need some understanding of CPU
slide-93
SLIDE 93

The Dream

  • Design your language
  • ???
  • PROFIT
slide-94
SLIDE 94

What We Want

  • Design your language
  • Write an interpreter
  • PROFIT
slide-95
SLIDE 95

Truffle

  • Language framework built on Graal
  • Designed to fulfill the dream
  • Implement interpreter
  • Truffle feeds that to backend
  • No compiler expertise needed
  • https://wiki.openjdk.java.net/display/Graal/Truffle+FAQ+and+Guidelines
slide-96
SLIDE 96

Truffle AST JVM Language Graal Intermediate Representation Graal Optimizer Native Code

All we need

slide-97
SLIDE 97
slide-98
SLIDE 98
slide-99
SLIDE 99

The Payoff

  • RubyTruffle now part of JRuby
  • Released in JRuby 9k
  • Many languages in the works
  • May become part of OpenJDK releases?
  • Forcing Hotspot folks to push harder
slide-100
SLIDE 100

What Have We Learned?

slide-101
SLIDE 101

The JVM has its problems, but we can fix them.

slide-102
SLIDE 102

This is all open source…you can help

slide-103
SLIDE 103

Nothing is impossible.

slide-104
SLIDE 104

Thank you!

  • Charles Oliver Nutter
  • @headius, headius@headius.com
  • http://blog.headius.com