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

Rinline: An inline expansion optimizar for Ruby

Rinline: An inline expansion optimizar for Ruby

pocke

May 22, 2019
Tweet

More Decks by pocke

Other Decks in Programming

Transcript

  1. Agenda • What is inline expansion • What is Rinline

    • How Rinline works • The future of Rinline
  2. What is inline expansion • Replacing method call with body

    of the called method. • It reduce overhead of method call.
  3. Inline expansion example # Before def foo bar end def

    bar puts 42 end # After def foo puts 42 end
  4. Benchmark • `m + n` vs `1 + 2` ◦

    def m() 1 end; def n() 2 end • `m + n`: 5.71s ◦ x1.56 slower • `1 + 2`: 3.66s
  5. Rinline • Rinline is a PoC of inline expansion optimizer

    for Ruby • https://github.com/pocke/rinline • It makes Ruby faster! ◦ (I hope)
  6. Data flow • Method name → UnboundMethod → RubyVM::AST::Node →

    String of code → Inline expanded code → Ruby program by eval
  7. RubyVM::AST::Node → String of Code • Use File.binread path =

    m.source_location[0] range = node.first_index..node.last_index source = File.binread(path)[range] # => “def foo() bar() end” note: first|last_index are defined here https://github.com/pocke/rinline/blob/2cb6ffb055c619998973f5bd2a349fcb6599e95c/lib/rinline/ext/ast_ext.rb#L144-L161
  8. String of code → Inline expanded code • Replacing method

    call with called method body • I will talk it on the next section!
  9. Inline expanded code → Ruby program by eval • Use

    class_eval Object.class_eval(inline_expanded_code)
  10. Method call in Ruby • FCALL → method call with

    arguments ▪ foo(1, 2) • VCALL → method call without arguments ▪ foo • CALL → method call with receiver ▪ x.foo • OPCALL → method call with operator ▪ foo + bar
  11. Rinline supports FCALL and VCALL • Easy to support (Supporting

    CALL is difficult) ◦ CALL has a receiver, and receiver klass is unknown statically in most cases
  12. How to replace • RubyVM::AST::Node → VCALL/FCALL Node → UnboundMethod

    → RubyVM::AST::Node → String of called method → lvar renamed code → Replaced code
  13. Example def foo x = 1 bar p x *

    3 end def bar x = 21 p x * 2 end
  14. Find VCALL / FCALL def foo x = 1 bar

    # ← VCALL node p x * 3 end
  15. VCALL/FCALL Node → UnboundMethod → Method definition Node method_name =

    vcall_node.children[0] # => :bar method = Klass.instance_method(method_name) method_def_node = RubyVM::AST.of(method)
  16. Example def foo x = 1 bar p x *

    3 # => 3 end def bar x = 21 p x * 2 # => 42 end
  17. Example: It will be break! def foo x = 1

    x = 21 p x * 2 # => 42 p x * 3 # => 63 end def bar x = 21 p x * 2 end
  18. Fix local var collision def foo x = 1 x_XXX

    = 21 p x_XXX * 2 # => 42 p x * 3 # => 3 end def bar x = 21 p x * 2 end
  19. lvar renamed code → Replaced code def foo x =

    1 x_XXX = 21 p x_XXX * 2 p x * 3 end It is ok, but actually the replaced code will be enclosed with double braces
  20. lvar renamed code → Replaced code def foo x =

    1 ((x_XXX = 21 p x_XXX * 2)) p x * 3 end
  21. Why braces bar * 3 # => 9 1 +

    2 * 3 # => 7 (1 + 2) * 3 # => 9 def bar 1 + 2 end
  22. Why double braces p (1; 2) # => SyntaxError! p

    ((1; 2)) # => Valid def bar 1; 2 end
  23. Meaningful optimization • Currently it makes micro benchmark faster •

    But it does not make actual app faster ◦ For example, I tried to optimize optcarrot, but it does not change the FPS
  24. Fix many bugs • Rinline generates invalid code ◦ For

    example: It breaks on define_method
  25. Support super • Currently, Rinline just ignores `super` node •

    It can support `super` with UnboundMethod#super_method ◦ https://twitter.com/udzura/status/11303947244 58565633
  26. pp self • Masataka “pocke” Kuwabara • Work for Bit

    Journey, Inc. Thank you for listening!