Upgrade to Pro — share decks privately, control downloads, hide ads and more …

State of JRuby 2014

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Hiro Asari Hiro Asari
February 21, 2014

State of JRuby 2014

RubyConf AU
21 February, 2014, Sydney, NSW, Australia

Video: https://vimeo.com/90823948

Avatar for Hiro Asari

Hiro Asari

February 21, 2014
Tweet

More Decks by Hiro Asari

Other Decks in Technology

Transcript

  1. Ruby Thread models OS Threads Green Threads Kernel GIL Ruby

    interpreter Ruby 1.8 Green Threads GIL Ruby interpreter Kernel Ruby 1.9, 2.0 Green Threads Kernel JVM JRuby
  2. Time per GC versus heap usage Time per GC 0ms

    75ms 150ms 225ms 300ms Heap usage (MRI/JRuby) 188KB/29MB 27MB/127MB 199MB/238MB Ruby 2.0.0 JRuby
  3. 0 1.5 3 4.5 6 Interpreted AST (i.e., no compiler)

    Interpreted IR MRI 2.1.0 Compiled AST, no InvokeDynamic Compiled AST, with InvokeDynamic Mandelbrot
  4. Web Benchmarks JSON Serialization 0 5500 11000 16500 22000 rack

    rails-stripped rails sinatra jruby 1.7.8 ruby 2.0.0
  5. Web Benchmarks JSON Serialization (Log scale) 1 100 10000 rack

    rails-stripped rails sinatra jruby 1.7.8 ruby 2.0.0
  6. Brief History of JRuby • 2001 — started by Jan

    Arne Petersen • 2006 — Rails support • 2011 — v 1.6.0 (experimental C extension support) • 2012 — v 1.7.0 (InvokeDynamic support)
  7. 2013 • Released 1.6.8 • community-based release • Released 1.7.2

    through 1.7.9 • 1.7.4 introduced experimental Ruby 2.0 support
  8. 2013 $ git checkout master Switched to branch 'master' Your

    branch is up-to-date with 'origin/master'. $ git log --format='%ai %s' | grep -v Merge | grep 2013-\\d\\d-\\d\\d -c 2459 ! ! ! ! ! ! ! !
  9. 2013 $ git co jruby-1_7 Switched to branch 'jruby-1_7' Your

    branch is up-to-date with 'origin/jruby-1_7'. $ git log --format='%ai %s' | grep -v Merge | grep 2013-\\d\\d-\\d\\d -c 1939 ! ! ! ! ! ! ! !
  10. JRuby Architecture JVM FFI JDK JRuby Core Classes Ruby Core

    classes Other Java Libraries Compiler & Byte Code Backend Extras Ruby Application Standard Libs
  11. JVM Over Time 0 6 12 18 24 Java 1.4

    Java 5 Java 6 Java 7 JRuby 1.0.3 (bm_red_black_tree.rb)
  12. vs MRI 0 6 12 18 24 Java 1.4 Java

    5 Java 6 Java 7 JRuby 1.0.3 (bm_red_black_tree.rb) MRI 1.8
  13. InvokeDynamic • New language feature in Java 7 • Optimized

    any kind of calls like Java calls • Ruby as fast as Java — in theory
  14. JRuby Architecture JVM FFI JDK JRuby Core Classes Ruby Core

    classes Other Java Libraries JRuby runtime Extras Ruby Application Standard Libs
  15. Nailgun • Keep a single JVM running in background •

    Toss commands over to it • It stays hot, so code starts faster • Hard to clean up all state (e.g. threads) • Can’t get access to user’s terminal
  16. Drip • Start a new JVM after each command •

    Pre-boot JVM plus optional code • Analyze command line for differences • Age out unused instances • https://github.com/flatland/drip
  17. Rails startup time Seconds (lower is better) 0 4 8

    12 16 7u51 7u51 + optimizations 8 b128 8 b128 + optimizations 7u51 + Drip 7u51 + optimizations + Drip 8 b128 + Drip 8 b128 + optimizations + Drip
  18. public class DripMain {! public static RubyInstanceConfig DRIP_CONFIG;! public static

    Ruby DRIP_RUNTIME;! ! public static final String JRUBY_DRIP_WARMUP_ENV = "JRUBY_DRIP_WARMUP";! public static final String JRUBY_DRIP_WARMUP_DEFAULT = "1 + 1";! public static final String JRUBY_DRIP_PREBOOT_FILE = "./dripmain.rb";! ! public static void main(String[] args) throws IOException {! // warmup JVM first! Ruby ruby = Ruby.newInstance();! ! String envWarmup = System.getenv(JRUBY_DRIP_WARMUP_ENV);! if (envWarmup != null && envWarmup.length() > 0) {! ruby.evalScriptlet(envWarmup);! } else {! ruby.evalScriptlet(JRUBY_DRIP_WARMUP_DEFAULT);! }! ! // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);! ! RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);! ! if (dripMain.exists()) {! FileInputStream fis = new FileInputStream(dripMain);! try {! ruby.getLoadService().load(dripMain.getAbsolutePath(), false);! } finally {! fis.close();! }! }! ! // use config and runtime from preboot process! DRIP_CONFIG = config;! DRIP_RUNTIME = ruby;! }! }!
  19. public static final String JRUBY_DRIP_WARMUP_ENV = "JRUBY_DRI public static final

    String JRUBY_DRIP_WARMUP_DEFAULT = "1 + 1 public static final String JRUBY_DRIP_PREBOOT_FILE = "./dripm public static void main(String[] args) throws IOException {! // warmup JVM first! Ruby ruby = Ruby.newInstance();! String envWarmup = System.getenv(JRUBY_DRIP_WARMUP_ENV);! if (envWarmup != null && envWarmup.length() > 0) {! ruby.evalScriptlet(envWarmup);! } else {! ruby.evalScriptlet(JRUBY_DRIP_WARMUP_DEFAULT);! }! // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);! RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);!
  20. public static final String JRUBY_DRIP_WARMUP_ENV = "JRUBY_DRI public static final

    String JRUBY_DRIP_WARMUP_DEFAULT = "1 + 1 public static final String JRUBY_DRIP_PREBOOT_FILE = "./dripm public static void main(String[] args) throws IOException {! // warmup JVM first! Ruby ruby = Ruby.newInstance();! String envWarmup = System.getenv(JRUBY_DRIP_WARMUP_ENV);! if (envWarmup != null && envWarmup.length() > 0) {! ruby.evalScriptlet(envWarmup);! } else {! ruby.evalScriptlet(JRUBY_DRIP_WARMUP_DEFAULT);! }! // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);! RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);!
  21. public static final String JRUBY_DRIP_WARMUP_ENV = "JRUBY_DRI public static final

    String JRUBY_DRIP_WARMUP_DEFAULT = "1 + 1 public static final String JRUBY_DRIP_PREBOOT_FILE = "./dripm public static void main(String[] args) throws IOException {! // warmup JVM first! Ruby ruby = Ruby.newInstance();! String envWarmup = System.getenv(JRUBY_DRIP_WARMUP_ENV);! if (envWarmup != null && envWarmup.length() > 0) {! ruby.evalScriptlet(envWarmup);! } else {! ruby.evalScriptlet(JRUBY_DRIP_WARMUP_DEFAULT);! }! // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);! RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);!
  22. // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);!

    RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);! if (dripMain.exists()) {! FileInputStream fis = new FileInputStream(dripMain);! try {! ruby.getLoadService().load(dripMain.getAbsolutePa } finally {! fis.close();! }! }! // use config and runtime from preboot process! DRIP_CONFIG = config;! DRIP_RUNTIME = ruby;! }!
  23. // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);!

    RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);! if (dripMain.exists()) {! FileInputStream fis = new FileInputStream(dripMain);! try {! ruby.getLoadService().load(dripMain.getAbsolutePa } finally {! fis.close();! }! }! // use config and runtime from preboot process! DRIP_CONFIG = config;! DRIP_RUNTIME = ruby;! }!
  24. // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);!

    RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);! if (dripMain.exists()) {! FileInputStream fis = new FileInputStream(dripMain);! try {! ruby.getLoadService().load(dripMain.getAbsolutePa } finally {! fis.close();! }! }! // use config and runtime from preboot process! DRIP_CONFIG = config;! DRIP_RUNTIME = ruby;! }!
  25. // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);!

    RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);! if (dripMain.exists()) {! FileInputStream fis = new FileInputStream(dripMain);! try {! ruby.getLoadService().load(dripMain.getAbsolutePa } finally {! fis.close();! }! }! // use config and runtime from preboot process! DRIP_CONFIG = config;! DRIP_RUNTIME = ruby;! }!
  26. // preboot actual runtime! Ruby.clearGlobalRuntime();! File dripMain = new File(JRUBY_DRIP_PREBOOT_FILE);!

    RubyInstanceConfig config = new RubyInstanceConfig();! ruby = Ruby.newInstance(config);! if (dripMain.exists()) {! FileInputStream fis = new FileInputStream(dripMain);! try {! ruby.getLoadService().load(dripMain.getAbsolutePa } finally {! fis.close();! }! }! // use config and runtime from preboot process! DRIP_CONFIG = config;! DRIP_RUNTIME = ruby;! }!
  27. JRuby Startup rake -T 0 2.5 5 7.5 10 C

    Ruby JRuby JRuby (best) JRuby (drip) JRuby (drip init) JRuby (dripmain)
  28. Brief History of JRuby • 2001 — started by Jan

    Arne Petersen • 2006 — Rails support • 2011 — v 1.6.0 (experimental C extension support) • 2012 — v 1.7.0 (InvokeDynamic support) • 2014 — v 9000
  29. JRuby 9000 Version 0 20 40 60 80 100 Years

    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Illustrator (Mac OS), since 1987 MRI (since 1993) JRuby (since 2001)
  30. JRuby Architecture JVM FFI JDK JRuby Core Classes Ruby Core

    classes Other Java Libraries Intermediate Representation Extras Ruby Application Standard Libs
  31. Intermediate Representation Lexical! Analysis Parsing Bytecode Generation Interpret AST IR

    Instructions CFG DFG … Existing New! Dalvik Generation … Semantic! Analysis Optimization
  32. 1 check_arity(2, 0, -1)! 2 a(0:0) = recv_pre_reqd_arg(0)! 3 thread_poll!

    4 line_num(2)! 5 %v_2 = call(+, a(0:0), [1:Fixnum])! 6 return(%v_2) -Xir.passes=LocalOptimizationPass,DeadCodeElimination def foo(a, b)! c = 1! d = a + c! end 0 check_arity(2, 0, -1)! 1 a(0:0) = recv_pre_reqd_arg(0)! 2 b(0:1) = recv_pre_reqd_arg(1)! 3 %block(0:2) = recv_closure! 4 thread_poll! 5 line_num(1)! 6 c(0:3) = 1:fixnum! 7 line_num(2)! 8 %v_0 = call(+, a(0:0), [c(0:3)])! 9 d(0:4) = copy(%v_0)! 10 return(%v_0) propagation def foo(a)! ! a + 1! end
  33. JRuby Architecture JVM FFI JDK JRuby Core Classes Ruby Core

    classes Other Java Libraries Truffle Extras Ruby Application Standard Libs
  34. Truffle • AST interpreting framework from Oracle Labs • Potential

    alternative to: • AST interpreter • bytecode backend • IR
  35. 0 1.5 3 4.5 6 Interpreted AST (i.e., no compiler)

    Interpreted IR MRI 2.1.0 Compiled AST, no InvokeDynamic Compiled AST, with InvokeDynamic Truffle Mandelbrot
  36. JRuby Architecture Graal FFI JDK JRuby Core Classes Ruby Core

    classes Other Java Libraries Extras Ruby Application Standard Libs Truffle
  37. Mandelbrot 0 1.5 3 4.5 6 Interpreted AST (i.e., no

    compiler) Interpreted IR Compiled AST, no InvokeDynamic Compiled AST, with InvokeDynamic MRI 2.1.0 Truffle Truffle + Graal
  38. JRuby Architecture JVM FFI JDK JRuby Core Classes Ruby Core

    classes Other Java Libraries JRuby runtime Extras Ruby Application Standard Libs
  39. 2.1 Compatibility • No 2.0 mode will be provided (1.7.x

    has limited support) • Some work to do on 2.1 specs