Slide 1

Slide 1 text

Over 9000 JRuby in 2015

Slide 2

Slide 2 text

Me • Charles Oliver Nutter • [email protected] • @headius • Red Hat

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Ruby on the JVM JVM SUCKS ROFL AbstractMetaRubyImplementationFactoryFactoryImpl I don't like Java so I don't like JRuby LOL applets

Slide 5

Slide 5 text

JRuby is Ruby! on the JVM... shhh!

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Oldest alternative Ruby…around for 4-5 years before any others.

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

But why?

Slide 11

Slide 11 text

Ruby is not perfect.

Slide 12

Slide 12 text

Sometimes, Ruby is the wrong tool.

Slide 13

Slide 13 text

We can make Ruby the right tool for more jobs.

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

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!

Slide 16

Slide 16 text

Challenges • Concurrency • Language and Libraries • Straight-line Performance • Garbage Collection • Tooling

Slide 17

Slide 17 text

Concurrency

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Multicore in JRuby 300MB JRuby Instance One instance across 10 threads = 300MB

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

MRI 1.9.3+, Eight Threads

Slide 24

Slide 24 text

JRuby, Eight Threads

Slide 25

Slide 25 text

Presentation: Bringing Concurrency to Ruby

Slide 26

Slide 26 text

Concurrency Futures

Slide 27

Slide 27 text

Education

Slide 28

Slide 28 text

concurrent-ruby

Slide 29

Slide 29 text

Work with ruby-core on concurrency primitives

Slide 30

Slide 30 text

Mutation testing and monitoring

Slide 31

Slide 31 text

Object ownership and explicit transfer

Slide 32

Slide 32 text

Language and Libraries

Slide 33

Slide 33 text

In MRI, two choices

Slide 34

Slide 34 text

Ruby

Slide 35

Slide 35 text

C

Slide 36

Slide 36 text

In JRuby, you can use…

Slide 37

Slide 37 text

Ruby

Slide 38

Slide 38 text

Java

Slide 39

Slide 39 text

Scala

Slide 40

Slide 40 text

Clojure

Slide 41

Slide 41 text

Javascript

Slide 42

Slide 42 text

Micro Focus Visual Cobol

Slide 43

Slide 43 text

And dozens of others

Slide 44

Slide 44 text

And any language callable with FFI

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Alternatives

Slide 48

Slide 48 text

Java Integration • Call Java (Scala, Clojure, ...) from Ruby • Smart mapping of method names • Type conversions as appropriate • Super easy and fun

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

@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;
 }

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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)

Slide 54

Slide 54 text

Language and Library Futures

Slide 55

Slide 55 text

9000 will match MRI

Slide 56

Slide 56 text

More core in Ruby

Slide 57

Slide 57 text

Eliminating object wrappers

Slide 58

Slide 58 text

Better two-way integration with JVM languages

Slide 59

Slide 59 text

Better FFI tooling in JRuby and JVM

Slide 60

Slide 60 text

Performance

Slide 61

Slide 61 text

Steady Improvement

Slide 62

Slide 62 text

MRI 1.9 ~ 2-3x 1.8

Slide 63

Slide 63 text

MRI 2.1 ~ 2-3x 1.9

Slide 64

Slide 64 text

Faster than most interpreted languages

Slide 65

Slide 65 text

Not even close to compiled/jitted speed

Slide 66

Slide 66 text

JRuby • Faster than MRI on average • Steady improvement • Approaching Java in some areas

Slide 67

Slide 67 text

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)

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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)

Slide 70

Slide 70 text

The Good • Method dispatch cheap or free • Method activation cheap or free • Languages inline together (Java, Ruby, Scala…) • Shared resources across languages

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Performance Futures

Slide 74

Slide 74 text

Intermediate Representation • Traditional compiler architecture for JRuby • Instructions, operands, control-flow graph • Optimization passes • Optimize before emitting bytecode • Closer mapping to JVM operations

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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!

Slide 77

Slide 77 text

Numeric loop performance 0 0.9 1.8 2.7 3.6 times faster than MRI 2.1 JRuby 1.7 JRuby 9k

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

Numeric loop performance 0 15 30 45 60 times faster than MRI 2.1 JRuby 9k JRuby 1.7 9k+unbox

Slide 80

Slide 80 text

The Bad • Work in progress - join us! • Large-scale rework • Requires knowledge of compiler design • Doesn’t address object layout yet

Slide 81

Slide 81 text

Truffle • Optimizing language framework • Builds on Graal • Write AST + interpreter, it builds a JIT • Hints for object layout, unboxing • Early days but very exciting

Slide 82

Slide 82 text

mandelbrot(500) 0 10 20 30 40 times faster than MRI 2.1 JRuby 9k + indy JRuby 9k + unboxing JRuby 9k + Truffle

Slide 83

Slide 83 text

GC

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

GC runs per iteration, gc_stress.rb 0 750 1500 2250 3000 Ruby 2.2 JRuby

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

Many GCs • Serial collector • Parallel collector • Concurrent collector (CMS) • Concurrent region collector (G1) • Continuously concurrent collector (Azul)

Slide 88

Slide 88 text

JRuby, One Thread

Slide 89

Slide 89 text

JRuby, One Thread Serial GC Parallel GC Concurrent GC

Slide 90

Slide 90 text

Unlikely GC will be a problem for most apps.

Slide 91

Slide 91 text

Tooling

Slide 92

Slide 92 text

JVM tools are awesome

Slide 93

Slide 93 text

No content

Slide 94

Slide 94 text

$ jruby --manage …

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

No content

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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"

Slide 99

Slide 99 text

Heap Dumps VisualVM or jmap + jhat

Slide 100

Slide 100 text

Useful to Us

Slide 101

Slide 101 text

Maybe Useful for You

Slide 102

Slide 102 text

Tooling Futures

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

More Stats • What metrics would be interesting? • Hook up to monitoring services? • Remote debugging and profiling?

Slide 105

Slide 105 text

alienist $ jruby -e 'gets'& $ jmap -dump:live,file=foo.dump $ alienist -c foo.dump Took 33.7012s to parse 596577 objects from 5698 classes. Ruby Instances: 11,627, Ruby Classes: 351

Slide 106

Slide 106 text

{ "name": "Gem::Platform", "size": 325, "id": 34200727296, "instances": [ { "id": 34211817960, "size": 40, "variables": [ ["@cpu", 34186089840], ["@os", 34211854176], ["@version", 34186089840] ], "references": [34211803176] }, ] }, Class name ids!

Slide 107

Slide 107 text

alienist_viewer

Slide 108

Slide 108 text

alienist_viewer

Slide 109

Slide 109 text

alienist TODO • Concept of Size, query language? • Make (someone make) MRI dumper • Allow for impl-specific data • Specification for JSON format

Slide 110

Slide 110 text

Your Turn

Slide 111

Slide 111 text

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.

Slide 112

Slide 112 text

jruby.org

Slide 113

Slide 113 text

Use 'jruby-9000' in Ruby installers

Slide 114

Slide 114 text

Test with 'jruby-head' on TravisCI

Slide 115

Slide 115 text

File bugs at bugs.jruby.org

Slide 116

Slide 116 text

Help us build Ruby’s Future!

Slide 117

Slide 117 text

Thank you!