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

JRuby: Looking Forward

JRuby: Looking Forward

Over the past few years, we've update JRuby to support Ruby 3.1 compatibility and new JVM features like native fibers and FFI. Now we're returning to performance work, catching up with the latest optimizations. In this talk we'll explore some popular benchmarks and show how we're improving JRuby across the board. Well also discuss the future of JRuby and how Rubyists can help.

Delivered May 12, 2023 at RubyKaigi in Matsumoto, Japan.


May 12, 2023

More Decks by headius

Other Decks in Programming


  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 atop the Java Virtual Machine

    • 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, 15+ years of real-world use
  3. What Is Important? • Usability: compatibility, startup time, warmup time

    • Runtime features: GC, JIT, monitoring, pro fi ling, concurrency • Platform features: mobile, server, desktop, integration, deployment • Performance: straight line, scaling, parallelism, resource usage • Different applications needs different capabilities
  4. JRuby Install • Install a JDK • Java 11+ recommended,

    there's many distributions out there • 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- [] ~ $ 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 • JRuby 9.5 with 3.2 support before 2024 (help us!) • Compatibility before performance!
  7. Very high language compatibility High core compatibility, missing parts mostly

    POSIX things Most of stdlib works well, MRI-speci fi c extensions excluded Nearly all command line works same https://eregon.me/rubyspec-stats/
  8. Startup Time • JVM is not designed to start up

    quickly • Most of core JDK starts in interpreter • Long tail to optimize code and reach peak performance • JRuby makes it worse • Ruby parser, compiler and interpreter are interpreted by JVM • Lazy compile to bytecode, bytecode is interpreted by JVM
  9. 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
  10. ruby -e 1 0s 0.45s 0.9s 1.35s 1.8s -e 1

    1.686s 0.053s CRuby 3.2 JRuby 9.4
  11. JRuby With Full Optimization Ruby (.rb) JIT Java Instructions (java

    bytecode) Ruby Instructions (IR) parse interpret interpreter interpret better native code java bytecode interpreter C1 compile native code execute C2 compile Java Virtual Machine JRuby Internals
  12. JRuby --dev Mode Ruby (.rb) Ruby Instructions (IR) parse interpret

    interpreter Java Virtual Machine JRuby Internals C1 compile interpreter as native code execute
  13. ruby -e 1 0s 0.45s 0.9s 1.35s 1.8s -e 1

    1.271s 1.686s 0.053s CRuby 3.2 JRuby 9.4 JRuby 9.4 --dev
  14. rails new testapp --skip-bundle 0s 1.5s 3s 4.5s 6s rails

    new testapp --skip-bundle 2.7s 5.918s 0.314s CRuby JRuby JRuby --dev
  15. Ahead-of-time Compilation • Maybe we can start with native code?

    • GraalVM Native Image is a well known option • Disables most dynamic JVM features • Project Leyden: standardized AOT for Hotspot JVM • Start native, but support and optimize dynamic stuff later!
  16. ruby -e 1 0s 0.75s 1.5s 2.25s 3s -e 1

    0.034s 2.308s 1.271s 1.686s 0.053s CRuby 3.2 JRuby 9.4 JRuby 9.4 --dev Tru ffl eRuby --jvm Tru ffl eRuby native
  17. rails new testapp --skip-bundle 0s 4s 8s 12s 16s rails

    new testapp --skip-bundle 3.218s 15.676s 2.7s 5.918s 0.314s CRuby JRuby JRuby --dev Tru ffl eRuby --jvm Tru ffl eRuby native
  18. Snapshot a "Warm" JRuby? • Pre-optimize and save a snapshot?

    • CRIU: Checkpoint and Restore in Userspace (Linux only) • IBM Semeru JDK "InstantOn" • Working prototype with JRuby! • CRaC: OpenJDK support for checkpoint and restore • Cross-platform and standardized (coming soon?)
  19. ruby -e 1 0s 0.45s 0.9s 1.35s 1.8s -e 1

    0.21s 1.271s 1.686s 0.053s CRuby 3.2 JRuby 9.4 JRuby 9.4 --dev JRuby 9.4 CRIU
  20. rails new testapp --skip-bundle 0s 1.5s 3s 4.5s 6s rails

    new testapp --skip-bundle 0.89s 2.7s 5.918s 0.314s CRuby JRuby JRuby --dev JRuby CRIU
  21. 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
  22. JVM JIT • HotSpot JIT is most widely deployed •

    C1 "client" JIT: fast, simple optimizations • C2 "server" JIT: needs pro fi le data, heavy optimization • Both enabled with various "tiers" of JIT • Graal JIT: newer, more aggressive optimizations • OpenJ9 JIT (IBM JDK): of fl ine AOT, JIT servers, CRIU
  23. 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/
  24. Project Loom • JRuby's fi bers are based on threads

    • Too many, JVM blows up! • Scheduling, resource-intensive • Loom brings fi bers to JVM • Easily handles thousands of fi bers • Faster context-switching
  25. 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
  26. $ 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) 😩
  27. C100k on JRuby? • See @ioquatix talks on async and

    falcon! • Just starting to add support for io-event, async-io in JRuby • To do: Fiber/IO scheduler and Buffer APIs • Great way to contribute!
  28. 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
  29. jextract code generator • Writing bindings using FFI or Fiddle

    is still challenging • Parameter sizes, struct layout, in and out values, pointers • Differences across platforms • jextract: produce Panama/FFI stub code from a C header fi le • Call that stub code from JRuby... easy FFI for all! //point.h struct Point2d { double x; double y; }; double distance(struct Point2d); jextract --source -t org.jextract point.h import java.lang.foreign.*; import static org.jextract.point_h.*; import org.jextract.Point2d; var session = MemorySession.openCon fi ned(); MemorySegment point = MemorySegment.allocateNative(Point2d.$LAYOUT(), session); Point2d.x$set(point, 3d); Point2d.y$set(point, 4d); distance(point);
  30. 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 • 2x performance for most operations
  31. Yet Another Ruby Parser (YARP) • Simple C library for

    parsing Ruby that we can share • JRuby already integrating it! • 60% of nodes implemented, 
 `gem list` almost runs! • 20% faster startup for "-e 1" • Using JNI, but could be even faster with Panama
  32. 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
  33. 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
  34. JRubyFX • JRuby wrapper for JavaFX • Supports FXML layout

    or direct widget scripting • Could be combined with Ruboto for FX on Android apps!
  35. 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!
  36. 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
  37. 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
  38. Scaling Applications • Classic problem on CRuby/MRI • No concurrent

    threads, so we need worker processes • Processes duplicate runtime state and waste resources • JRuby is a good solution • Multi-threaded single process runs your entire site • Single process with leading-edge GC uses resources better
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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 ¥¥¥¥
  46. JRuby Future • JRuby 9.4 continues to stabilize • Big

    optimization work coming the rest of this year • JRuby 9.5: Java 17, Ruby 3.2, many optimizations • Lots of new JVM features to leverage • Very exciting time for Rubyists! • Try JRuby today and let us know how it goes!
  47. Thank You! • Charles Oliver Nutter • [email protected] • @headius(@mastodon.social)

    • https://github.com/jruby/jruby • https://www.jruby.org