Over 9000: JRuby in 2015

F1d37642fdaa1662ff46e4c65731e9ab?s=47 headius
January 31, 2015

Over 9000: JRuby in 2015

JRuby 9000 represents the biggest-ever leap forward for JRuby. Not only have we caught up on compatibility (9000 will be 2.2-compatible from release), but we've completely redesigned our JVM-based runtime and have opened our codebase up to the JRuby+Truffle research project from Oracle Labs. The changes we've made will make it easier to keep up with MRI on compatibility and give us the potential to run Ruby as fast as Java or C. The entire Ruby world will change over the next year, and JRuby 9000 will be leading the way. We'll talk about what Ruby's going to look like once JRuby is "over 9000".

F1d37642fdaa1662ff46e4c65731e9ab?s=128

headius

January 31, 2015
Tweet

Transcript

  1. Over 9000 JRuby in 2015

  2. Me • Charles Oliver Nutter • headius@headius.com • @headius •

    Red Hat
  3. None
  4. Ruby on the JVM JVM SUCKS ROFL AbstractMetaRubyImplementationFactoryFactoryImpl I don't

    like Java so I don't like JRuby LOL applets
  5. JRuby is Ruby! on the JVM... shhh!

  6. Stable: 1.7.19 • Ruby 1.8 and 1.9 support • AST

    interpreter and JVM bytecode JIT • Easy integration with JVM libraries • No support for MRI C ext
  7. Oldest alternative Ruby…around for 4-5 years before any others.

  8. Aaron Patterson Aditya Bhardwaj Akinori MUSHA Alan Moore Alex Coles

    Alex Dowad Alex Tambellini Aliaksei Palkanau Aman Gupta Anders Bengtsson Andreas Woess Andrew Grimm Andrew Kiellor Andy Lindeman Anil Wadghule Anoop Sankar Anthony W. Juckel Antoine Toulme Arne Hormann Arun Agrawal Aslak Hellesøy Atsuhiko Yamanaka Ben Browning Benoit Cerrina Benoit Daloze Bernerd Schaefer Bernhard Urban Bill Dortch Bob Beaty Bob McWhirter Bob Potter Bohuslav Kabrda Brad Heller Brandur Brian Browning Brice Figureau Bruce Adams Bruno Oliveira Chad Fowler Charles Oliver Nutter Charlie Somerville Chris Andrews Chris Heald Chris Jester-Young Chris Price Chris Seaton Chris Sinjakli Chris White Christian Christian Meier Christoffer Sawicki Clayton O'Neill Clayton Wheeler Colin Jones Conrad Irwin Daniel Azuma Daniel Hahn Daniel Lucraft Daniel Luz Daniel Marcotte Daniel Noll Daniel Pittman Dario Bertini Dave Thomas David Calavera David Corbin David Grayson David Hudson David Kellum David Masover David Pollak Deepak Giridharagopal Dennis Ranke Dmitry Ratnikov Don Schwartz Douglas Campos Dwayne Litzenberger Ed Sinjiashvili Edward Anderson Eric Sendelbach Erik Michaels-Ober Frederic Jean Garrett Conaty Gerard Fowley Gino Lucero Greg Mefford Gustav Munkby Gustavo Frederico Temple Pedrosa Heiko W. Rupp Hiro Asari Hironobu Nishikokura Hiroshi Nakamura Hongli Lai (Phusion) Iain Barnett Ian Dees Isaiah Peng Jacob Evans Jake Goulding James Abley James Pickering Jan Arne Petersen Jan Graichen Jan Xie Jason Karns Jason Staten Jason Voegele Javier Jay Jeff Pace Jeff Simpson Jeff Stone Jeremy Evans Jez Ng Joe Joe Kutner Joey Gibson John Croisant John F. Douthat John Firebaugh John Shahid Jon Zeppieri Jonathan Adams Jordan Sissel Jose Rivera Josef Haider Joseph LaFata Josh Ballanco Josh Matthews Joshua Go Juergen Herzog Kamil Bednarz Karol Bucek Kenichi Kamiya Ketan Padegaonkar Kevin Menard Kohsuke Kawaguchi Koichiro Ohba Konstantin Haase Kouhei Sutou Kristian Meier Kubo Takehiro Kyrylo Silin Lars Westergren Lelon Stoldt Leonardo Borges Lin Jen-Shin Loren Segal Luca Simone Lucas Allan Amorim Malte Swart Manish Marcin Mielzynski Marcin Mielżyński Mark McCraw Mark Rada Mark Triggs Mark Warren Martin Harriman Martin Ott Martin Traverso Mateusz Lenik Matjaz Gregoric Matt Hauck Matt Wilbur Matt Wilson Matthew Denner Matthew Kerwin Matthias Grimmer Maximilian Konzack MenTaLguY Micah Martin Michael J. Cohen Michael Klishin Michael Kohl Michal Papis Mike Dalessio NAKAMURA NARUSE, Yui Naoto "Kevin" IMAI TOYODA Nicholas Jefferson Nick Howard Nick Klauer Nick Klauer (a03182) Nick Muerdter Nick Sieger Ola Bini Ole Christian Rynning Olov Lassus Ori Kremer Pablo Varela Patrick Mahoney Patrick Plenefisch Patrick Toomey Paul Brown Paul Mucur Paul Phillips Pekka Enberg Peter Suschlik Peter Vandenabeele Phil Smith Philip Jenvey Pierre-Yves Ritschard Pierrick Rouxel Prathamesh Sonpatki Rajarshi Das Rhett Sutphin Rick Ohnemus Riley Lynch Robert Glaser Robin Dupret Robin Message Rohit Arondekar Ron Dahlgren Ryan Blue Ryan Brown Ryan Fowler Sakumatti Luukkonen Samu Voutilainen Satoru Chinen Scott Blum Scott Clasen Seamus Abshere Sebastian Staudt Sebastien Le Callonnec Seth Wright Shugo Maeda Smit Shah Stefan Huber Stefan Matthias Aust Stephen Bannasch Steven Cook Steven Parkes Subramanya Sastry Syver Enstad Sébastien Le Callonnec Ted Pennings Teemu Theo Theo Hultberg Thomas E Enebo Thomas E. Enebo Thomas Enebo Thomas Wuerthinger Tim Felgentreff Tobi Vollebregt Tobias Crawley Travis Tilley Tristan Hill Unbit Uwe Kubosch Vipul A M Vishnu Gopal Vitor de Lima Vladimir Sizikov Vít Ondruch Wayne Meissner William Thurston Xavier Shay Xb Yoko Harada Yosuke Zach Anker amuino areman arkxu donv elcubo enebo eregon geemus hmalphettes james jc00ke john muhl jonforums josedonizetti kares kiichi kristian lfstad-bren mkristian mohamed peter royal qbproger rdp retnuh rogerdpack rohit ryenus sglee77 simonjsmithuk takeru tduehr the8472 thedarkone timfel tnarik uid41545 unknown wayne williamd wpc yousuke zszugyi
  9. None
  10. But why?

  11. Ruby is not perfect.

  12. Sometimes, Ruby is the wrong tool.

  13. We can make Ruby the right tool for more jobs.

  14. None
  15. Preview: JRuby 9000 • Ruby 2.2 compatible • New optimizing

    runtime and compiler • Reworked IO, Process, encoding, and more • JRuby 9.0.0.0.pre1 is already out there!
  16. Challenges • Concurrency • Language and Libraries • Straight-line Performance

    • Garbage Collection • Tooling
  17. Concurrency

  18. True Parallellism Ruby Threads Native Threads Ruby 1.8.7 Ruby 2.0.0

    Green Threading CPU Cores in Use JRuby Global Lock Single Thread Real Threading
  19. Multicore in MRI 200MB MRI Instance 200MB MRI Instance 200MB

    MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance Ten-way concurrency * 200MB = 2GB
  20. Multicore in MRI 200MB MRI Instance 200MB MRI Instance 200MB

    MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 100-way concurrency * 200MB = 20GB 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance 200MB MRI Instance
  21. Multicore in JRuby 300MB JRuby Instance One instance across 10

    threads = 300MB
  22. Multicore in JRuby 300MB JRuby Instance One instance across 100

    threads = 300MB
  23. MRI 1.9.3+, Eight Threads

  24. JRuby, Eight Threads

  25. Presentation: Bringing Concurrency to Ruby

  26. Concurrency Futures

  27. Education

  28. concurrent-ruby

  29. Work with ruby-core on concurrency primitives

  30. Mutation testing and monitoring

  31. Object ownership and explicit transfer

  32. Language and Libraries

  33. In MRI, two choices

  34. Ruby

  35. C

  36. In JRuby, you can use…

  37. Ruby

  38. Java

  39. Scala

  40. Clojure

  41. Javascript

  42. Micro Focus Visual Cobol

  43. And dozens of others

  44. And any language callable with FFI

  45. JRuby 1.6 C Exts • Limited support (now disabled) •

    Removed to a separate repo • If you want it, support it • Some stuff worked...most didn’t
  46. Problems • Performance • Data copying to emulate raw structs

    • Locking to keep C code thread-safe • Multiple JRuby instances in one JVM • No way from C to know which one • Huge API to support
  47. Alternatives

  48. Java Integration • Call Java (Scala, Clojure, ...) from Ruby

    • Smart mapping of method names • Type conversions as appropriate • Super easy and fun
  49. import javax.swing.JFrame import javax.swing.JLable frame = JFrame.new("Window") label = JLabel.new("Hello")

    frame.add(label) frame.default_close_operation = JFrame::EXIT_ON_CLOSE frame.pack frame.visible = true
  50. Java Native Extensions • Similar to C ext for MRI,

    but with Java • Fast call protocol...basically free • Same GC for all objects • Have to keep in sync if C version too
  51. @JRubyMethod
 public IRubyObject each(ThreadContext context, Block block) {
 if (!block.isGiven())

    {
 return enumeratorizeWithSize(context, this, "each", enumLengthFn());
 }
 
 for (int i = 0; i < realLength; i++) {
 block.yield(context, safeArrayRef(values, begin + i));
 } 
 return this;
 }
  52. FFI • Ruby API/DSL for calling native code • Came

    from Rubinius • Runs on all Ruby impls • Solves "access" use case • Works well for coarse-grained calls
  53. Ruby FFI example class Timeval < FFI::Struct layout :tv_sec =>

    :ulong, :tv_usec => :ulong end module LibC extend FFI::Library ffi_lib FFI::Library::LIBC attach_function :gettimeofday, [ :pointer, :pointer ], :int end t = Timeval.new LibC.gettimeofday(t.pointer, nil)
  54. Language and Library Futures

  55. 9000 will match MRI

  56. More core in Ruby

  57. Eliminating object wrappers

  58. Better two-way integration with JVM languages

  59. Better FFI tooling in JRuby and JVM

  60. Performance

  61. Steady Improvement

  62. MRI 1.9 ~ 2-3x 1.8

  63. MRI 2.1 ~ 2-3x 1.9

  64. Faster than most interpreted languages

  65. Not even close to compiled/jitted speed

  66. JRuby • Faster than MRI on average • Steady improvement

    • Approaching Java in some areas
  67. 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)
  68. Versus MRI 1.8 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
  69. 0 0.55 1.1 1.65 2.2 1.1.6 1.4.0 1.5.6 1.6.8 1.7.0

    OpenJDK 8 (bm_red_black_tree.rb)
  70. The Good • Method dispatch cheap or free • Method

    activation cheap or free • Languages inline together (Java, Ruby, Scala…) • Shared resources across languages
  71. red/black tree, pure Ruby versus native ruby-2.0.0 + Ruby ruby-2.0.0

    + C ext jruby + Ruby Runtime per iteration 0 0.75 1.5 2.25 3 0.29s 0.51s 2.48s
  72. The Bad • Unoptimized patterns often have terrible performance characteristics

    • Numeric operations allocate objects • Instance vars require wrapper, indirection • Java objects must be wrapped or marshaled • Closure state is expensive • Blah blah startup time blah blah
  73. Performance Futures

  74. Intermediate Representation • Traditional compiler architecture for JRuby • Instructions,

    operands, control-flow graph • Optimization passes • Optimize before emitting bytecode • Closer mapping to JVM operations
  75. def foo(a, b) c = 1 d = a +

    c end 0 check_arity(2, 0, -1) 1 a = recv_pre_reqd_arg(0) 2 b = recv_pre_reqd_arg(1) 3 %block = recv_closure 4 thread_poll 5 line_num(1) 6 c = 1 7 line_num(2) 8 %v_0 = call(:+, a, [c]) 9 d = copy(%v_0) 10 return(%v_0) Register-based 3 address format IR Instructions Semantic Analysis
  76. The Good • Well-understood techniques • Creator worked on Java

    JIT years ago • Drastically simpler JVM bytecode backend • Less one-off opto code • Simpler runtime • Performance!
  77. Numeric loop performance 0 0.9 1.8 2.7 3.6 times faster

    than MRI 2.1 JRuby 1.7 JRuby 9k
  78. def loop(a)
 i = 0
 while i < a
 i

    +=1
 end
 end def loop(a:int)
 i = 0
 while i < a
 i +=1
 end
 end
  79. Numeric loop performance 0 15 30 45 60 times faster

    than MRI 2.1 JRuby 9k JRuby 1.7 9k+unbox
  80. The Bad • Work in progress - join us! •

    Large-scale rework • Requires knowledge of compiler design • Doesn’t address object layout yet
  81. Truffle • Optimizing language framework • Builds on Graal •

    Write AST + interpreter, it builds a JIT • Hints for object layout, unboxing • Early days but very exciting
  82. mandelbrot(500) 0 10 20 30 40 times faster than MRI

    2.1 JRuby 9k + indy JRuby 9k + unboxing JRuby 9k + Truffle
  83. GC

  84. Time per GC versus heap usage Time per GC 0ms

    75ms 150ms 225ms 300ms Heap usage (MRI/JRuby) 188KB/29MB Ruby 2.0.0 JRuby
  85. GC runs per iteration, gc_stress.rb 0 750 1500 2250 3000

    Ruby 2.2 JRuby
  86. GC time per iteration, gc_stress.rb 0 0.6 1.2 1.8 2.4

    Ruby 2.2 JRuby
  87. Many GCs • Serial collector • Parallel collector • Concurrent

    collector (CMS) • Concurrent region collector (G1) • Continuously concurrent collector (Azul)
  88. JRuby, One Thread

  89. JRuby, One Thread Serial GC Parallel GC Concurrent GC

  90. Unlikely GC will be a problem for most apps.

  91. Tooling

  92. JVM tools are awesome

  93. None
  94. $ jruby --manage …

  95. None
  96. None
  97. require 'jmx'
 
 def in_mb(value)
 format "%0.2f Mb" % (value.to_f

    / (1024 * 1024))
 end
 
 server = JMX.simple_server
 client = JMX.connect
 memory = client["java.lang:type=Memory"]
 
 Thread.new do
 puts "Enter 'gc' to garbage collect or anything else to quit"
 while gets.chomp == "gc"
 memory.gc
 end
 
 server.stop
 exit 0
 end
 
 while (true)
 heap = in_mb(memory.heap_memory_usage.used)
 non_heap = in_mb(memory.non_heap_memory_usage.used)
 
 puts "Heap: #{heap}, Non-Heap: #{non_heap}"
 sleep(2)
 end
  98. require 'jmx'
 
 class UsefulMetrics < RubyDynamicMBean
 rw_attribute :name, :string,

    "My sample attribute"
 r_attribute :explicit_reader, :int, "Sample int with writer", :my_reader
 
 operation "Doubles a value"
 parameter :int, "a", "Value to double"
 returns :int
 def double(a)
 a + a
 end
 end
 
 my_server = JMX::MBeanServer.new
 
 dyna = UsefulMetrics.new("domain.UsefulMetrics", $$.to_s)
 domain = my_server.default_domain
 my_server.register_mbean dyna, "#{domain}:type=UsefulMetrics"
  99. Heap Dumps VisualVM or jmap + jhat

  100. Useful to Us

  101. Maybe Useful for You

  102. Tooling Futures

  103. Alienist Java Heap Ruby Heap hprof JSON https://github.com/enebo/alienist_viewer https://github.com/enebo/alienist Ruby

    Heap JSON Rails App
  104. More Stats • What metrics would be interesting? • Hook

    up to monitoring services? • Remote debugging and profiling?
  105. alienist $ jruby -e 'gets'& $ jmap -dump:live,file=foo.dump <pid> $

    alienist -c foo.dump Took 33.7012s to parse 596577 objects from 5698 classes. Ruby Instances: 11,627, Ruby Classes: 351
  106. { "name": "Gem::Platform", "size": 325, "id": 34200727296, "instances": [ {

    "id": 34211817960, "size": 40, "variables": [ ["@cpu", 34186089840], ["@os", 34211854176], ["@version", 34186089840] ], "references": [34211803176] }, ] }, Class name ids!
  107. alienist_viewer

  108. alienist_viewer

  109. alienist TODO • Concept of Size, query language? • Make

    (someone make) MRI dumper • Allow for impl-specific data • Specification for JSON format
  110. Your Turn

  111. Excluding merges, 24 authors have pushed 790 commits to master

    and 909 commits to all branches. On master, 1,880 files have changed and there have been 69,878 additions and 66,549 deletions.
  112. jruby.org

  113. Use 'jruby-9000' in Ruby installers

  114. Test with 'jruby-head' on TravisCI

  115. File bugs at bugs.jruby.org

  116. Help us build Ruby’s Future!

  117. Thank you!