Slide 1

Slide 1 text

Scaling Ruby with JRuby Thomas E. Enebo (@tom_enebo) Charles O. Nutter (@headius)

Slide 2

Slide 2 text

• JRuby co-leads • Red Hat Charles Thomas Ruby Java Beer

Slide 3

Slide 3 text

What is JRuby? Ruby Implementation (2.5.7 compatible) …which happens to run on the Java Virtual Machine (JVM)

Slide 4

Slide 4 text

JRuby 9.2.9.0 • Lots of memory improvements • Better support for Java module system • .jruby.java_opts file to manage JVM options • Improved startup time for most commands • Dozens of issues fixed • 9.2.10.0 coming out next week? 9.3.0.0 next (2.6.x)

Slide 5

Slide 5 text

Why JRuby? What are the differences? - JRuby - C Ruby

Slide 6

Slide 6 text

Native Threads vs Global Interpreter Lock

Slide 7

Slide 7 text

require 'benchmark' ary = (1..1000000).to_a loop { puts Benchmark.measure { 10.times { ary.each {|i|} } } } Unthreaded require 'benchmark' ary = (1..1000000).to_a loop { puts Benchmark.measure { (1..10).map { Thread.new { ary.each {|i|} } }.map(&:join) } } Multi-threaded

Slide 8

Slide 8 text

Ruby 2.5.7 unthreaded JRuby unthreaded Ruby 2.5.7 threaded JRuby threaded

Slide 9

Slide 9 text

Time (in seconds) 0 0.09 0.18 0.27 0.36 Unthreaded Threaded 0.15 0.23 0.35 0.34 CRuby JRuby (lower is better)

Slide 10

Slide 10 text

Virtual Machine vs Going It Alone

Slide 11

Slide 11 text

Ruby Teams Hiro Marcin Nahi Subbu Douglas Christian Karol Tom Charlie Hiro charli Nahi zzak nurse hsbt matz ko1 nobu OS (libc, ...) JRuby MRI

Slide 12

Slide 12 text

Ruby Teams Hiro Marcin Nahi Subbu Douglas Christian Karol Tom Charlie Hiro charli Nahi zzak nurse hsbt matz ko1 nobu OS (libc, ...) JRuby MRI JVM POSIX

Slide 13

Slide 13 text

JVM Ruby Teams Hiro Marcin Nahi Subbu Douglas Christian Karol Tom Charlie Hiro charli Nahi zzak nurse hsbt matz ko1 nobu OS (libc, ...) JRuby MRI Better ??? POSIX

Slide 14

Slide 14 text

!Perspective! ??? Total control can be great!

Slide 15

Slide 15 text

Shoulders of Giants JVM J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose Hiro Marcin Nahi Subbu Douglas Christian Dmitry Tom Charlie JRuby

Slide 16

Slide 16 text

All the stuff! JVM J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose J. Rose Garbage Collection Native JIT Profiled Optimizations Native Threading Tooling Cross Platform

Slide 17

Slide 17 text

Alternate JVMs • Hotspot: standard OpenJDK Java VM • OpenJ9: open source version of IBM's J9 JVM • New GC and JIT tuning options • Startup time and JIT code-caching features • GraalVM: replaces Hotspot JIT with Graal JIT • New JIT optimizations, ahead-of-time native compilation

Slide 18

Slide 18 text

JVM is everywhere++! • Unix++, Windows • Exotic platforms: zLinux, OpenVMS, AS/400 • Mobile: Android’s Dalvik, Embedded JVMs

Slide 19

Slide 19 text

Java bytecode == portability

Slide 20

Slide 20 text

