Over 9000
JRuby in 2015
Me
• Charles Oliver Nutter
• headius@headius.com
• @headius
• Red Hat
Ruby on the JVM
JVM SUCKS ROFL
AbstractMetaRubyImplementationFactoryFactoryImpl
I don't like Java so I don'tlike JRuby
LOL applets
JRuby is Ruby!
on the JVM... shhh!
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
Oldest alternative Ruby…around for 4-5
years before any others.
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
But why?
Ruby is not perfect.
Sometimes, Ruby is the
wrong tool.
We can make Ruby the
right tool for more jobs.
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!
Challenges
• Concurrency
• Language and Libraries
• Straight-line Performance
• Garbage Collection
• Tooling
Concurrency
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
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
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
Multicore in JRuby
300MB JRuby
Instance
One instance across 10 threads = 300MB
Multicore in JRuby
300MB JRuby
Instance
One instance across 100 threads = 300MB
MRI 1.9.3+, Eight Threads
JRuby, Eight Threads
Presentation:
Bringing Concurrency
to Ruby
Concurrency Futures
Education
concurrent-ruby
Work with ruby-core
on concurrency
primitives
Mutation testing and
monitoring
Object ownership and
explicit transfer
Language and Libraries
In MRI, two choices
Ruby
C
In JRuby, you can use…
Ruby
Java
Scala
Clojure
Javascript
Micro Focus
Visual Cobol
And dozens of others
And any language
callable with FFI
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
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
Alternatives
Java Integration
• Call Java (Scala, Clojure, ...) from Ruby
• Smart mapping of method names
• Type conversions as appropriate
• Super easy and fun
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
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
@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;

}
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
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)
Language and Library
Futures
9000 will match MRI
More core in Ruby
Eliminating object
wrappers
Better two-way
integration with JVM
languages
Better FFI tooling in
JRuby and JVM
Performance
Steady Improvement
MRI 1.9 ~ 2-3x 1.8
MRI 2.1 ~ 2-3x 1.9
Faster than most
interpreted languages
Not even close to
compiled/jitted speed
JRuby
• Faster than MRI on average
• Steady improvement
• Approaching Java in some areas
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)
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
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)
The Good
• Method dispatch cheap or free
• Method activation cheap or free
• Languages inline together (Java, Ruby, Scala…)
• Shared resources across languages
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
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
Performance Futures
Intermediate Representation
• Traditional compiler architecture for JRuby
• Instructions, operands, control-flow graph
• Optimization passes
• Optimize before emitting bytecode
• Closer mapping to JVM operations
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
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!
Numeric loop performance
0
0.9
1.8
2.7
3.6
times faster than MRI 2.1
JRuby 1.7 JRuby 9k
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
Numeric loop performance
0
15
30
45
60
times faster than MRI 2.1
JRuby 9k JRuby 1.7 9k+unbox
The Bad
• Work in progress - join us!
• Large-scale rework
• Requires knowledge of compiler design
• Doesn’t address object layout yet
Truffle
• Optimizing language framework
• Builds on Graal
• Write AST + interpreter, it builds a JIT
• Hints for object layout, unboxing
• Early days but very exciting
mandelbrot(500)
0
10
20
30
40
times faster than MRI 2.1
JRuby 9k + indy JRuby 9k + unboxing JRuby 9k + Truffle
GC
Time per GC versus heap usage
TimeperGC
0ms
75ms
150ms
225ms
300ms
Heap usage (MRI/JRuby)
188KB/29MB
Ruby 2.0.0 JRuby
GC runs per iteration, gc_stress.rb
0
750
1500
2250
3000
Ruby 2.2 JRuby
GC time per iteration, gc_stress.rb
0
0.6
1.2
1.8
2.4
Ruby 2.2 JRuby
Many GCs
• Serial collector
• Parallel collector
• Concurrent collector (CMS)
• Concurrent region collector (G1)
• Continuously concurrent collector (Azul)
JRuby, One Thread
JRuby, One Thread
Serial
GC
Parallel
GC
Concurrent
GC
Unlikely GC will be a
problem for most apps.
Tooling
JVM tools are awesome
$ jruby --manage …
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
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"
Heap Dumps
VisualVM or jmap + jhat
Useful to Us
Maybe Useful forYou
Tooling Futures
Alienist
Java Heap Ruby Heap
hprof JSON
https://github.com/enebo/alienist_viewer
https://github.com/enebo/alienist
Ruby Heap
JSON
Rails App
More Stats
• What metrics would be interesting?
• Hook up to monitoring services?
• Remote debugging and profiling?
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
{
"name": "Gem::Platform",
"size": 325,
"id": 34200727296,
"instances": [
{
"id": 34211817960,
"size": 40,
"variables": [
["@cpu", 34186089840],
["@os", 34211854176],
["@version", 34186089840]
],
"references": [34211803176]
},
]
},
Class name
ids!
alienist_viewer
alienist_viewer
alienist TODO
• Concept of Size, query language?
• Make (someone make) MRI dumper
• Allow for impl-specific data
• Specification for JSON format
Your Turn
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.
jruby.org
Use 'jruby-9000'
in Ruby installers
Test with 'jruby-head'
on TravisCI
File bugs at
bugs.jruby.org
Help us build
Ruby’s Future!
Thank you!

