Ruby 2.4 Internals
Koichi Sasada
ko1@cookpad.com

Note:Ruby 2.4.0 has
several bugs.
Note2:
No x.y.0 doesn’t
have several bugs.

Ruby 2.4.0

New features
written in a release announcement• Introduce hash table improvement (by Vladimir Makarov)• Binding#irb: Start a REPL session similar to binding.pry• Unify Fixnum and Bignum into Integer• String supports Unicode case mappings• Performance improvements• Array#max, Array#min• Regexp#match?
• speed up instance variable access• Debugging• Thread#report_on_exception and Thread.report_on_exception• Thread deadlock detection now shows threads with their backtrace and dependency• Other notable changes since 2.3• Support OpenSSL 1.1.0 (drop support for 0.9.7 or prior)• ext/tk is now removed from stdlib Feature #8539• XMLRPC is now removed from stdlib Feature #12160

Ruby 2.4 Internals
Koichi Sasada
ko1@cookpad.com

https://blog.heroku.com/r
uby-2-4-features-hashesintegers-roundingAny othertopics?

benchmark/bm_app_lc_fizzbuzz.rb

Bug #10212 MRI is not for lambda calculus

JRuby 26 sec

mruby 27 sec
MRI 114 sec

Feature #12628
change block/env structs

Ruby 2.4 Internals
Change block/env structs
Koichi Sasada
ko1@cookpad.com

Issues1.

2.

3.

we need to clear
rb_control_frame_t::block_iseq for
every frame setup. It consumes space (aVALUE for each frame) and initializing
time.
There are several block passing ways byISeq (iter{...}), Proc(iter(&pr)),Symbol(iter(:sym)). However, they are
not optimized (for Symbol blocks, thereis only ad-hoc check code).
Env (and Proc, Binding) objects are not
WB-protected ([Bug #10212]).

Method dispatch (etc)
improvements
Cleanup src code
Improve GC perf.

Patch•https://github.com/ruby/ruby/compare/tru
nk...ko1:block_code•“Showing with 1,863 additions and 1,070
deletions.”

Approaches•For (1), (2)• Introduce Block Handler (BH)
• Using BH•For (3)• Introduce Write Barriers (WB) for Env objects

WB protected or unprotected?
•Ruby 2.1.0 introduced Generational GC• Only newer objects• GenGC requires “Write barriers” (WB), but
MRI allows WB unprotected objects(See my past presentations for details)• WB protected objects: GenGC → Fast
• WB unprotected objects: Not GenGC → Slow

RubyVM::Env objects•Env objects represent
captured local variables• Each Proc or Binding has
at least one Env object• Proc object “$pr”
consists of 3 Env objects

a=1
1.times{|b|1.times{|c|$pr = Proc.new{# you can access a, b, c}}}
Proc$pr

Env
c=0
Env
b=0

Env
a=1

RubyVM::Env objects wereWB-unprotected•They were WB unprotected because:• Difficulty of implementation• Performance issue

Performance issue
Assignment performance• Ruby 2.3 or before*(ep - idx) = val;

• Naïve implementation#define VM_EP_IN_HEAP_P(th, ep) ¥(!((th)->stack <= (ep) && ¥(ep) < ((th)->stack + (th)->stack_size)))if (VM_EP_IN_HEAP_P(ep)) {RB_OBJ_WRITE(VM_ENV_EP_ENVVAL(ep),
ep-idx, val);}else *(ep - idx) = val;

Ideas
1. Lightweight escape detection
2. Skip WB except really required timing

Idea
Lightweight escape detection•Move cfp->flags to ep[0]•Introduce a VM_ENV_FLAG_ESCAPED flag torepresent escaped Env.
• // Before#define VM_EP_IN_HEAP_P(th, ep)(!((th)->stack <= (ep) && (ep) < ((th)->stack + (th)->stack_size)))• // After#define VM_EP_IN_HEAP_P(ep)(ep[0] & VM_ENV_FLAG_ESCAPED)Idea
Skip WB except really required timing
1.
2.

3.
4.

At initializing Env objects, VM_ENV_FLAG_WB_REQUIRED is true.
At first local variable assignment, VM_ENV_FLAG_WB_REQUIRED
is true, we remember this Env object forcibly. And turn off thisflag.
At next local variable assignment, VM_ENV_FLAG_WB_REQUIRED
is false, so we can ignore WB protection.
At GC marking for this Env object, we turn on
VM_ENV_FLAG_WB_REQUIRED and goto (2).

Very danger technique because it depends on GC implementationNaïve code
#define VM_EP_IN_HEAP_P(th, ep)(!((th)->stack <= (ep)&& (ep) < ((th)->stack + (th)->stack_size)))
vm_env_write(const VALUE *ep, int index, VALUE v) {if (VM_EP_IN_HEAP_P(ep)) {RB_OBJ_WRITE(VM_ENV_EP_ENVVAL(ep), ep-idx, val);}else {*(ep - idx) = val;}}Final code
vm_env_write(const VALUE *ep, int index, VALUE v) {VALUE flags = ep[VM_ENV_DATA_INDEX_FLAGS];
if (LIKELY((flags & VM_ENV_FLAG_WB_REQUIRED) == 0)) {*(ep - idx) = val; /* mostly used */}else {/* remember env value forcibly */vm_env_write_slowpath(ep, index, v);}}

Benchmark result

Bug #10212 MRI is not for lambda calculus
lc_fizzbuzz with MRI versions
Execution time (sec)app_lc_fizzbuzz
150
100500ruby_2_0

ruby_2_1

ruby_2_2

ruby_2_3

trunkBug #10212 MRI is not for lambda calculus
lc_fizzbuzz with MRI, JRuby, mruby
Execution time (sec)app_lc_fizzbuzz
50
40

40.18536.97630

18.706

20100
trunk

jruby

mruby

Summary•Ruby 2.4.0 has many improvements•Now Proc (Env) objects are WB protected
and we have more faster GC (marking)•My ideas allow to protect Env objects
without big performance impact

Thank you for your attention
Koichi Sasada<ko1@cookpad.com>