actionmailer-javamail, active_documentum, activerecord-jdbc-adapter, activerecord-jdbcdbf-adapter, activerecord-jdbcderby-adapter, activerecord-jdbch2-adapter, activerecord-jdbchsqldb-adapter, activerecord-jdbcmssql-adapter, activerecord-jdbcmysql-adapter, activerecord-jdbcpostgresql-adapter, activerecord-jdbcsqlite3- adapter, activerecord-netezza-adapter, activerecord-vertica-adapter, akephalos, akephalos-nerian, akephalos2, akka-actor-jars, akka-remote-jars, akubra_llstore_migrate, Antwrap, async-http-client-jars, atomic, atoulme-Antwrap, autotest-java, bbrowning-deltacloud-client, bbrowning-deltacloud-core, bcrypt- ruby, bee_java, berkeley-db-java-jars, bert, bio-maf, blockenspiel, boc, bond, bosdk, bouncy-castle-java, boxed-geminabox, brute-fuzzy, bryanl-gherkin, bson, buby, buildr, buildr-resolver, buildrizpack, butternut, capistrano-java, capybara-java_script_lint, carrierwave-neo4j, carrierwave_imagevoodoo, cassandra-jars, chrest, cloby, commons-io-jars, concurrently, contextual, coupler, cucumber-java, cucumber-jvm, cuke4duke, cuuid, dm-ldap-adapter, dm-lucene-adapter, do-jdbc_sqlserver, do_derby, do_h2, do_hsqldb, do_jdbc, do_mysql, do_openedge, do_oracle, do_postgres, do_sqlite3, do_sqlserver, doubleshot, dripdrop, dubious, duby, engineyard-visualvm, epall-limelight, errbit_zmq_handler, eurydice, euston, euston-daemons, euston-eventstore, euston-projections, euston-rabbitmq, euston- websites, eventmachine, excemel, faye-websocket, ffi, fig, file-find, fishwife, foreman, forkit, forkjoin, gamelan, gemshit, geoip-jars, get_back, gherkin, glassfish, gravitext-util, gravitext-xmlprod, grizzly, guava-jars, hadoop-find, hashdot-test-daemon, hiredis, hitimes, hope, hostor, hot_bunnies, hourglass, hpricot, http_parser.rb, inline_javascript, iudex, iudex-async-httpclient, iudex-barc, iudex-brutefuzzy-protobuf, iudex-brutefuzzy-service, iudex-char-detector, iudex-core, iudex-da, iudex-filter, iudex-html, iudex-http, iudex-http-test, iudex-httpclient-3, iudex-jetty-httpclient, iudex-rome, iudex-simhash, iudex-worker, ivy-jars, iyyov, jactive_support, java-autotest, java-inline, java2ruby-xmldsig, java_bin, java_inline, java_override, java_properties, java_streamify, java_testing_guff, javabean_xml, javaclass, javaeye4r, javagems, javajake, javaobj, javaobjs, javaobs, javaparse, javasand, javascript-securehash-rails, javascript-state-machine-rails, javascript_auto_include, javascript_eraser, javascript_features, javascript_i18n, javascript_localize, javascript_safe_logger, javascript_util_asset_pack, javascripto, javascripto-rails, jdbc-derby, jdbc-hsqldb, jdbc-jtds, jdbc-openedge, jdbc-openedge-internal, jdbc-postgres, jdbc-sqlite3, jedis-jars, jena-jruby, jessica, jettr, jetty, jetty- jsp, jgeoip, jms4r, jpdfer, jrack_handlers, jrtm, jruby-activemq, jruby-akka_jars, jruby-elasticsearch_jars, jruby-httpclient, jruby-launcher, jruby-management, jruby- metrics, jruby-pageant, jruby-vijava, jruby_gc_stats, jruby_sandbox, jruby_threach, jrubyconf-button, jsmetric4java, json, json-jruby, jsound, kb-activerecord-jdbc- adapter, kirk, kyotocabinet-java, ladle, latex-decode, launchy, libnotify, limelight, linecache, logback, logback-jars, looksee, lumix, mack-javascript, markdownj, maven_irb_plugin, metrics-core-jars, metrics-java, mguymon-buildr, mikka, mini_aether, mirah, mirah_model, miso-java, mixology, mm_mq, mongrel, msgpack-idl- java, msgpack-jruby, multimeter, naether, nanoc-javascript-concatenator, neo4j, neo4j-admin, neo4j-advanced, neo4j-community, neo4j-core, neo4j-enterprise, neo4j-spatial, neo4j-will_paginate, neo4j-wrapper, netty-jars, ning-compress-jars, nio4r, nokogiri, nokogiri-fitzsimmons, nokogiri-maven, nosqoop4u, ontomde- demo-java5, ontomde-java, ontomde-java-frontend, ontomde-uml2-java, ontomde-uml2-kbjava, open_nlp, pacer, pacer-dex, pacer-neo4j, pacer-orient, pelops-jars, persvr, pg_array_parser, protobuf-jars, pry, puma, qtjruby-core, qwirk_active_mq_adapter, qwirk_jms_adapter, rabbitmqadmin-cli, ragweed, rails_javascript_helpers, rakejava, rave, rcov, realityforge-jekyll, realityforge-jekylltask, redcar-bundles, redcar-clojure, redcar-filter-through-command, redcar- groovy, redcar-icons, redcar-javamateview, redcar-javascript, redcar-mirah, redcar-svnkit, redcar-xulrunner-win, RedCloth, refinerycms-javascripts, reigns, revo- nokogiri, rika, rjack-async-httpclient, rjack-commons-codec, rjack-commons-dbcp, rjack-commons-dbutils, rjack-commons-pool, rjack-httpclient-3, rjack- httpclient-4, rjack-icu, rjack-jackson, rjack-jdom, rjack-jets3t, rjack-jetty, rjack-jetty-jsp, rjack-jms, rjack-jms-spec, rjack-logback, rjack-lucene, rjack-maven, rjack- mina, rjack-nekohtml, rjack-protobuf, rjack-qpid-client, rjack-rome, rjack-slf4j, rjack-solr, rjack-tarpit, rjack-xerces, rmagick4j, rmodbus, rtm-javatmapi, rtm- majortom, rtm-ontopia, rtm-tinytim, rtm-tmql, rubeus, rubinius-core-api, ruby-blockcache, ruby-debug-base, ruby-maven, ruby2java, rubydoop, rubyjedi- nokogiri_java, scala-inline, scala-library-jars, scriptty, slf4j, slf4j-jars, slyphon-zookeeper, slyphon-zookeeper_jar, smackr, smartimage, SNMP4JR, solr_sail, spiegela- jruby-httpclient, sproutcore, spymemcached, sqldroid, steamcannon-deltacloud-client, steamcannon-deltacloud-core, stilts-stomp-client, supermarket, svm_toolkit, swt, theduke, thick, to-javascript, torquebox-base, torquebox-cache, torquebox-configure, torquebox-container-foundation, torquebox-core, torquebox- messaging, torquebox-messaging-container, torquebox-naming, torquebox-naming-container, torquebox-security, torquebox-server, torquebox-vfs, torquebox- web, twitter4j4r, UDJrb, unageanu-javaclass, unimidi, universe-javascript, url_escape, weakling, webbit-jars, wildnet-jackson, wildnet-netty, wildnet-server, wildsonet-hazelcast, wildsonet-netty, wildsonet-server, wildsonet-streamer, wrest, wrong, xdojava, xqruby, zookeeper No build tools!

