unused space
code
readonly data
1 method
Heap
2MB
(big)
about 4KB
?KB
Slide 35
Slide 35 text
unused space
code
readonly data
unused space
code
readonly data
unused space
code
readonly data
3 methods
Heap
Slide 36
Slide 36 text
unused space
code
readonly data
unused space
code
readonly data
unused space
code
readonly data
L2, ...
cache
L1
cache
Heap
Slide 37
Slide 37 text
unused space
code
readonly data
unused space
code
readonly data
unused space
code
readonly data
L2, ...
cache
L1
cache
Heap
Slide 38
Slide 38 text
unused space
Heap
code
readonly data
unused space
code
readonly data
unused space
code
readonly data
L2, ...
cache
L1
cache
Slide 39
Slide 39 text
2. When there are still methods to be JIT-ed
Slide 40
Slide 40 text
"JIT compaction"
Slide 41
Slide 41 text
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"
Slide 42
Slide 42 text
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
Slide 43
Slide 43 text
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
Slide 44
Slide 44 text
CPU/memory resources
for C compiler
Slide 45
Slide 45 text
Locks on GC, waitpid, ...
Slide 46
Slide 46 text
3. When TracePoint is enabled
(to be fixed after Ruby 2.6)
Slide 47
Slide 47 text
What's TracePoint? When is it enabled?
• gem 'web-console' on your Rails application
• byebug or binding.pry (with pry-byebug)
• Measuring coverage
Slide 48
Slide 48 text
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
Slide 49
Slide 49 text
What is made faster by JIT?
Slide 50
Slide 50 text
1. Almost all methods
Slide 51
Slide 51 text
def plus(a, b)
a + b
end
Virtual Machine
getlocal :a
getlocal :b
send :+
Program Counter
Stack Pointer
Computer
Registers
Instruction Pointer
Slide 52
Slide 52 text
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
def three
1 + 2
end
putobject 1
putobject 2
send :+
JIT
mov $0x3,%eax
retq
Method inlining on JIT
Slide 56
Slide 56 text
3. Calling Ruby method
Slide 57
Slide 57 text
Method dispatch on Ruby VM
foo.bar
Slide 58
Slide 58 text
Method dispatch on Ruby VM
foo.bar
Method search
(slow)
Slide 59
Slide 59 text
Method dispatch on Ruby VM
foo.bar
Method search
(slow)
Ruby
method
C function
attr_reader
alias
Method entry
Foo#bar
Slide 60
Slide 60 text
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
Slide 61
Slide 61 text
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?
Slide 62
Slide 62 text
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?
Slide 63
Slide 63 text
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
Slide 64
Slide 64 text
4. Instance variable access
Slide 65
Slide 65 text
Reading instance variable on VM
@foo
Slide 66
Slide 66 text
Reading instance variable on VM
@foo
Search index
(many branches, slow)
Slide 67
Slide 67 text
Reading instance variable on VM
@foo
Search index
(many branches, slow)
self.class.
instance_variables[index = 2]
Slide 68
Slide 68 text
Reading instance variable on VM
Search index
(many branches, slow)
self.class.
instance_variables[index = 2]
@foo
has cache
Verify cache
Slide 69
Slide 69 text
Reading instance variable on VM
Search index
(many branches, slow)
self.class.
instance_variables[index = 2]
@foo different
class?
has cache
Verify cache
Slide 70
Slide 70 text
Reading instance variable on VM
Search index
(many branches, slow)
self.class.
instance_variables[index = 2]
@foo different
class?
has cache
Verify cache
Slide 71
Slide 71 text
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
Slide 72
Slide 72 text
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 ✓ ?
Slide 73
Slide 73 text
Future of Ruby's JIT
Slide 74
Slide 74 text
Will Ruby 2.6 be fast only on Optcarrot?
Slide 75
Slide 75 text
Future idea (not for 2.6)
• Stack allocation of objects
- That requires "escape analysis" (not easy)
- We haven't estimated its possible impact yet
Slide 76
Slide 76 text
Some rooms for improvements in 2.6 (!?)
• Change heuristics to trigger/compact JIT
• Profile-guided optimization
• Method inlining
Slide 77
Slide 77 text
How can we experiment them?
Benchmark!
Slide 78
Slide 78 text
No content
Slide 79
Slide 79 text
No content
Slide 80
Slide 80 text
Create a benchmark for Ruby 3x3?
Slide 81
Slide 81 text
benchmark_driver.gem
Slide 82
Slide 82 text
No content
Slide 83
Slide 83 text
No content
Slide 84
Slide 84 text
No content
Slide 85
Slide 85 text
No content
Slide 86
Slide 86 text
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!!!