Over 9000: JRuby in 2015

5,821 views

Published on

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

Published in: Technology

Over 9000: JRuby in 2015

  1. 1. Over 9000 JRuby in 2015
  2. 2. Me • Charles Oliver Nutter • headius@headius.com • @headius • Red Hat
  3. 3. Ruby on the JVM JVM SUCKS ROFL AbstractMetaRubyImplementationFactoryFactoryImpl I don't like Java so I don'tlike JRuby LOL applets
  4. 4. JRuby is Ruby! on the JVM... shhh!
  5. 5. 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
  6. 6. Oldest alternative Ruby…around for 4-5 years before any others.
  7. 7. 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 AtsuhikoYamanaka 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 JasonVoegele 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 PabloVarela Patrick Mahoney Patrick Plenefisch Patrick Toomey Paul Brown Paul Mucur Paul Phillips Pekka Enberg Peter Suschlik PeterVandenabeele 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 SamuVoutilainen 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 TobiVollebregt 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
  8. 8. But why?
  9. 9. Ruby is not perfect.
  10. 10. Sometimes, Ruby is the wrong tool.
  11. 11. We can make Ruby the right tool for more jobs.
  12. 12. 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!
  13. 13. Challenges • Concurrency • Language and Libraries • Straight-line Performance • Garbage Collection • Tooling
  14. 14. Concurrency
  15. 15. True Parallellism Ruby Threads Native Threads Ruby 1.8.7 Ruby 2.0.0 Green Threading CPU Cores in Use JRuby Global LockSingle Thread Real Threading
  16. 16. 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
  17. 17. 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
  18. 18. Multicore in JRuby 300MB JRuby Instance One instance across 10 threads = 300MB
  19. 19. Multicore in JRuby 300MB JRuby Instance One instance across 100 threads = 300MB
  20. 20. MRI 1.9.3+, Eight Threads
  21. 21. JRuby, Eight Threads
  22. 22. Presentation: Bringing Concurrency to Ruby
  23. 23. Concurrency Futures
  24. 24. Education
  25. 25. concurrent-ruby
  26. 26. Work with ruby-core on concurrency primitives
  27. 27. Mutation testing and monitoring
  28. 28. Object ownership and explicit transfer
  29. 29. Language and Libraries
  30. 30. In MRI, two choices
  31. 31. Ruby
  32. 32. C
  33. 33. In JRuby, you can use…
  34. 34. Ruby
  35. 35. Java
  36. 36. Scala
  37. 37. Clojure
  38. 38. Javascript
  39. 39. Micro Focus Visual Cobol
  40. 40. And dozens of others
  41. 41. And any language callable with FFI
  42. 42. 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
  43. 43. 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
  44. 44. Alternatives
  45. 45. Java Integration • Call Java (Scala, Clojure, ...) from Ruby • Smart mapping of method names • Type conversions as appropriate • Super easy and fun
  46. 46. 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
  47. 47. 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
  48. 48. @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;
 }
  49. 49. 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
  50. 50. 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)
  51. 51. Language and Library Futures
  52. 52. 9000 will match MRI
  53. 53. More core in Ruby
  54. 54. Eliminating object wrappers
  55. 55. Better two-way integration with JVM languages
  56. 56. Better FFI tooling in JRuby and JVM
  57. 57. Performance
  58. 58. Steady Improvement
  59. 59. MRI 1.9 ~ 2-3x 1.8
  60. 60. MRI 2.1 ~ 2-3x 1.9
  61. 61. Faster than most interpreted languages
  62. 62. Not even close to compiled/jitted speed
  63. 63. JRuby • Faster than MRI on average • Steady improvement • Approaching Java in some areas
  64. 64. 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)
  65. 65. 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
  66. 66. 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)
  67. 67. The Good • Method dispatch cheap or free • Method activation cheap or free • Languages inline together (Java, Ruby, Scala…) • Shared resources across languages
  68. 68. 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
  69. 69. 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
  70. 70. Performance Futures
  71. 71. Intermediate Representation • Traditional compiler architecture for JRuby • Instructions, operands, control-flow graph • Optimization passes • Optimize before emitting bytecode • Closer mapping to JVM operations
  72. 72. 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 InstructionsSemantic Analysis
  73. 73. 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!
  74. 74. Numeric loop performance 0 0.9 1.8 2.7 3.6 times faster than MRI 2.1 JRuby 1.7 JRuby 9k
  75. 75. 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
  76. 76. Numeric loop performance 0 15 30 45 60 times faster than MRI 2.1 JRuby 9k JRuby 1.7 9k+unbox
  77. 77. The Bad • Work in progress - join us! • Large-scale rework • Requires knowledge of compiler design • Doesn’t address object layout yet
  78. 78. Truffle • Optimizing language framework • Builds on Graal • Write AST + interpreter, it builds a JIT • Hints for object layout, unboxing • Early days but very exciting
  79. 79. mandelbrot(500) 0 10 20 30 40 times faster than MRI 2.1 JRuby 9k + indy JRuby 9k + unboxing JRuby 9k + Truffle
  80. 80. GC
  81. 81. Time per GC versus heap usage TimeperGC 0ms 75ms 150ms 225ms 300ms Heap usage (MRI/JRuby) 188KB/29MB Ruby 2.0.0 JRuby
  82. 82. GC runs per iteration, gc_stress.rb 0 750 1500 2250 3000 Ruby 2.2 JRuby
  83. 83. GC time per iteration, gc_stress.rb 0 0.6 1.2 1.8 2.4 Ruby 2.2 JRuby
  84. 84. Many GCs • Serial collector • Parallel collector • Concurrent collector (CMS) • Concurrent region collector (G1) • Continuously concurrent collector (Azul)
  85. 85. JRuby, One Thread
  86. 86. JRuby, One Thread Serial GC Parallel GC Concurrent GC
  87. 87. Unlikely GC will be a problem for most apps.
  88. 88. Tooling
  89. 89. JVM tools are awesome
  90. 90. $ jruby --manage …
  91. 91. 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
  92. 92. 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"
  93. 93. Heap Dumps VisualVM or jmap + jhat
  94. 94. Useful to Us
  95. 95. Maybe Useful forYou
  96. 96. Tooling Futures
  97. 97. Alienist Java Heap Ruby Heap hprof JSON https://github.com/enebo/alienist_viewer https://github.com/enebo/alienist Ruby Heap JSON Rails App
  98. 98. More Stats • What metrics would be interesting? • Hook up to monitoring services? • Remote debugging and profiling?
  99. 99. 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
  100. 100. { "name": "Gem::Platform", "size": 325, "id": 34200727296, "instances": [ { "id": 34211817960, "size": 40, "variables": [ ["@cpu", 34186089840], ["@os", 34211854176], ["@version", 34186089840] ], "references": [34211803176] }, ] }, Class name ids!
  101. 101. alienist_viewer
  102. 102. alienist_viewer
  103. 103. alienist TODO • Concept of Size, query language? • Make (someone make) MRI dumper • Allow for impl-specific data • Specification for JSON format
  104. 104. Your Turn
  105. 105. 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.
  106. 106. jruby.org
  107. 107. Use 'jruby-9000' in Ruby installers
  108. 108. Test with 'jruby-head' on TravisCI
  109. 109. File bugs at bugs.jruby.org
  110. 110. Help us build Ruby’s Future!
  111. 111. Thank you!

×