O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

The Year of JRuby - RubyC 2018

A status update on JRuby, covering compatibility, Rails, and next-gen performance numbers. JRuby is currently the fastest way to run Rails apps, and we're doing work to make it even faster in the future.

  • Seja o primeiro a comentar

  • Seja a primeira pessoa a gostar disto

The Year of JRuby - RubyC 2018

  1. 1. 2018: The Year of JRuby Charles Oliver Nutter @headius
  2. 2. Hello! • Charles Oliver Nutter • headius@headius.com, @headius • JVM language advocate at Red Hat • 20-year JVM veteran, 12 years on JRuby • Excited to be back in Kyiv!
  3. 3. The JRuby Guys • Shout out to my buddy Tom Enebo • Just presented at RubyKaigi in Sendai, Japan • We've worked together on JRuby for over 12 years!
  4. 4. Beer For You! • I am a beer lover! • I brought some American craft beers to give away! • Try running something on JRuby, show me, and take your pick: • Bender: oatmeal brown ale • Furious: dark American IPA • Foggy Geezer: New England IPA
  5. 5. What is JRuby • It's just Ruby! • Ruby 2.5 compatible, if something's broken tell us • Supports pure-Ruby gems, many extensions • We want to be a Ruby first! • It's a JVM language • Full access to the power of the JVM platform!
  6. 6. Ruby! • JRuby 9.2 is now Ruby 2.5 compatible • Modulo a few things we couldn't finish • You won't notice unless you use some weird edge features • Primary focus is always on compatibility and experience • Performance comes along later
  7. 7. Compatibility
  8. 8. JRuby 9.2 • Ruby 2.5, of course • Better support for non-ASCII identifiers • Method names, constants, symbols... • Keyword arguments optimizations • Preparing for new optimizations and JVMs
  9. 9. Supporting Ruby Versions • Checklist based off CRuby's NEWS file • Update our copy of CRuby's tests and make them pass • Same process for ruby/spec, our own tests, and key libraries
  10. 10. New Feature? 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! • Profit! • We are always standing by on IRC, Gitter, Twitter to help you
  11. 11. Library Compatibility • We also run library tests • Rails, Rake, RubyGems, ... • Core Ruby tests will never cover 100% of users • Rails in particular is a key target
  12. 12. Performance
  13. 13. JRuby Architecture • Mixed-mode runtime • Interpreter runs for a while, gathering information • Just-in-time (JIT) compiler compiles hot code to JVM • JVM turns that into optimized native code • Heavy dependence on JVM to optimize well • Newer JVM JITs showing great promise!
  14. 14. JRuby JVM Client (C1) Native JIT Server (C2) Native JIT Interpreter Bytecode JIT Bytecode Interpreter CPU
  15. 15. Microbenching • Very fun to show off, see improve • Practically useless • Like judging a person by how much they can bench press • JRuby has won microbenchmarks for years, never faster on Rails • Easier to isolate specific measurements • Great for exploring new runtimes and tech
  16. 16. bench_aref • Testing calls to ary[n] • Reduced overhead in JRuby 9.2 • String#[] needs to store $~ • We have to track that just in case ary is a String • JRuby 9.1 deoptimized more than necessary
  17. 17. require 'benchmark/ips' @a = [1] def foo(a) a[0] end Benchmark.ips do |x| x.time = 10 x.warmup = 15 x.report("Instantiation") { a = @a i = 0; while i < 1_000_000; foo(a); i += 1; end } end
  18. 18. bench_aref iterations per second (higher is better) 0 iter/s 10 iter/s 20 iter/s 30 iter/s 40 iter/s JDK10 JRuby 9.1 JRuby 9.2 39.132 30.248
  19. 19. InvokeDynamic • JVM support for dynamic invocation • Let the JVM see through all the dynamic bits of Ruby • Added in Java 7, with much input and testing from JRuby • Steadily improving performance, reducing overhead • -Xcompile.invokedynamic • May be default soon!
  20. 20. bench_aref iterations per second (higher is better) 0 iter/s 22.5 iter/s 45 iter/s 67.5 iter/s 90 iter/s JDK10 JDK10 with Indy JRuby 9.1 JRuby 9.2 JRuby 9.1 JRuby 9.2 89.169 39.132 68.122 30.248
  21. 21. Graal • New JVM native JIT written in Java • Faster evolution • More advanced optimization • Plugs into JDK9+ via command line flags • Shipped with JDK10...try it today!
  22. 22. bench_aref iterations per second (higher is better) 0 iter/s 45 iter/s 90 iter/s 135 iter/s 180 iter/s JDK10 JDK10 with Indy JDK10 Graal with Indy JRuby 9.1 JRuby 9.2 JRuby 9.1 JRuby 9.2 JRuby 9.1 JRuby 9.2 172.541 89.169 39.132 100.401 68.122 30.248
  23. 23. bench_mandelbrot • Generate a text Mandelbrot fractal • See? Useful! • Test of numeric performance • Heavy reliance on JVM to optimize • Graal is especially good to us here
  24. 24. 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
  25. 25. bench_mandelbrot total execution time (lower is better) 0s 1s 2s 3s 4s CRuby 2.5 CRuby 2.6 JIT JRuby JRuby Indy JRuby Indy Graal 0.139s 1.33s 2.95s 3.5s3.57s
  26. 26. bench_mandelbrot total execution time (lower is better) 0s 0.036s 0.071s 0.107s 0.142s JRuby Indy Graal TruffleRuby 0.142s0.139s
  27. 27. bench_red_black • Pure-Ruby red/black tree implementation • Good for comparing object-heavy, indirection-heavy code • Lots of blocks, instance variable accesses • Benchmark creates tree, searches it, modifies it, clears it
  28. 28. bench_red_black.rb def rbt_bm n = 100_000 a1 = []; n.times { a1 << rand(999_999) } a2 = []; n.times { a2 << rand(999_999) } start = Time.now tree = RedBlackTree.new n.times {|i| tree.add(i) } n.times { tree.delete(tree.root) } tree = RedBlackTree.new a1.each {|e| tree.add(e) } a2.each {|e| tree.search(e) } tree.inorder_walk {|key| key + 1 } tree.reverse_inorder_walk {|key| key + 1 } n.times { tree.minimum } n.times { tree.maximum } return Time.now - start end
  29. 29. bench_red_black total execution time (lower is better) 0s 0.325s 0.65s 0.975s 1.3s CRuby 2.5 CRuby 2.6 JIT JRuby JRuby Indy JRuby Indy Graal 0.526s 0.431s 1.171s1.143s 1.222s
  30. 30. JRuby on Rails
  31. 31. A Long, Hard Journey • JRuby first ran Rails in 2006 • Almost as long as Rails has existed! • Thousands of JRoR instances around the world • JRuby 9000, Ruby 2.4, 2.5 work slowed down Rails support • Rails 5.0 not supported for at least a year • ActiveRecord suffered the most
  32. 32. ActiveRecord JDBC • ActiveRecord atop Java DataBase Connectivity API • No C extensions, no -devel packages, no headers or compiling • Rebooted last year to reduce maintenance hassle • 1:1 match with Rails versions (e.g. Rails 5.1 = ARJDBC 51.x) • SQLite3, MySQL/Maria, PostgreSQL • Sharing 90%+ of code with Rails
  33. 33. Example: MySQL support • Old adapter: 1600+ lines of Ruby code • 50.0 adapter: 284 lines • More can be removed, moved into Rails • Reduced Java/JDBC code as well • 51.0 adapter: only 6 lines had to change!
  34. 34. Rails 5.1.6 actioncable: 139 runs, 733 assertions, 10 failures, 2 errors actionpack: 3063 runs, 14947 assertions, 2 failures, 0 errors actionmailer: 204 runs, 456 assertions, 0 failures, 0 errors actionview: 1957 runs, 4303 assertions, 3 failures, 4 errors activejob: 137 runs, 302 assertions, 0 failures, 0 errors activemodel: 713 runs, 2017 assertions, 0 failures, 0 errors activerecord: 4991 runs, 13902 assertions, 0 failures, 0 errors activesupport: 3671 runs, 760486 assertions, 14 failures, 0 errors railties: 40 runs, 73 assertions, 0 failures, 1 errors 99.995% pass
  35. 35. Failure: TimeWithZoneTest#test_minus_with_time_precision [activesupport/ test/core_ext/time_with_zone_test.rb:340]: Expected: 86399.999999998 Actual: 86399.99999999799 😩
  36. 36. Rails 5.2.0 actioncable: something broken bootstrapping actionpack: 3148 runs, 15832 assertions, 1 failures, 0 errors actionmailer: 204 runs, 457 assertions, 0 failures, 0 errors actionview: 1990 runs, 4395 assertions, 4 failures, 4 errors activejob: 173 runs, 401 assertions, 0 failures, 0 errors activemodel: 803 runs, 2231 assertions, 0 failures, 0 errors activerecord: 5226 runs, 14665 assertions, 8 failures, 6 errors activesupport: 4135 runs, 762864 assertions, 17 failures, 2 errors railties: uses fork()
  37. 37. Rails 5.2 Status • Tests are running pretty well • Scaffolded apps work fine • SQLite3 looks good • MySQL, PostgreSQL need updates
  38. 38. JRuby on Rails Performance
  39. 39. ActiveRecord Performance • Rails apps live and die by ActiveRecord • Largest CPU consumer by far • Heavy object churn, GC overhead • Create, read, and update measurements • If delete is your bottleneck, we need to talk • CRuby 2.5.1 vs JRuby 9.2 on JDK10
  40. 40. ActiveRecord create operations per second 0 40 80 120 160 JRuby JRuby Indy JRuby Graal CRuby 157.233 144.092140.449135.135
  41. 41. ActiveRecord find(id) operations per second 0 1250 2500 3750 5000 JRuby JRuby Indy JRuby Graal CRuby 3,940 4,672 4,999 3,937
  42. 42. ActiveRecord select operations per second 0 1050 2100 3150 4200 JRuby JRuby Indy JRuby Graal CRuby 3,125 3,703 4,132 2,403
  43. 43. ActiveRecord find_all operations per second 0 525 1050 1575 2100 JRuby JRuby Indy JRuby Graal CRuby 1,597 2,016 1,908 1,677
  44. 44. ActiveRecord update operations per second 0 1750 3500 5250 7000 JRuby JRuby Indy JRuby Graal CRuby 2,604 6,250 6,944 4,000
  45. 45. Scaling Rails • Classic problem on MRI • No concurrent threads, so we need processes • Processes inevitably duplicate runtime state • Much effort and lots of money wasted • JRuby is a great answer! • Multi-threaded single process runs your whole site
  46. 46. Measuring Rails Performance • Requests per second • ActiveRecord operations per second • CRuby versus JRuby, various configurations
  47. 47. Requests Per Second • Rails 5.1.6, Postgresql 10, scaffolded view • 4k requests to warm up, then measure every 10k • EC2 c4.xlarge: 4 vCPUs, 7.5GB • Bench, database, and app on same instance
  48. 48. Requests per second, full stack scaffolded read on Postgresql 0 325 650 975 1300 JRuby JDK8 Indy JRuby JDK10 Indy CRuby
  49. 49. Requestspersecond 0 325 650 975 1300 Requests over time 10k 20k 30k 40k 50k 60k 70k 80k 90k 100k CRuby 2.5 JRuby JDK8 JRuby JDK8 Indy JRuby JDK10 JRuby JDK10 Indy JRuby JDK10 Graal Indy CRuby 2.6 JIT
  50. 50. JRuby on Rails Memory • Single instance is much bigger, 400-500MB versus 50MB • Ten CRuby processes = 500MB • Ten JRuby threads = 400-500MB • May need to tell JVM a memory cap • For 100-way or 1000-way...you do the math
  51. 51. JRuby is the fastest way to run Rails applications.
  52. 52. Summary
  53. 53. JRuby Flags • -Xcompile.invokedynamic • Enable the use of JVM's InvokeDynamic feature • Faster straight-line perf, maybe slower startup/warmup • -Xfixnum.cache=false • Disable caching -256..256 Fixnum objects to help Graal • --dev for improved startup time (disables optimization)
  54. 54. Getting Graal • Download JDK10, it's in there • -XX:+UnlockExperimentalVMOptions
 -XX:+EnableJVMCI
 -XX:+UseJVMCICompiler • Put in JAVA_OPTS or prefix with -J at JRuby CLI • Download "GraalVM" as your JDK • Commercial product, but maybe that's your thing
  55. 55. JDK10 Warnings • JDK9 introduced stricter encapsulation • We poke through that encapsulation to support Ruby features • You'll see warnings...they're harmless, but we'll deal with them
  56. 56. Thank You! Charles Oliver Nutter headius@headius.com @headius http://jruby.org https://github.com/jruby/jruby

×