Slide 21

Slide 21 text

Ruby C Extensions vs Java Native Extensions

Slide 22

Slide 22 text

Why Extensions? • Performance • Access Existing Library (e.g. openssl)

Slide 23

Slide 23 text

C Extensions • API is massive • API is specific to C Ruby’s implementation • It is the implementation • Huge support cost (if non-CRuby wanted to support it) • At odds with concurrent Ruby execution

Slide 24

Slide 24 text

Java Native Extensions • API is massive • API is specific to JRuby’s implementation • It is the implementation* • Allows Concurrent Ruby Execution • Support cost minimal • Common Gems Supported * We have plans for a formal API

Slide 25

Slide 25 text

oj: Optimized json • Fast json parsing and dumping with many options • Common transitive dependency with its own API • Needed for many popular apps/libraries https://github.com/ohler55/oj

Slide 26

Slide 26 text

oj for JRuby! • 10700 lines of Java (vs 20k lines of C) • Almost ready: 469 runs, 4883 assertions, 12 failures, 1 errors • 5 date/time differences • Streaming IO missing • No optimization work yet

Slide 27

Slide 27 text

Load Performance 0M 0.3M 0.6M 0.9M 1.2M small medium large 0.13 0.29 1.1 0.05 0.11 0.36 0.06 0.11 0.72 MRI (oj) JRuby (json) JRuby (oj) Millions of loads per second (higher is better) https://techblog.thescore.com/2014/05/23/benchmarking-json-generation-in-ruby/

