over 9000: jruby in 2015
TRANSCRIPT
Over 9000JRuby in 2015
Ruby on the JVM
JVM SUCKS R
OFLAbstractMetaRubyImplementationFactoryFactoryImpl
I don't like Java so I don't like JRuby
LOL applet
s
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 PattersonAditya BhardwajAkinori MUSHA
Alan MooreAlex Coles
Alex DowadAlex Tambellini
Aliaksei PalkanauAman Gupta
Anders BengtssonAndreas WoessAndrew GrimmAndrew KiellorAndy LindemanAnil WadghuleAnoop Sankar
Anthony W. JuckelAntoine ToulmeArne HormannArun AgrawalAslak Hellesøy
Atsuhiko YamanakaBen BrowningBenoit CerrinaBenoit Daloze
Bernerd SchaeferBernhard Urban
Bill DortchBob Beaty
Bob McWhirterBob Potter
Bohuslav KabrdaBrad Heller
BrandurBrian BrowningBrice FigureauBruce Adams
Bruno OliveiraChad Fowler
Charles Oliver NutterCharlie Somerville
Chris AndrewsChris Heald
Chris Jester-YoungChris Price
Chris SeatonChris SinjakliChris White
ChristianChristian Meier
Christoffer Sawicki
Clayton O'NeillClayton Wheeler
Colin JonesConrad IrwinDaniel AzumaDaniel Hahn
Daniel LucraftDaniel Luz
Daniel MarcotteDaniel Noll
Daniel PittmanDario BertiniDave Thomas
David CalaveraDavid CorbinDavid GraysonDavid HudsonDavid Kellum
David MasoverDavid Pollak
Deepak GiridharagopalDennis Ranke
Dmitry RatnikovDon Schwartz
Douglas CamposDwayne Litzenberger
Ed SinjiashviliEdward AndersonEric Sendelbach
Erik Michaels-OberFrederic Jean
Garrett ConatyGerard FowleyGino LuceroGreg Mefford
Gustav MunkbyGustavo Frederico
Temple PedrosaHeiko W. Rupp
Hiro AsariHironobu Nishikokura
Hiroshi NakamuraHongli Lai (Phusion)
Iain BarnettIan Dees
Isaiah PengJacob Evans
Jake GouldingJames Abley
James PickeringJan Arne Petersen
Jan GraichenJan Xie
Jason KarnsJason StatenJason Voegele
JavierJay
Jeff PaceJeff SimpsonJeff Stone
Jeremy EvansJez Ng
JoeJoe KutnerJoey Gibson
John CroisantJohn F. DouthatJohn Firebaugh
John ShahidJon Zeppieri
Jonathan AdamsJordan SisselJose RiveraJosef Haider
Joseph LaFataJosh Ballanco
Josh MatthewsJoshua Go
Juergen HerzogKamil BednarzKarol Bucek
Kenichi KamiyaKetan Padegaonkar
Kevin MenardKohsuke Kawaguchi
Koichiro OhbaKonstantin Haase
Kouhei SutouKristian MeierKubo Takehiro
Kyrylo SilinLars Westergren
Lelon StoldtLeonardo Borges
Lin Jen-ShinLoren SegalLuca Simone
Lucas Allan AmorimMalte Swart
ManishMarcin Mielzynski
Marcin MielżyńskiMark McCraw
Mark RadaMark Triggs
Mark WarrenMartin Harriman
Martin OttMartin TraversoMateusz Lenik
Matjaz GregoricMatt HauckMatt WilburMatt Wilson
Matthew DennerMatthew Kerwin
Matthias GrimmerMaximilian Konzack
MenTaLguYMicah Martin
Michael J. CohenMichael KlishinMichael KohlMichal Papis
Mike DalessioNAKAMURANARUSE, Yui
Naoto "Kevin" IMAI TOYODA
Nicholas JeffersonNick HowardNick Klauer
Nick Klauer (a03182)Nick Muerdter
Nick SiegerOla Bini
Ole Christian RynningOlov LassusOri KremerPablo Varela
Patrick MahoneyPatrick PlenefischPatrick Toomey
Paul BrownPaul MucurPaul Phillips
Pekka EnbergPeter Suschlik
Peter VandenabeelePhil Smith
Philip JenveyPierre-Yves Ritschard
Pierrick RouxelPrathamesh Sonpatki
Rajarshi DasRhett SutphinRick Ohnemus
Riley LynchRobert GlaserRobin DupretRobin Message
Rohit ArondekarRon Dahlgren
Ryan BlueRyan BrownRyan Fowler
Sakumatti LuukkonenSamu VoutilainenSatoru Chinen
Scott BlumScott Clasen
Seamus AbshereSebastian Staudt
Sebastien Le CallonnecSeth WrightShugo Maeda
Smit ShahStefan Huber
Stefan Matthias AustStephen Bannasch
Steven CookSteven Parkes
Subramanya SastrySyver Enstad
Sébastien Le CallonnecTed Pennings
TeemuTheo
Theo HultbergThomas E EneboThomas E. EneboThomas Enebo
Thomas WuerthingerTim FelgentreffTobi VollebregtTobias Crawley
Travis TilleyTristan Hill
UnbitUwe Kubosch
Vipul A MVishnu GopalVitor de Lima
Vladimir SizikovVít Ondruch
Wayne MeissnerWilliam Thurston
Xavier ShayXb
Yoko HaradaYosuke
Zach Ankeramuinoaremanarkxudonv
elcuboeneboeregongeemus
hmalphettesjamesjc00ke
john muhljonforums
josedonizettikareskiichi
kristianlfstad-brenmkristianmohamedpeter royalqbproger
rdpretnuh
rogerdpackrohit
ryenussglee77
simonjsmithuktakerutduehr
the8472thedarkone
timfeltnarik
uid41545unknown
waynewilliamd
wpcyousukezszugyi
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
NativeThreads
Ruby 1.8.7 Ruby 2.0.0
Green Threading
CPU Coresin 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 FocusVisual 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.JFrameimport javax.swing.JLable
frame = JFrame.new("Window")label = JLabel.new("Hello")
frame.add(label)frame.default_close_operation = JFrame::EXIT_ON_CLOSEframe.packframe.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
@JRubyMethodpublic 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 exampleclass Timeval < FFI::Struct layout :tv_sec => :ulong, :tv_usec => :ulongend
module LibC extend FFI::Library ffi_lib FFI::Library::LIBC attach_function :gettimeofday, [ :pointer, :pointer ], :intend
t = Timeval.newLibC.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 + cend
0 check_arity(2, 0, -1)1 a = recv_pre_reqd_arg(0)2 b = recv_pre_reqd_arg(1)3 %block = recv_closure4 thread_poll5 line_num(1)6 c = 17 line_num(2)8 %v_0 = call(:+, a, [c])9 d = copy(%v_0)10 return(%v_0)
Register-based
3 address format
IR InstructionsSemanticAnalysis
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.1JRuby 1.7 JRuby 9k
def loop(a) i = 0 while i < a i +=1 endend
def loop(a:int) i = 0 while i < a i +=1 endend
Numeric loop performance
0
15
30
45
60
times faster than MRI 2.1JRuby 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.1JRuby 9k + indy JRuby 9k + unboxing JRuby 9k + Truffle
GC
Time per GC versus heap usage
Tim
e pe
r G
C
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)) endserver = JMX.simple_serverclient = JMX.connectmemory = 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 endwhile (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 endendmy_server = JMX::MBeanServer.newdyna = UsefulMetrics.new("domain.UsefulMetrics", $$.to_s)domain = my_server.default_domainmy_server.register_mbean dyna, "#{domain}:type=UsefulMetrics"
Heap Dumps
VisualVM or jmap + jhat
Useful to Us
Maybe Useful for You
Tooling Futures
Alienist
Java Heap Ruby Heaphprof JSON
https://github.com/enebo/alienist_viewer
https://github.com/enebo/alienist
Ruby HeapJSON
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.dumpTook 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 atbugs.jruby.org
Help us buildRuby’s Future!
Thank you!