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

Over 9000: JRuby in 2015

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

headius

January 31, 2015
Tweet

More Decks by headius

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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!
  4. 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
  5. 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
  6. 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
  7. C

  8. 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
  9. 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
  10. Java Integration • Call Java (Scala, Clojure, ...) from Ruby

    • Smart mapping of method names • Type conversions as appropriate • Super easy and fun
  11. 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
  12. 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
  13. @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;
 }
  14. 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
  15. 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)
  16. 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)
  17. 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
  18. 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)
  19. The Good • Method dispatch cheap or free • Method

    activation cheap or free • Languages inline together (Java, Ruby, Scala…) • Shared resources across languages
  20. 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
  21. 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
  22. Intermediate Representation • Traditional compiler architecture for JRuby • Instructions,

    operands, control-flow graph • Optimization passes • Optimize before emitting bytecode • Closer mapping to JVM operations
  23. 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
  24. 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!
  25. 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
  26. Numeric loop performance 0 15 30 45 60 times faster

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

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

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

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

  31. 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
  32. Many GCs • Serial collector • Parallel collector • Concurrent

    collector (CMS) • Concurrent region collector (G1) • Continuously concurrent collector (Azul)
  33. 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
  34. 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"
  35. More Stats • What metrics would be interesting? • Hook

    up to monitoring services? • Remote debugging and profiling?
  36. 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
  37. { "name": "Gem::Platform", "size": 325, "id": 34200727296, "instances": [ {

    "id": 34211817960, "size": 40, "variables": [ ["@cpu", 34186089840], ["@os", 34211854176], ["@version", 34186089840] ], "references": [34211803176] }, ] }, Class name ids!
  38. alienist TODO • Concept of Size, query language? • Make

    (someone make) MRI dumper • Allow for impl-specific data • Specification for JSON format
  39. 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.