Ruby 2.6 JIT / RubyConf 2018

Ruby 2.6 JIT / RubyConf 2018

RubyConf 2018 Los Angeles

08d5432a5bc31e6d9edec87b94cb1db1?s=128

Takashi Kokubun

November 15, 2018
Tweet

Transcript

  1. Takashi Kokubun @k0kubun Ruby 2.6 JIT

  2. @k0kubun Takashi Kokubun / Arm Ruby Committer for JIT, ERB

  3. None
  4. None
  5. None
  6. None
  7. Ruby 2.6

  8. None
  9. What's "JIT"?

  10. JIT: Just In Time Compiler

  11. None
  12. History of Ruby implementation

  13. def plus(a, b) a + b end + a b

    Parse Ruby 1.8 Traverse
  14. def plus(a, b) a + b end + a b

    Compile Ruby 1.9~2.5 Interpret getlocal :a getlocal :b send :+
  15. def plus(a, b) a + b end + a b

    JIT Ruby 2.6 (New!) Execute getlocal :a getlocal :b send :+ mov %rdi,%rdx mov %rsi,%rax add %rdx,%rax
  16. How can we use it?

  17. or ruby --jit ... RUBYOPT="--jit" rails s ...

  18. Let's benchmark Ruby 2.6 JIT

  19. None
  20. https://gist.github.com/k0kubun/887b9567afa86b326556389ef00e4200

  21. https://gist.github.com/k0kubun/887b9567afa86b326556389ef00e4200 Ruby 2.0: 34.3 Ruby 2.6: 86.7 2.53x faster

  22. https://gist.github.com/k0kubun/887b9567afa86b326556389ef00e4200 Ruby 2.5: 48.3 Ruby 2.6: 86.7 1.80x faster

  23. Achieved Ruby 3x2.5 !!!

  24. How about other benchmarks?

  25. None
  26. None
  27. JIT makes things slower?

  28. Today's topic JIT performance characteristics

  29. When does Ruby become slow on JIT?

  30. 1. When there are many JIT-ed methods

  31. --jit-max-cache (default: 1000)

  32. JIT by C compiler & dynamic loading

  33. unused space code readonly data 1 method Heap

  34. unused space code readonly data 1 method Heap 2MB (big)

    about 4KB ?KB
  35. unused space code readonly data unused space code readonly data

    unused space code readonly data 3 methods Heap
  36. unused space code readonly data unused space code readonly data

    unused space code readonly data L2, ... cache L1 cache Heap
  37. unused space code readonly data unused space code readonly data

    unused space code readonly data L2, ... cache L1 cache Heap
  38. unused space Heap code readonly data unused space code readonly

    data unused space code readonly data L2, ... cache L1 cache
  39. 2. When there are still methods to be JIT-ed

  40. "JIT compaction"

  41. unused space code readonly data unused space code readonly data

    unused space code readonly data When it reaches --jit-max-cache, it fires "JIT compaction"
  42. unused space code readonly data unused space code readonly data

    unused space code readonly data unused space code code code readonly data readonly data readonly data
  43. unused space code readonly data unused space code readonly data

    unused space code readonly data unused space code code code readonly data readonly data readonly data L2, ... cache
  44. CPU/memory resources for C compiler

  45. Locks on GC, waitpid, ...

  46. 3. When TracePoint is enabled (to be fixed after Ruby

    2.6)
  47. What's TracePoint? When is it enabled? • gem 'web-console' on

    your Rails application • byebug or binding.pry (with pry-byebug) • Measuring coverage
  48. Summary: When JIT makes Ruby slow Optcarrot Rails 1. When

    there are many JIT-ed methods ✓ 2. When there are still methods to be JIT-ed (Is it a short benchmark?) ✓ 3. When TracePoint is enabled
  49. What is made faster by JIT?

  50. 1. Almost all methods

  51. def plus(a, b) a + b end Virtual Machine getlocal

    :a getlocal :b send :+ Program Counter Stack Pointer Computer Registers Instruction Pointer
  52. def plus(a, b) a + b end Virtual Machine getlocal

    :a getlocal :b send :+ Program Counter Stack Pointer Computer JIT Instruction Pointer Registers mov %rdi,%rdx mov %rsi,%rax add %rdx,%rax
  53. 2. Basic operators on core classes

  54. VM-optimized methods Integer, Float +, -, *, /, % Array

    +, <<, [], []=, empty?, size, length, min, max Hash [], []=, empty?, size, length String +, <<, =~, empty?, size, length, succ common !, !=, ==, <, >, <=, >=
  55. def three 1 + 2 end putobject 1 putobject 2

    send :+ JIT mov $0x3,%eax retq Method inlining on JIT
  56. 3. Calling Ruby method

  57. Method dispatch on Ruby VM foo.bar

  58. Method dispatch on Ruby VM foo.bar Method search (slow)

  59. Method dispatch on Ruby VM foo.bar Method search (slow) Ruby

    method C function attr_reader alias Method entry Foo#bar
  60. Method dispatch on Ruby VM foo.bar Method search (slow) Verify

    cache Ruby method C function attr_reader alias Method entry Foo#bar has cache
  61. Method dispatch on Ruby VM foo.bar Method search (slow) Verify

    cache Ruby method C function attr_reader alias Method entry Foo#bar has cache redefined?
  62. Method dispatch on Ruby VM foo.bar Method search (slow) Verify

    cache Ruby method C function attr_reader alias Method entry Foo#bar has cache redefined?
  63. Method dispatch on JIT foo.bar Verify cache Ruby method Method

    entry Foo#bar has cache Cancel JIT! Less branches Less memory access redefined? some inlining
  64. 4. Instance variable access

  65. Reading instance variable on VM @foo

  66. Reading instance variable on VM @foo Search index (many branches,

    slow)
  67. Reading instance variable on VM @foo Search index (many branches,

    slow) self.class. instance_variables[index = 2]
  68. Reading instance variable on VM Search index (many branches, slow)

    self.class. instance_variables[index = 2] @foo has cache Verify cache
  69. Reading instance variable on VM Search index (many branches, slow)

    self.class. instance_variables[index = 2] @foo different class? has cache Verify cache
  70. Reading instance variable on VM Search index (many branches, slow)

    self.class. instance_variables[index = 2] @foo different class? has cache Verify cache
  71. Reading instance variable on JIT self.class. instance_variables[2] @foo different class?

    has cache Verify cache Cancel JIT! inlined index Less branches Less memory access
  72. Summary: When JIT makes Ruby fast Optcarrot Rails 1. Almost

    all methods ✓ ✓ 2. Basic operators on core classes ✓ 3. Calling Ruby method ✓ ✓ 4. Instance variable access ✓ ?
  73. Future of Ruby's JIT

  74. Will Ruby 2.6 be fast only on Optcarrot?

  75. Future idea (not for 2.6) • Stack allocation of objects

    - That requires "escape analysis" (not easy) - We haven't estimated its possible impact yet
  76. Some rooms for improvements in 2.6 (!?) • Change heuristics

    to trigger/compact JIT • Profile-guided optimization • Method inlining
  77. How can we experiment them? Benchmark!

  78. None
  79. None
  80. Create a benchmark for Ruby 3x3?

  81. benchmark_driver.gem

  82. None
  83. None
  84. None
  85. None
  86. Conclusion • Ruby 2.6.0-preview3 JIT is still early days -

    Small --jit-max-cache might be an option for now • We still have chance to make Ruby 2.6.0 better - Benchmarks are wanted!!!