Over 9000: JRuby in 2015

  • 1.
  • 2.
    Me • Charles OliverNutter • headius@headius.com • @headius • Red Hat
  • 4.
    Ruby on theJVM JVM SUCKS ROFL AbstractMetaRubyImplementationFactoryFactoryImpl I don't like Java so I don'tlike JRuby LOL applets
  • 5.
    JRuby is Ruby! onthe JVM... shhh!
  • 6.
    Stable: 1.7.19 • Ruby1.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…aroundfor 4-5 years before any others.
  • 8.
    Aaron Patterson Aditya Bhardwaj AkinoriMUSHA 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
  • 10.
  • 11.
    Ruby is notperfect.
  • 12.
    Sometimes, Ruby isthe wrong tool.
  • 13.
    We can makeRuby the right tool for more jobs.
  • 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 • Languageand Libraries • Straight-line Performance • Garbage Collection • Tooling
  • 17.
  • 18.
    True Parallellism Ruby Threads Native Threads Ruby 1.8.7Ruby 2.0.0 Green Threading CPU Cores in Use JRuby Global LockSingle Thread Real Threading
  • 19.
    Multicore in MRI 200MBMRI Instance 200MB MRI Instance 200MB MRI Instance 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 200MBMRI Instance 200MB MRI Instance 200MB MRI Instance 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 300MBJRuby Instance One instance across 10 threads = 300MB
  • 22.
    Multicore in JRuby 300MBJRuby Instance One instance across 100 threads = 300MB
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
    Work with ruby-core onconcurrency primitives
  • 30.
  • 31.
  • 32.
  • 33.
    In MRI, twochoices
  • 34.
  • 35.
  • 36.
    In JRuby, youcan use…
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
    JRuby 1.6 CExts • Limited support (now disabled) • Removed to a separate repo • If you want it, support it • Some stuff worked...most didn’t
  • 46.
    Problems • Performance • Datacopying 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.
  • 48.
    Java Integration • CallJava (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(ThreadContextcontext, 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/DSLfor 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 classTimeval < 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.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
    Better FFI toolingin JRuby and JVM
  • 60.
  • 61.
  • 62.
    MRI 1.9 ~2-3x 1.8
  • 63.
    MRI 2.1 ~2-3x 1.9
  • 64.
  • 65.
    Not even closeto compiled/jitted speed
  • 66.
    JRuby • Faster thanMRI on average • Steady improvement • Approaching Java in some areas
  • 67.
    JVM Over Time 0 6 12 18 24 Java1.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 Java1.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.61.6.8 1.7.0 OpenJDK 8 (bm_red_black_tree.rb)
  • 70.
    The Good • Methoddispatch cheap or free • Method activation cheap or free • Languages inline together (Java, Ruby, Scala…) • Shared resources across languages
  • 71.
    red/black tree, pureRuby 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 • Unoptimizedpatterns 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.
  • 74.
    Intermediate Representation • Traditionalcompiler 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 InstructionsSemantic Analysis
  • 76.
    The Good • Well-understoodtechniques • 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 timesfaster 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 timesfaster than MRI 2.1 JRuby 9k JRuby 1.7 9k+unbox
  • 80.
    The Bad • Workin progress - join us! • Large-scale rework • Requires knowledge of compiler design • Doesn’t address object layout yet
  • 81.
    Truffle • Optimizing languageframework • 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 thanMRI 2.1 JRuby 9k + indy JRuby 9k + unboxing JRuby 9k + Truffle
  • 83.
  • 84.
    Time per GCversus heap usage TimeperGC 0ms 75ms 150ms 225ms 300ms Heap usage (MRI/JRuby) 188KB/29MB Ruby 2.0.0 JRuby
  • 85.
    GC runs periteration, gc_stress.rb 0 750 1500 2250 3000 Ruby 2.2 JRuby
  • 86.
    GC time periteration, gc_stress.rb 0 0.6 1.2 1.8 2.4 Ruby 2.2 JRuby
  • 87.
    Many GCs • Serialcollector • Parallel collector • Concurrent collector (CMS) • Concurrent region collector (G1) • Continuously concurrent collector (Azul)
  • 88.
  • 89.
  • 90.
    Unlikely GC willbe a problem for most apps.
  • 91.
  • 92.
  • 94.
  • 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.
  • 100.
  • 101.
  • 102.
  • 103.
    Alienist Java Heap RubyHeap hprof JSON https://github.com/enebo/alienist_viewer https://github.com/enebo/alienist Ruby Heap JSON Rails App
  • 104.
    More Stats • Whatmetrics 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.
  • 108.
  • 109.
    alienist TODO • Conceptof Size, query language? • Make (someone make) MRI dumper • Allow for impl-specific data • Specification for JSON format
  • 110.
  • 111.
    Excluding merges, 24authors 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.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.