Save 37% off PRO during our Black Friday Sale! »

Scalable Apps with JRuby

F1d37642fdaa1662ff46e4c65731e9ab?s=47 headius
September 06, 2019

Scalable Apps with JRuby

F1d37642fdaa1662ff46e4c65731e9ab?s=128

headius

September 06, 2019
Tweet

Transcript

  1. Scalable Apps with JRuby Charles Oliver Nutter (@headius)

  2. สวัสดี! • Charles Oliver Nutter • headius@headius.com, @headius • JVM

    language advocate at Red Hat • 24 years of JVM, 13 years of JRuby • My first time in Thailand!
  3. None
  4. None
  5. What is JRuby • It's just Ruby! • Ruby 2.5

    compatible, if something's broken tell us • Supports pure-Ruby gems, standard library, many extensions • We want to be a Ruby first! • It's a JVM language • Full access to the power of the JVM platform!
  6. None
  7. None
  8. JVM Benefits • Widely deployed and widely supported runtime •

    Excellent JIT, GC, concurrency, and platform support • Wide array of libraries • Rich tools for monitoring, profiling, debugging • WORA: JRuby feels the same on Windows as on Linux
  9. Parallel and Concurrent

  10. JVM Tools and GC

  11. Thousands of Libraries Released artifacts: 7,000,212 Unique artifacts: 347,981 Released

    gem files: 1,042,770 Unique artifacts: 154,626 Maven Central RubyGems.org
  12. Fun Stuff event(:player_egg_throw) do |e| e.hatching = true e.num_hatches =

    120 e.player.mesg "hatched" end Purugin
  13. JRuby is Ruby plus the best parts of the JVM

  14. Roadmap • 9.2.8.0 (Ruby 2.5 compatible) is current version •

    9.1.x is EOL • Support 2.6 or 2.7? 9.1.17.0 ... 9.2.0.0 2.5.3+ 2.3.x 2.6? 9.2.7.0 master jruby-9_1 9.1.1.18.0 EOL ruby-2.6
  15. Skip 2.6 Support? • JRuby 9.3 -> Ruby 2.7? •

    How important is 2.6? • We skipped 2.4 to no ill effects • Less maintenance for us • 2.6 Checklist: https://github.com/jruby/jruby/issues/5576
  16. You Can Help! • New features are great opportunities to

    contribute! • Learn more about how Ruby and JRuby work • Help us keep up with Ruby development • Use Ruby or Java, we'll accept both • We are always standing by on IRC, Gitter, Twitter to help you
  17. Getting Started

  18. None
  19. JRuby Install • Install a JDK: https://adoptopenjdk.net/ • Java 8

    recommended, there's many distributions out there • Java 9+ work well but may print warnings • Install JRuby • Recommended: Ruby installer, Docker image, system package • Download tarball/zip or use Windows installer
  20. Install JRuby

  21. That's it!

  22. JRuby IRB

  23. Gems, Paths, etc • Don't share gem path with other

    Rubies • C extension gems have JRuby versions (e.g. Nokogiri) • Ruby installers will handle this for you • System packages may not • Watch out for .ruby-version silently switching
  24. Getting Help • JRuby on GitHub: https://github.com/jruby/jruby • Chat with

    JRuby devs, users • #jruby on Freenode IRC • jruby/jruby on Gitter • jruby on Matrix • Mailing list: https://lists.ruby-lang.org
  25. 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
  26. Low-Level Performance

  27. Microbenchmarks - Usually not useful to users • Not related

    to typical applications (e.g. Rails) • JRuby has won microbenchmarks for years + Fun to show off and improve + Easier to isolate specific measurements + Quick way to explore new runtimes and tech
  28. bench_mandelbrot • Generate a Mandelbrot fractal image • Useful? Hmmm

    • Good test of numeric algorithm performance • Heavily relies on JVM to optimize
  29. bench_mandelbrot.rb def mandelbrot(size) sum = 0 byte_acc = 0 bit_num

    = 0 y = 0 while y < size ci = (2.0*y/size)-1.0 x = 0 while x < size zrzr = zr = 0.0 zizi = zi = 0.0 cr = (2.0*x/size)-1.5 escape = 0b1 z = 0 while z < 50
  30. bench_mandelbrot total execution time (lower is better) 0s 1s 2s

    3s 4s CRuby 2.5 CRuby 2.6 JIT JRuby JRuby Indy 1.33s 2.95s 3.5s 3.57s
  31. InvokeDynamic • JVM support for dynamic calls, variables, etc •

    Java 7 feature after input and testing from JRuby • Steadily improving performance, reducing overhead • Opt-in due to startup impact: -Xcompile.invokedynamic • May be default very soon!
  32. bench_mandelbrot total execution time (lower is better) 0s 1s 2s

    3s 4s CRuby 2.6 JIT JRuby JRuby Indy 1.33s 2.95s 3.5s
  33. New JVMs and JITs • IBM's OpenJ9 • Recently open-sourced

    • Many compelling features • Graal: JIT written in Java • Faster evolution • More advanced optimization
  34. bench_mandelbrot total execution time (lower is better) 0s 1s 2s

    3s 4s CRuby 2.6 JIT JRuby JRuby Indy 1.33s 2.95s 3.5s
  35. bench_mandelbrot total execution time (lower is better) 0s 1s 2s

    3s 4s CRuby 2.6 JIT JRuby Indy JRuby Indy Graal 0.139s 1.33s 3.5s
  36. Optimizing Objects and Arrays • Ruby instance vars are dynamic,

    but usually predictable • Ruby arrays are mutable, but usually small and immutable • We can make compact JVM objects • Instance vars, array elements as Java fields • Reduce memory use and allocation • Similar to CRuby storing references in object header
  37. Objects in Rails `select` Bench percent live alloc'ed class rank

    self accum bytes objs bytes objs name 23 0.82% 73.58% 1744576 18168 5894464 61396 org.jruby.gen.RubyObject17 32 0.44% 78.33% 937784 23432 2071464 51774 org.jruby.gen.RubyObject2 42 0.30% 81.96% 633312 19775 1525824 47666 org.jruby.gen.RubyObject0 43 0.30% 82.26% 632168 11280 2783968 49705 org.jruby.gen.RubyObject6 46 0.27% 83.10% 587072 18330 2133984 66671 org.jruby.gen.RubyObject1 58 0.22% 86.08% 465056 3630 1672864 13066 org.jruby.gen.RubyObject25 60 0.21% 86.51% 439304 10970 1493024 37313 org.jruby.gen.RubyObject3 61 0.20% 86.71% 434608 9044 2311744 48151 org.jruby.gen.RubyObject5 68 0.16% 87.93% 349936 7280 1305136 27180 org.jruby.gen.RubyObject4 79 0.11% 89.34% 233824 3646 838432 13093 org.jruby.gen.RubyObject8 238 0.01% 96.11% 28088 314 30816 345 org.jruby.gen.RubyObject14
  38. 10M * One-variable Object 0MB 200MB 400MB 600MB 800MB Not

    Optimized Optimized 320 480 400 Ruby Object Object[] 33% memory reduction
  39. Arrays in Rails `select` Bench percent live alloc'ed class rank

    self accum bytes objs bytes objs name 5 4.90% 33.79% 10481824 218361 38183968 795489 org.jruby.RubyArray 11 3.11% 56.32% 6661072 138762 22817680 475358 org.jruby.specialized.RubyArrayOneObject 17 1.46% 67.96% 3124112 55779 15838128 282815 org.jruby.specialized.RubyArrayTwoObject More than half of allocated arrays are 1- or 2-element!
  40. 10M * One-element Array 0MB 250MB 500MB 750MB 1000MB Not

    Optimized Optimized 400 650 570 Ruby Object IRubyObject[] 33% memory reduction
  41. JVM is Great, But... • Runtime optimizations give us excellent

    performance...eventually • Startup time, warmup time are impacted • We continue to reduce this impact
  42. total execution time (lower is better) 0s 1.25s 2.5s 3.75s

    5s gem --version gem list (~350 gems) 4.6s 3.6s 0.7s 0.4s CRuby JRuby (JDK8)
  43. Why? • CRuby: Mostly native code at startup • Parser,

    compiler, interpreter, core classes, extensions • JRuby: 100% interpreted bytecode at startup • Everything in JRuby starts "cold" • JVM eventually optimizes...but it's too late for startup time
  44. Running in Same JVM total execution time (lower is better)

    0s 1.25s 2.5s 3.75s 5s gem list (~350 gems) 1.3s 1.6s 1.6s 1.6s 2.2s 1.7s 1.7s 2.0s 2.2s 3.5s 4.6s
  45. 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.15s 2.3s 3.45s 4.6s gem list (~350 gems) 3.0s 4.6s JRuby JRuby --dev
  46. Ahead-of-time Compile? • Precompile JVM code to native • Improves

    base startup time • Used by TruffleRuby currently • Coming soon: precompiled JRuby and Ruby code • Instant application startup?
  47. total execution time (lower is better) 0.4s 4.3s 8.2s 12.1s

    16s -e 1 0.5s 1.7s JRuby --dev TruffleRuby native total execution time (lower is better) 0s 4s 8s 12s 16s gem list (~350 gems) 14.9s 4.6s JRuby --dev TruffleRuby native
  48. Web Applications

  49. Small and Large • Sinatra and Roda • https://github.com/CaDs/ruby_benchmarks •

    Comparing JRuby, CRuby, and TruffleRuby • Rails • https://github.com/rubygems/rubygems.org • JRuby versus CRuby
  50. Sinatra and Roda • Well-supported on JRuby • Many production

    users at very large scales • Very simple example with no database • Benchmarking request routing mostly • Good indicator of small service performance
  51. Sinatra and Roda requests/second (higher is better) 0 12500 25000

    37500 50000 Sinatra Roda Roda (1 thread) Roda (WEBrick) 6,414 4,307 5,214 4,257 21,209 15,419 44,703 42,468 14,489 12,284 CRuby JRuby TruffleRuby
  52. Sinatra performance over time, requests/second 0rps 12500rps 25000rps 37500rps 50000rps

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

    5s 10s 15s 20s 25s CRuby JRuby
  54. JRuby on Rails • Benefit from the JVM, libraries, languages

    • Single JRuby process runs your whole site • "Are there JRuby users running Rails applications?" • Oh yes! And at scale! • The best way to scale large Rails apps today
  55. --- testapp_mri_pg/Gemfile +++ testapp_jruby_pg/Gemfile -# Use postgresql as the database

    for Active Record -gem 'pg', '>= 0.18', '< 2.0' +# Use jdbcpostgresql as the database for Active Record +gem 'activerecord-jdbcpostgresql-adapter' # See https://github.com/rails/execjs#readme for more supported runtimes -# gem 'mini_racer', platforms: :ruby - +gem 'therubyrhino' group :development do - # Spring speeds up development... - gem 'spring' - gem 'spring-watcher-listen', '~> 2.0.0' end Minimal Config Diffs --- testapp_mri_pg/config/database.yml +++ testapp_jruby_pg/config/database.yml @@ -65,5 +81,7 @@ production: <<: *default database: rails_prod + host: localhost + port: 5432 username: rails_prod password: rails_prod --- testapp_mri_pg/config/puma.rb 2019-04-19 04:48:51.425474315 +0000 +++ testapp_jruby_pg/config/puma.rb 2019-04-17 08:56:53.529154189 +0000 @@ -4,7 +4,7 @@ # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 2 } +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 20 } threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. @@ -21,7 +21,7 @@ # Workers do not work on JRuby or Windows (both of which do not support # processes). # -workers ENV.fetch("WEB_CONCURRENCY") { 2 } +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code
  56. Rails 6 actioncable: 203 runs, 921 assertions, 0 failures, 10

    errors actionmailbox: 79 runs, 205 assertions, 0 failures, 2 errors actionmailer: 220 runs, 509 assertions, 0 failures, 0 errors actionpack: 3255 runs, 16027 assertions, 1 failures, 0 errors actiontext: 53 runs, 94 assertions, 5 failures, 0 errors actionview: 2068 runs, 4667 assertions, 2 failures, 3 errors activejob: 301 runs, 659 assertions, 0 failures, 0 errors activemodel: 844 runs, 2350 assertions, 0 failures, 0 errors activestorage:225 runs, 696 assertions, 0 failures, 0 errors activesupport: 4362 runs, 13909 assertions, 15 failures, 1 errors 99.902% passing!
  57. Failure: TimeWithZoneTest#test_minus_with_time_precision [activesupport/ test/core_ext/time_with_zone_test.rb:340]: Expected: 86399.999999998 Actual: 86399.99999999799

  58. Rails is the Thing! • If you can't run Rails

    fast, you've got more work to do • By far the biggest use case for Ruby • Frustratingly difficult to optimize • JRuby has run microbenchmarks faster for years... • ...but Rails performance was always about the same • Times are changing!
  59. ActiveRecord Performance • Rails apps live and die by ActiveRecord

    • Largest CPU consumer by far • Heavy object churn, GC overhead • Create, read, and update measurements • CRuby 2.5.1 vs JRuby 9.2 on JDK11
  60. ActiveRecord Selects time for 1000 selects, lower is better 0

    0.075 0.15 0.225 0.3 CRuby 2.5 JRuby C2 JRuby Graal binary boolean date datetime decimal float integer string text time timestamp *
  61. Scaling Rails • Classic problem on MRI • No concurrent

    threads, so we need processes • Processes duplicate runtime state and waste resources • JRuby is the answer! • Multi-threaded single process runs your entire site
  62. Simple Rails Performance • Rails 5.1.6, Postgresql 10, scaffolded view

    • JRuby: 10 threads, CRuby: 10 processes • 4k requests to warm up, then measure every 10k • EC2 c4.xlarge: 4 vCPUs, 7.5GB • Bench, database, and app on same instance
  63. Requests per second, full stack scaffolded read on Postgresql 0

    325 650 975 1300 CRuby JRuby 1,253.86 910.02
  64. Requests per second 0 325 650 975 1300 Requests over

    time 10k 20k 30k 40k 50k 60k 70k 80k 90k 100k CRuby 2.5 CRuby 2.6 JIT JRuby 9.2
  65. JRuby on Rails Memory • Single instance is roughly 10x

    larger • Threading to the rescue! • Ten CRuby processes = 500MB • Ten JRuby threads = 400-500MB • Copy-on-write helps CRuby a bit • Eventually most processes grow Instances vs Memory 0MB 1250MB 2500MB 3750MB 5000MB 1 5 10 100 CRuby JRuby
  66. rubygems.org Performance • Laptop (i7 with 16Gb of memory) (localhost)

    • No SSL • Puma instead of Unicorn • Single machine: • rails, postgresql, elasticsearch, toxiproxy, redis, memcached • Benchmark driver: Apache Bench (ab)
  67. Performance versus concurrent users, requests/second Peak Requests/second 0 100 200

    300 400 Concurrent threads 1 2 4 6 8 10 12 14 16 18 20 JRuby puma single 2.6.2 puma clustered 2.6.2 puma single 3 16
  68. Memory Usage Memory (RES) 0 1000 2000 3000 4000 #

    of test runs 1 Run (35 mins) 6 Runs 11 Runs 16 Runs 21 Runs 1,536 1,536 1,536 1,434 1,228 3,620 3,540 3,460 3,340 3,060 CRuby (20 workers) JRuby (20 threads, 384m heap) +8.4% +14.4% +4.5% +6.7% +0% 2.5x smaller +0% +3.3% +3.2%
  69. Scaling Takeaways + Uses less memory (threads vs processes) +

    More memory stable over time + More CPU efficient – More warmup time
  70. JRuby is the fastest way to run Rails applications.

  71. Migrating to JRuby

  72. Try JRuby! • New apps will be easiest, of course

    • Existing apps require a few extra steps • Tweak database, server configs • Bundle install and deal with C extensions • Run your tests
  73. Use-Case: Discourse • “A platform for community discussion” • Very

    large, well-known Rails application • >500 gems • 250,000 lines of Ruby • JRuby is not currently supported • But it is almost working!
  74. Step 1: jruby-lint

  75. jruby-lint gem See how ready your Ruby code is to

    run on JRuby $ gem install jruby-lint $ cd my-app $ jrlint
  76. [] ~/projects/discourse $ jrlint JRuby-Lint version 0.9.0 ./Gemfile:: [gems, info]

    For more on gem compatibility see http://wiki.jruby.org/C-Extension-Alternatives ./Gemfile:80: [gems, warning] Found gem 'oj' which is reported to have some issues: Try `gson`, `json` or `json_pure` instead.| gem 'oj' ./Gemfile:81: [gems, warning] Found gem 'pg' which is reported to have some issues: Use activerecord-jdbcpostgresql-adapter instead or pg_jruby (drop-in replacement).| gem 'pg' ./Gemfile:187: [gems, warning] Found gem 'mysql2' which is reported to have some issues: Use activerecord-jdbcmysql-adapter.| gem 'mysql2' ./Gemfile:188: [gems, warning] Found gem 'redcarpet' which is reported to have some issues: Same as with **RDiscount** use alternatives such as kramdown, Maruku or markdown_j| gem 'redcarpet' ./Gemfile:189: [gems, warning] Found gem 'sqlite3' which is reported to have some issues: Use activerecord-jdbcsqlite3-adapter.| gem 'sqlite3', '~> 1.3.13' ./app/mailers/user_notifications.rb:152: [nonatomic, warning] Non-local operator assignment (@popular_topics) is not guaranteed to be atomic. @popular_topics = topics_for_digest[0, SiteSetting.digest_topics] ./app/mailers/user_notifications.rb:630: [nonatomic, warning] Non-local operator assignment (@site_name) is not guaranteed to be atomic. @site_name = SiteSetting.email_prefix.presence || SiteSetting.title # used by I18n ./app/models/report.rb:21: [nonatomic, warning] Non-local operator assignment (@start_date) is not guaranteed to be atomic. @start_date ||= Report.default_days.days.ago.utc.beginning_of_day ./app/models/report.rb:22: [nonatomic, warning] Non-local operator assignment (@end_date) is not guaranteed to be atomic. @end_date ||= Time.now.utc.end_of_day ./app/models/admin_dashboard_next_data.rb:17: [nonatomic, warning] Non-local operator assignment (@json) is not guaranteed to be atomic. @json ||= get_json ./app/models/topic_posters_summary.rb:31: [nonatomic, warning] Non-local operator assignment (@descriptions_by_id) is not guaranteed to be atomic. @descriptions_by_id ||= begin ./app/models/topic_posters_summary.rb:33: [nonatomic, warning] Non-local operator assignment (descriptions[id]) is not guaranteed to be atomic. descriptions[id] ||= [] ./app/models/topic_posters_summary.rb:79: [nonatomic, warning] Non-local operator assignment (@avatar_lookup) is not guaranteed to be atomic. @avatar_lookup ||= options[:avatar_lookup] || AvatarLookup.new(user_ids) ./app/models/topic_posters_summary.rb:83: [nonatomic, warning] Non-local operator assignment (@primary_group_lookup) is not guaranteed to be atomic. @primary_group_lookup ||= options[:primary_group_lookup] || PrimaryGroupLookup.new(user_ids) ./app/models/directory_item.rb:8: [nonatomic, warning] Non-local operator assignment (@headings) is not guaranteed to be atomic. @headings ||= [:likes_received, ./app/models/directory_item.rb:18: [nonatomic, warning] Non-local operator assignment (@types) is not guaranteed to be atomic. @types ||= Enum.new(all: 1, ./app/models/group_history.rb:11: [nonatomic, warning] Non-local operator assignment (@actions) is not guaranteed to be atomic. @actions ||= Enum.new( ./app/models/locale_site_setting.rb:10: [nonatomic, warning] Non-local operator assignment (@values) is not guaranteed to be atomic. @values ||= supported_locales.map do |locale| ./app/models/locale_site_setting.rb:25: [nonatomic, warning] Non-local operator assignment (@language_names) is not guaranteed to be atomic. @language_names ||= begin ./app/models/locale_site_setting.rb:41: [nonatomic, warning] Non-local operator assignment (@supported_locales) is not guaranteed to be atomic. @supported_locales ||= begin ./app/models/category.rb:98: [nonatomic, warning] Non-local operator assignment (TOPIC_CREATION_PERMISSIONS) is not guaranteed to be atomic. TOPIC_CREATION_PERMISSIONS ||= [:full] ./app/models/category.rb:99: [nonatomic, warning] Non-local operator assignment (POST_CREATION_PERMISSIONS) is not guaranteed to be atomic. POST_CREATION_PERMISSIONS ||= [:create_post, :full] ./app/models/category.rb:109: [nonatomic, warning] Non-local operator assignment (@topic_id_cache) is not guaranteed to be atomic. @topic_id_cache = DistributedCache.new('category_topic_ids') ./app/models/category.rb:228: [nonatomic, warning] Non-local operator assignment (@@cache) is not guaranteed to be atomic. @@cache ||= LruRedux::ThreadSafeCache.new(1000) ./app/models/category.rb:520: [nonatomic, warning] Non-local operator assignment (@has_children) is not guaranteed to be atomic. @has_children ||= (id && Category.where(parent_category_id: id).exists?) ? :true : :false
  77. Unsupported Extensions ./Gemfile:: [gems, info] For more on gem compatibility

    see http://wiki.jruby.org/C-Extension-Alternatives ./Gemfile:80: [gems, warning] Found gem 'oj' which is reported to have some issues: Try `gson`, `json` or `json_pure` instead.| gem 'oj' ./Gemfile:81: [gems, warning] Found gem 'pg' which is reported to have some issues: Use activerecord-jdbcpostgresql-adapter instead or pg_jruby (drop-in replacement).| gem 'pg' ./Gemfile:187: [gems, warning] Found gem 'mysql2' which is reported to have some issues: Use activerecord-jdbcmysql-adapter.| gem 'mysql2' ./Gemfile:188: [gems, warning] Found gem 'redcarpet' which is reported to have some issues: Same as with **RDiscount** use alternatives such as kramdown, Maruku or markdown_j| gem 'redcarpet' ./Gemfile:189: [gems, warning] Found gem 'sqlite3' which is reported to have some issues: Use activerecord-jdbcsqlite3-adapter.| gem 'sqlite3', '~> 1.3.13'
  78. None
  79. Threading Concerns ./app/models/report.rb:21: [nonatomic, warning] Non-local operator assignment (@start_date) is

    not guaranteed to be atomic. @start_date ||= Report.default_days.days.ago.utc.beginning_of_day ./app/models/report.rb:22: [nonatomic, warning] Non-local operator assignment (@end_date) is not guaranteed to be atomic. @end_date ||= Time.now.utc.end_of_day ./app/models/admin_dashboard_next_data.rb:17: [nonatomic, warning] Non-local operator assignment (@json) is not guaranteed to be atomic. @json ||= get_json ./app/models/topic_posters_summary.rb:31: [nonatomic, warning] Non-local operator assignment (@descriptions_by_id) is not guaranteed to be atomic. @descriptions_by_id ||= begin ./app/models/topic_posters_summary.rb:33: [nonatomic, warning] Non-local operator assignment (descriptions[id]) is not guaranteed to be atomic. descriptions[id] ||= [] ./app/models/topic_posters_summary.rb:79: [nonatomic, warning] Non-local operator assignment (@avatar_lookup) is not guaranteed to be atomic. @avatar_lookup ||= options[:avatar_lookup] || AvatarLookup.new(user_ids) ./app/models/topic_posters_summary.rb:83: [nonatomic, warning] Non-local operator assignment (@primary_group_lookup) is not guaranteed to be atomic. @primary_group_lookup ||= options[:primary_group_lookup] || PrimaryGroupLookup.new(user_ids) @language_names) is not guaranteed to be atomic. @language_names ||= begin
  80. Unsupported Features ./script/measure.rb:48: [objectspace, warning] Use of ObjectSpace is expensive

    and disabled by default. Use -X+O to enable. ObjectSpace.each_object do |o| ./script/check_forking.rb:14: [fork, error] Kernel#fork is not implemented on JRuby. child = fork do ./script/check_forking.rb:17: [fork, error] Kernel#fork is not implemented on JRuby. grand_child = fork do
  81. Step 2: Replace C Extensions

  82. Why Not Support C Extensions? • Often used for performance

    reasons • The API is enormous and invasive • Direct pointer access to Ruby objects • JRuby 1.6 shipped experimental support, but... • Huge amount of work for partial compatibility • Performance was slower than pure Ruby!
  83. $ bundle install

  84. Options 1.Remove it if not needed 2.Use a pure-Ruby version

    if performance is good enough 3.Call into a native library using FFI (Foreign Function Interface) 4.Use an equivalent JRuby library 5.Write a JRuby extension
  85. oj: Optimized json • Fast json parsing and dumping with

    many options • No support for JRuby currently • Common transitive dependency with its own API • Needed for many popular apps/libraries https://github.com/ohler55/oj
  86. oj for JRuby! • 9200 lines of Java (vs 20k

    lines of C) • Almost ready: 448 runs, 765 assertions, 43 failures, 12 errors • 35 F/E from minor features, date/time diffs • Performs even better than the C extension! • ...and we've done almost no optimization!
  87. 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/
  88. 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/
  89. That's it! • Once your application bundles, it should run!

    • Remaining issues: come talk to us • Many options for deploying, packaging as Java app, etc • Remember those JVM tools!
  90. Wrapping Up

  91. JRuby is here for you!

  92. Get the best of the JVM without leaving Ruby

  93. Use JVM libraries and languages

  94. Scale up your apps

  95. Save money on server resources

  96. Keep the Ruby dream alive!

  97. Thank You! • Charles Oliver Nutter • headius@headius.com • Follow

    me on Twitter: @headius • http://jruby.org