Slide 1

Slide 1 text

Inline Class#new Feature #21254

Slide 2

Slide 2 text

Implement Class#new in Ruby class Class def new(...) Primitive.attr! :c_trace Primitive.pop!( Primitive.send_delegate!( Primitive.dup!(Primitive.rb_class_alloc2), :initialize, ...)) end end class BasicObject def initialize Primitive.attr! :c_trace nil end end

Slide 3

Slide 3 text

Class#new ➡ Ruby

Slide 4

Slide 4 text

Inline Class#new Feature #21254

Slide 5

Slide 5 text

Instructions

Slide 6

Slide 6 text

Before > ruby --dump=insns -e'Object.new' == disasm: #@-e:1 (1,0)-(1,10)> 0000 opt_getconstant_path ( 1)[Li] 0002 opt_send_without_block 0004 leave

Slide 7

Slide 7 text

After > ./miniruby --dump=insns -e'Object.new' == disasm: #@-e:1 (1,0)-(1,10)> 0000 opt_getconstant_path ( 1)[Li] 0002 putnil 0003 swap 0004 opt_new , 11 0007 opt_send_without_block 0009 jump 14 0011 opt_send_without_block 0013 swap 0014 pop 0015 leave

Slide 8

Slide 8 text

Speedup

Slide 9

Slide 9 text

Positional Parameters Allocations per Second by Ruby version Allocations Per Second 0 9500000 19000000 28500000 38000000 Number of Parameters 0 1 2 3 4 5 6 7 8 9 10 Ruby 3.5 + inlining Ruby 3.4

Slide 10

Slide 10 text

~1.8x Faster

Slide 11

Slide 11 text

Keyword Parameters Allocations per second by Ruby version Allocations Per Second 0 10000000 20000000 30000000 40000000 Number of Parameters 0 1 2 3 4 5 6 7 8 9 10 Ruby 3.5+inlining Ruby 3.4

Slide 12

Slide 12 text

3 Keyword Params: 3.2x faster

Slide 13

Slide 13 text

10 Keyword Params: 6.2x faster

Slide 14

Slide 14 text

Positional Parameters + Varied Classes Allocations per second by Ruby version
 (varying allocated class) Allocations per Second 0 9500000 19000000 28500000 38000000 Number of Parameters 0 1 2 3 4 5 6 7 8 9 10 Ruby 3.5+Inlining Ruby 3.4

Slide 15

Slide 15 text

Keyword Parameters + Varied Classes Allocations per second by Ruby version
 (varying allocated class) Allocations Per Second 0 10000000 20000000 30000000 40000000 Number of Parameters 0 1 2 3 4 5 6 7 8 9 10 Ruby 3.5+Inlining Ruby 3.4

Slide 16

Slide 16 text

Downsides

Slide 17

Slide 17 text

More memory

Slide 18

Slide 18 text

Measure ISeq size How many bytes does the “alloc” method use? require "objspace" def alloc Object.new end m = method(:alloc) insn = RubyVM::InstructionSequence.of(insn) puts ObjectSpace.memsize_of(insn) Ruby 3.5 + inlining: 656 bytes Ruby 3.4: 544 bytes +122 Bytes

Slide 19

Slide 19 text

Real World Memory Increase (ISeq Only) 0.5% Increase in ISeq Size (Ruby 3.4.2 vs 3.5.0+inline) irb(main):001> 737191972 - 733354388 => 3837584 Shopify M onolith ISeq Sizes

Slide 20

Slide 20 text

Real World Memory Increase (all memory) 1mb increase total (~4GB heap) irb(main):001> 3981075617 - 3979926505 => 1149112 Shopify M onolith Total H eap

Slide 21

Slide 21 text

Different Stack Trace

Slide 22

Slide 22 text

Stack Trace is Different Class#new is missing class Foo def initialize puts caller end end def hello Foo.new end hello > ruby test.rb test.rb:8:in 'Class#new' test.rb:8:in 'Object#hello' test.rb:11:in '' Ruby 3.4 > ./ruby test.rb test.rb:8:in 'Object#hello' test.rb:11:in '' Ruby 3.5 + inlining

Slide 23

Slide 23 text

No Test Failures 😅