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

Ruby JIT Hacking Guide / RubyKaigi 2023

Ruby JIT Hacking Guide / RubyKaigi 2023

Ruby JIT Hacking Guide
https://rubykaigi.org/2023/

Takashi Kokubun

May 13, 2023
Tweet

More Decks by Takashi Kokubun

Other Decks in Programming

Transcript

  1. Ruby JIT


    Hacking Guide
    @k0kubun

    View Slide

  2. @k0kubun

    View Slide

  3. RJIT ERB
    Haml Slim
    I maintain

    View Slide

  4. What's JIT?

    View Slide

  5. Ruby JITs
    MJIT
    Ruby 2.6

    View Slide

  6. Ruby JITs
    YJIT
    Ruby 3.1
    MJIT
    Ruby 2.6

    View Slide

  7. Ruby JITs
    YJIT
    Ruby 3.1
    MJIT
    Ruby 2.6
    RJIT
    Ruby 3.3

    View Slide

  8. Railsbench
    Speedup relative to 2.5 No JIT (s/s)
    0
    0.5
    1
    1.5
    2
    Ruby 2.5 Ruby 2.6 Ruby 2.7 Ruby 3.0 Ruby 3.1 Ruby 3.2 Ruby 3.3
    1.93
    1.85
    1.54 1.59
    1.2
    1.25
    1.08
    1.05
    1.07
    1.26
    1.24
    1.12
    1.09
    1.05
    1.07
    1
    No JIT MJIT / RJIT YJIT

    View Slide

  9. Ruby 3.2 YJIT

    View Slide

  10. Ruby 3.3 YJIT

    View Slide

  11. Using YJIT
    • Install Rust and then build Ruby


    • Use --yjit or export RUBY_YJIT_ENABLE=1

    View Slide

  12. Ruby JIT Hacking Guide

    View Slide

  13. Ruby Hacking Guide

    View Slide

  14. Ruby JIT Hacking Guide
    JIT
    JIT

    View Slide

  15. How Ruby JIT works
    Ruby

    View Slide

  16. How Ruby JIT works
    1
    +
    2
    Ruby
    Abstract

    Syntax

    Tree

    View Slide

  17. How Ruby JIT works
    1
    +
    2
    putobject 1


    putobject 2


    opt_plus


    leave
    Ruby
    Abstract

    Syntax

    Tree
    Instruction

    Sequence

    (Bytecode)

    View Slide

  18. How Ruby JIT works
    1
    +
    2
    putobject 1


    putobject 2


    opt_plus


    leave
    Ruby
    Abstract

    Syntax

    Tree
    Instruction

    Sequence

    (Bytecode)
    Machine

    Code

    View Slide

  19. x86_64 assembly

    View Slide

  20. • Read asm comments


    •--yjit-dump-disasm


    •--rjit-dump-disasm
    x86_64 assembly

    View Slide

  21. • mov: assignment instruction


    • esi: register for stack[0]
    x86_64 assembly
    stack[0] = 3

    View Slide

  22. x86_64 assembly
    • mov: assignment instruction


    • edi: register for stack[1]
    stack[0] = 3


    stack[1] = 5

    View Slide

  23. x86_64 assembly
    • add,sub: arithmetic instruction


    • rax: temporary register
    stack[0] = 3


    stack[1] = 5


    temp = stack[0]


    temp -= 1


    temp += stack[1]

    View Slide

  24. x86_64 assembly
    • jo: jump if over
    fl
    ow


    • rsi: register for stack[0]
    stack[0] = 3


    stack[1] = 5


    temp = stack[0]


    temp -= 1


    temp += stack[1]


    jump if overflow


    stack[0] = temp

    View Slide

  25. x86_64 encoding

    View Slide

  26. x86_64 encoding
    opv86: https://hikalium.github.io/opv86/

    View Slide

  27. x86_64 encoding
    opv86: https://hikalium.github.io/opv86/

    View Slide

  28. Calling a custom JIT
    Ruby 3.2 Ruby 3.3+
    1. Run Ruby with --mjit=pause --rjit=pause
    2. Override RubyVM::MJIT.compile RubyVM::RJIT#compile
    3. Call RubyVM::MJIT.resume RubyVM::RJIT.resume

    View Slide

  29. Building JIT is fun

    View Slide

  30. k0kubun/ruby-jit-challenge
    Ruby JIT Challenge

    View Slide

  31. Ruby JIT Challenge
    Hashtag: #ruby-jit-challenge
    Speedup relative to No JIT (s/s)
    0
    3
    6
    9
    12
    No JIT RJIT YJIT Ruby JIT
    11.08
    6.31
    3.75
    1
    Fibonatti benchmark

    View Slide

  32. Optimizing Ruby JIT

    View Slide

  33. Side exits
    side exit

    View Slide

  34. Method rede
    fi
    nition
    Rede
    fi
    nition Hook
    Invalidate

    View Slide

  35. Method rede
    fi
    nition
    Rede
    fi
    nition Hook
    Invalidate
    side exit

    View Slide

  36. Constant rede
    fi
    nition

    View Slide

  37. Register allocation: Stack
    0: rsi
    1: rdi
    4: r10
    2: r8
    3: r9
    VM stack

    View Slide

  38. Register allocation: Stack
    0: rsi
    1: rdi
    4: r10
    2: r8
    3: r9
    VM stack

    View Slide

  39. Register allocation: Local variables
    • Spill registers on C function calls


    • Binding


    • debug_inspector API

    View Slide

  40. Polymorphic method cache

    View Slide

  41. Splitting

    View Slide

  42. Method inlining

    View Slide

  43. Conclusion
    • Enjoy custom JIT development


    • Let's make YJIT the best Ruby JIT

    View Slide