Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby on the Modern JVM with JRuby

headius
April 11, 2024

Ruby on the Modern JVM with JRuby

JRuby has been the go-to option for high performance, scalable Ruby applications for over a decade, bringing world-class garbage collection, jit compilation, and concurrency to Ruby and Rails developers. With the release of Java 22, we now have true native fibers, fast native function and memory support, and fast startup and warm up are on the horizon. This talk will show you how to get started with JRuby and why it's so exciting for the Ruby community.

headius

April 11, 2024
Tweet

More Decks by headius

Other Decks in Programming

Transcript

  1. Who Am I • Charles Oliver Nutter • @headius(@mastodon.social) •

    [email protected] • JRuby developer since 2004 • Full-time JRuby and JVM language advocate since 2006
  2. What is JRuby? • Ruby on the Java Virtual Machine

    (JVM) • Ruby implementation fi rst, JVM language second • Many bene fi ts from JVM ecosystem • Ruby code should "just work" • Different extension API, no forking, parallel threads • Thousands of production users, 17+ years of real-world use
  3. What Is Important? • Usability: compatibility, startup time, warmup time

    • Runtime: GC, JIT, monitoring, pro fi ling, concurrency • Platform: mobile, server, desktop, integration, deployment • Performance: straight line, scaling, parallelism, resource usage • Approachable: easy to contribute with any level of experience • Different applications needs different things
  4. JRuby Install • Install a JDK • Latest Java recommended

    (Java 21 is LTS) • Java 8 supported for 9.4 and lower • Install JRuby • Recommended: Ruby installer, system package, Docker image • Download tarball/zip or Windows installer
  5. Test it out! [] ~ $ rvm use jruby Using

    /Users/headius/.rvm/gems/jruby-9.4.5.0 [] ~ $ irb >> runtime = java.lang.Runtime.runtime => #<Java::JavaLang::Runtime:0x64a896b0> >> runtime.available_processors => 8 >> runtime.free_memory => 91420584 >>
  6. Ruby Compatibility • JRuby 9.4 is Ruby 3.1 compatible •

    98% of language specs passing • Nearly complete core + stdlib features from 2.7, 3.0 and 3.1 • JRuby 9.3 (Ruby 2.6 compat) maintaned through 2023 • Compatibility before performance!
  7. JVM Language • JRuby defers many hard problems to the

    JVM platform • Native threading and VM internals • Memory management and garbage collection • Native JIT and optimizations • JRuby is a little VM on top of a big VM
  8. JRuby Compiler Pipeline 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
  9. JVM JIT • HotSpot JIT is most widely deployed •

    Multi-tier optimizations, fast and stable • Battle-tested for 20 years, dozens of contributors today • Graal JIT: newer, more aggressive optimizations • OpenJ9 JIT (IBM JDK): of fl ine AOT, JIT servers, checkpointing
  10. Indy on Java 8, 11, 17 Times faster than JRuby

    Java 8 no indy 0 1.25 2.5 3.75 5 Mandelbrot Red/Black 4.05x 3.92x 3.74x 3.68x 3.72x 1.97x Java 8 indy Java 11 indy Java 17 indy 3.28x CRuby JIT 1.86x CRuby JIT
  11. Indy + Graal JIT Times faster than JRuby Java 8

    no indy 0 4 8 12 16 Mandelbrot Red/Black 3.13x 15.7x 4.05x 3.92x 3.74x 3.68x 3.72x 1.97x Java 8 indy Java 11 indy Java 17 indy Graal CE indy Escape analysis But not always better
  12. JVM GC • Many options to tune JVM GCs •

    Heap size: small or large? • Throughput: faster allocations or shorter pause times? • Working set: large in-memory or mostly new objects? • Many options in standard OpenJDK • Serial, Parallel, G1, ZGC, Shenandoah, MMtk
  13. Core and Standard Library • Core class like String and

    Array mostly "native" Java • Small pieces moving to Ruby over time • Standard library largely shared with CRuby • Pure-Ruby libraries are the same (rss, rake, rubygems...) • Extension libraries have JRuby versions (digest, psych, json...) • Many thanks to @hsbt and others for working with us!
  14. Key Parts of JRuby Ruby parser Compiler and IR Core

    classes Standard library Java Native/C Ruby
  15. Key Parts of CRuby Ruby parser Compiler and IR Core

    classes Standard library Native/C Ruby Rust
  16. Monitoring and Pro fi ling • VisualVM: GUI console for

    basic JVM monitoring • JDK Flight Recorder: always-on monitoring with pro fi ling options • Low-overhead 1% to 5%, built into OpenJDK • JDK Mission Control: GUI client for Flight Recorder data • https://adoptium.net/jmc/
  17. Many Ways to Contribute • Wide array of languages (Java,

    Ruby, Kotlin, Scala, Clojure, C...) • Straightforward codebase (RubyString, RubyArray, etc) • Many levels of complexity (simple methods up to compiler optz) • Non-coding options (run tests, report issues, try things out) • We need your help!
  18. Scaling Applications • Classic problem on CRuby/MRI • No concurrent

    threads, so we need worker processes • Processes duplicate runtime state and waste resources • JRuby is an excellent solution • Multi-threaded single process runs your entire site • World-class GC uses resources better
  19. Baseline Rails App • Scaffolded "blog" application on PostgreSQL, Puma

    • IBM VPC instance: 8 vCPU, 32GB • CRuby 3.2, 16 workers • JRuby 9.4: 16 threads • Database, siege benchmark driver on same instance
  20. Requests per second 0 450 900 1350 1800 60s siege

    iteration 1 2 3 4 5 6 7 JRuby CRuby 3.2 CRuby 3.2 + YJIT
  21. requests per second (higher is better) 0rps 450rps 900rps 1350rps

    1800rps 1,550rps 1,280rps 1,705rps JRuby 9.4 CRuby 3.2 CRuby + YJIT
  22. Memory • JRuby: 3.4GB RSS • JRuby with 300MB heap:

    955MB RSS • JRuby G1: 1.6G • CRuby: 16x 103MB = 1.6GB • CRuby YJIT: 16x 125MB = 2GB
  23. requests per second (higher is better) 0rps 450rps 900rps 1350rps

    1800rps 1,643rps 1,550rps 1,280rps 1,705rps JRuby 9.4 CRuby 3.2 CRuby + YJIT JRuby 300MB heap
  24. RPS per MB of memory (16-way concurrency) 0rps/mb 0.45rps/mb 0.9rps/mb

    1.35rps/mb 1.8rps/mb 1.72 rps/MB 0.775 rps/MB 0.8 rps/MB 0.501 rps/MB JRuby 9.4 CRuby 3.2 CRuby + YJIT JRuby 300MB heap
  25. RPS per MB of memory (160-way concurrency) 0rps 3.5rps 7rps

    10.5rps 14rps 13.692 rps/MB 0.775 rps/MB 0.8 rps/MB 4.871 rps/MB JRuby 9.4 CRuby 3.2 CRuby + YJIT JRuby 300MB heap $$$$
  26. GUI Libraries • Swing, built into JDK • Clean, cross-platform,

    easy to build simple UIs • Scalable Windowing Toolkit (Eclipse SWT) • Native widgets, WebKit browser component, rich ecosystem • JavaFX (via JRubyFX, github/jruby/jrubyfx) • Scene-based, vector drawing, event-driven modern UI library
  27. Glimmer • Glimmer GUI DSL • Multiple backends (SWT, GTK,

    ...) • JRuby + SWT is the most mature • JRuby makes cross-platform GUI much easier! • Works same everywhere • GUI libraries shipped with gem
  28. JRubyFX • JRuby wrapper for JavaFX • Supports FXML layout

    or direct widget scripting • Deployable on mobile (Android)
  29. 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
  30. Ruboto: JRuby on Android • ruboto.org, https://github.com/ruboto/ruboto • Actively used

    for commercial projects today • Build interface with GUI builder, wire it up with Ruby code • Neglected a bit but being updated for JRuby 9.4 now!
  31. Ruboto IRB • IRB in JRuby on Android! • Plus

    an editor and script runner • Not currently in the store, but we will republish soon! • Search for "Ruboto Core" and "Ruboto IRB" APKs
  32. Updated Ruboto! • We are updating Ruboto! • JRuby 9.4,

    with Ruby 3.1 support • Latest versions of Android • Still a few bugs to work through • https://github.com/ruboto/JRuby9k_POC • jruby_9_4 branch • bundle install ; rake assemble
  33. Fibers • "virtual threads", "threadlets", "coroutines" • Threads depend on

    OS/CPU to switch contexts • Fibers explicitly switch to another call stack on same Thread • Switch automatically at IO or lock boundaries • Many fi bers to one OS thread, so fewer resources needed • See Vishwa and Ishani's concurrency talk!
  34. Thread 2 Fiber 2 Fiber 1 hands o ff to

    Fiber 2, which runs immediately etc Execution fl ow Fiber 1 Wait for IO Accept socket Wait for IO Accept socket Handle request Send response etc Thread 1 Wait for IO Accept socket Thread gets descheduled from CPU but still waiting for IO Handle request Send response
  35. Fibers on JRuby • No native JVM fi bers, so

    we have to simulate it with threads! • Very resource-intensive • No more than a couple thousand fi bers at a time • Slow context-switching, at the mercy of OS/CPU • Ruby world moving toward tens of thousands of fi bers...
  36. 5.times do t = Time.now # create 100k fibers ary

    = 100_000.times.map { Fiber.new { } } # resume and complete 100k fibers ary.each(&:resume) p Time.now - t end
  37. $ jruby fiber_test.rb [7.603s][warning][os,thread] Attempt to protect stack guard pages

    failed (0x00007fc240a00000-0x00007fc240a04000). # # A fatal error has been detected by the Java Runtime Environment: # Native memory allocation (mprotect) failed to protect 16384 bytes for # memory to guard stack pages # # An error report file with more information is saved as: # /home/headius/work/jruby93/hs_err_pid75149.log # # If you would like to submit a bug report, please visit: # https://bugreport.java.com/bugreport/crash.jsp # Aborted (core dumped) 😩
  38. Project Loom • Thread-based fi bers don't scale • Enumerators

    use fi bers • Structured concurrency is coming • Loom brings fi bers to JVM • Easily handles thousands of fi bers • Faster context-switching
  39. C100k on JRuby? • See @ioquatix talks on async and

    falcon! • Just starting to add support for io-event, async-io in JRuby • Fiber scheduler and Buffer APIs available now • Great way to contribute!
  40. Ruby and C • Native libraries go hand-in-hand with Ruby

    • Many extensions written directly to internal APIs • More and more moving to FFI or Fiddle without C code • JRuby needs to support as much as possible • Extensions must be ported to Java (or Ruby) • FFI and Fiddle supported and strongly recommended
  41. User Code JNR stub JNI call JNI impl libf f

    Target Library Java C/native
  42. Project Panama: FFI for JVM • Foreign function interface (FFI)

    • With JVM help to make direct calls • Foreign memory API • JVM-assisted access, lifecycle • API extraction from C/++ headers • Save time setting up bindings
  43. SQLite JDBC Adapter • Java DataBase Connectivity (JDBC) wrapper around

    SQLite • Used by JRuby for ActiveRecord, Sequel • Java Native Interface (JNI) currently, limits throughput • Proof-of concept Panama-based version working now • Early results: 2x performance
  44. Prism Parser • Simple C library for parsing Ruby that

    we can share • JRuby already integrating it! • Nearly all nodes implemented, most code runs today • 20% faster startup • Using JNI/JNR now, will be faster with Panama
  45. JRuby Future • JRuby 9.4 continues to stabilize • Moving

    toward maintenance mode • Lots of new JVM features to leverage • Very exciting time for (J)Rubyists! • Big announcement for the fi rst time today...
  46. JRuby 10! • Major leap forward • Ruby 3.4 support,

    Java 17 (or 21) minimum • New Prism parser with complete language features • Targeted optimization across the board • Our biggest jump since JRuby 9000 (9.0.0.0) • Now is the time to contribute! 𝕏
  47. Help Build JRuby 10 • Implement Ruby 3.2, 3.3, 3.4

    features (even in Ruby!) • Test libraries, applications and report issues • Triage reported bugs, con fi rm or reject or help fi x • We are standing by to work with you! • https://github.com/jruby/jruby
  48. Thank You! • Charles Oliver Nutter • [email protected] • @headius(@mastodon.social)

    • https://github.com/jruby/jruby • https://www.jruby.org • JRuby room on [matrix]