Slide 28

Slide 28 text

Dump Performance 0 0.75 1.5 2.25 3 small medium large 0.44 0.86 2.3 0.22 0.44 1.1 0.33 0.73 2.1 MRI (oj) JRuby (json) JRuby (oj) Million of dumps per second (higher is better) https://techblog.thescore.com/2014/05/23/benchmarking-json-generation-in-ruby/

Slide 29

Slide 29 text

Scripting Java

Slide 30

Slide 30 text

Scripting Java • Access Java libraries using a Ruby syntax • Or Scala, or Clojure, or Kotlin... • Alternatives to existing Ruby libraries • APIs which may not exist in Ruby • More options!

Slide 31

Slide 31 text

JRuby IRB

Slide 32

Slide 32 text

Brakeman Pro https://brakemanpro.com

Slide 33

Slide 33 text

Fun Stuff event(:player_egg_throw) do |e| e.hatching = true e.num_hatches = 120 e.player.mesg "hatched" end Purugin https://github.com/enebo/Purugin

Slide 34

Slide 34 text

JRuby Performance

Slide 35

Slide 35 text

Startup Time • Runtime optimizations give us excellent performance • ...eventually! • Startup time, warmup time are impacted • We continue working to reduce this impact • We compare some common commands

Slide 36

Slide 36 text

JRuby Architecture Ruby (.rb) JIT Java Instructions (java bytecode) Ruby Instructions (IR) parse interpret interpreter interpret C1 compile native code better native code java bytecode interpreter execute C2 compile Java Virtual Machine JRuby Internals deoptimize Performance improves the longer your app runs

Slide 37

Slide 37 text

0s 1.5s 3s 4.5s 6s gem list 5.025s 0.678s CRuby JRuby --dev

Slide 38

Slide 38 text

JRuby Flag: --dev • export JRUBY_OPTS="--dev" • Disables JRuby's JIT • Reduces JVM JIT • Don't use when benchmarking! • 30-40% reduction • More improvements coming total execution time (lower is better) 0s 1.5s 3s 4.5s 6s gem list (200 gems) 3.0s 5.0s JRuby JRuby --dev

Slide 39

Slide 39 text

0s 1.75s 3.5s 5.25s 7s rails console 6.7s 4.96s CRuby JRuby --dev

Slide 40

Slide 40 text

OpenJDK Class Data Sharing • Pre-validate and cache class data • Combined with --dev gives best current JRuby startup • http://blog.headius.com/2019/09/jruby-startup-time-exploration.html

Slide 41

Slide 41 text

Java 13 total execution time (lower is better) 0s 1.5s 3s 4.5s 6s gem list (200 gems) 2.84 3.358 5.589s 2.962s 5.025s JRuby JRuby --dev JRuby --dev --dev + CDS

Slide 42

Slide 42 text

App Performance • Sinatra and Roda • https://github.com/CaDs/ruby_benchmarks • Comparing CRuby, JRuby, and TruffleRuby • RedMine bug tracker and wiki • https://www.redmine.org/ • Real-world Rails application on JRuby, CRuby

Slide 43

Slide 43 text

Peak Performance • After initial startup, warmup, data caching • JRuby generally gives better peak performance • Be aware of warmup time

Slide 44

Slide 44 text

Sinatra and Roda requests/second (higher is better) 0 12500 25000 37500 50000 Sinatra Roda 5,846 4,257 44,703 42,468 14,489 12,284 CRuby JRuby TruffleRuby

Slide 45

Slide 45 text

Redmine Issue View, rendered and json (higher is better) 0 req/s 35 req/s 70 req/s 105 req/s 140 req/s issues/13 issues/13.json 137 req/s 50 req/s 91 req/s 41 req/s CRuby 2.6.2 JRuby 9.2.9

Slide 46

Slide 46 text

Warmup Time • Large applications take longer to warm up • Working on new JIT metrics, profiling to reduce this curve • Ahead-of-time compile to JVM bytecode should help

Slide 47

Slide 47 text

Sinatra performance over time, requests/second 0rps 12500rps 25000rps 37500rps 50000rps 5s 10s 15s 20s 25s CRuby JRuby

Slide 48

Slide 48 text

Roda performance over time, requests/second 0rps 12500rps 25000rps 37500rps 50000rps 5s 10s 15s 20s 25s CRuby JRuby

Slide 49

Slide 49 text

Redmine issues/13.json warmup, one-minute cycles 0 35 70 105 140 1st 2nd 3rd 4th 5th 6th CRuby 2.6.2 JRuby 9.2.9

Slide 50

Slide 50 text

Redmine memory usage (lower is better) 0MB 375MB 750MB 1125MB 1500MB Memory usage for 8 workers vs 8 threads 800MB 1,430MB 1,100MB CRuby JRuby (default) JRuby (300MB heap)

Slide 51

Slide 51 text

Monitoring and Profiling

Slide 52

Slide 52 text

JVM Tooling • Visual VM: monitor and profile threads, GC, managed heap • Mission Control: visualize Flight Recorder output • Flight Recorder: low-overhead system profiling in OpenJDK • async-profiler: command line profiles, flame graphs of CPU, alloc • All JVM tools work to monitor and profile JRuby apps

Slide 53

Slide 53 text

VisualVM • Standalone graphical console for monitoring, profiling • Open sourced as part of OpenJDK project • https://visualvm.github.io/

Slide 54

Slide 54 text

Visual VM and Visual GC

Slide 55

Slide 55 text

JDK Flight Recorder • JVM flag: -XX:+FlightRecorder • JRuby flag: -J-XX:+FlightRecorder or put in JAVA_OPTS • Add --flight-recorder to JRuby launcher? • No overhead or profiling until you start recording • And usually less than 1% overhead for that

Slide 56

Slide 56 text

JDK Mission Control • Control center and visualizer for Flight Recorder data • Start and manage recordings • Browse recorded data • https://adoptopenjdk.net/jmc.html

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

async-profiler • JVM extension for lightweight sampled profiles • Install jruby-async-profiler gem • jruby -J-agentpath:/lib/libasyncProfiler.so • Simplified command lines coming soon • See https://github.com/jvm-profiling-tools/async-profiler

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

Final Words

Slide 62

Slide 62 text

During Development • JRUBY_OPTS=--dev • BE WARY of .ruby-version • Don’t share gem paths between Ruby implementations • Java and C extensions clobber each other

Slide 63

Slide 63 text

Java 9+ Module System • New restrictions on access to core Java classes • You'll see warnings about JRuby or Ruby accesses • Config file for JVM flags: .jruby.java_opts • All your JVM options into a single file • Current dir, home dir, JRuby bin/ dir

Slide 64

Slide 64 text

Java Modules and .jruby.java_opts WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.bouncycastle.jcajce.provider.drbg.DRBG (file:/Users/headius/.m2/repository/org/ bouncycastle/bcprov-jdk15on/1.61/bcprov-jdk15on-1.61.jar) to constructor sun.security.provider.Sun() WARNING: Please consider reporting this to the maintainers of org.bouncycastle.jcajce.provider.drbg.DRBG WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release --add-opens java.base/java.io=org.jruby.dist --add-opens java.base/java.nio.channels=org.jruby.dist --add-opens java.base/sun.nio.ch=org.jruby.dist --add-opens java.management/sun.management=org.jruby.dist

Slide 65

Slide 65 text

Getting Help • JRuby on GitHub: https://github.com/jruby/jruby • Issues, Wiki • Chat with JRuby devs, users • jruby on Matrix • #jruby on Freenode IRC • Mailing list: https://lists.ruby-lang.org

Slide 66

Slide 66 text

JRuby Workshop • Free-form "getting started" plus hackfest • Get JRuby installed and try it out • Test out your libraries • Try to get your apps running

Slide 67

Slide 67 text

Thank You! • @headius, @tom_enebo • https://www.jruby.org • https://github.com/jruby/jruby • Chat with JRuby devs, users • #jruby on Freenode IRC • jruby on Matrix • Mailing list: https://lists.ruby-lang.org