Contenu connexe
Similaire à 東急Ruby会議向け「rubyの細かい話」 (20)
東急Ruby会議向け「rubyの細かい話」
- 5. z
遅い!
def func_p(d,n)
pr = proc{|d,n|
next hook if n==0
pr.call([d,d],n-1)
}
pr.call(d,n)
end
def func_m(d,n)
def func_m_sub(d,n)
return hook if n==0
func_m_sub([d,d],n-1)
end
func_m_sub(d,n)
end
N=2000
Benchmark.ips do |x|
x.report("func_p") { func_p(1,N) }
x.report("func_m") { func_m(1,N) }
x.compare!
end
Warming up --------------------------------------
func_p 86.000 i/100ms
func_m 391.000 i/100ms
Calculating -------------------------------------
func_p 1.063k (±59.1%) i/s - 1.032k in 6.066881s
func_m 3.987k (± 3.6%) i/s - 19.941k in 5.007711s
Comparison:
func_m: 3986.7 i/s
func_p: 1062.7 i/s - 3.75x slower
ここだけの話、
少し緩和できる方法があります。
- 6. z
そこで、細かい話
def proc_call (*args)
yield *args
end
def func_py(d,n)
pr = proc{|d,n|
next hook if n==0
proc_call([d,d],n-1,&pr)
}
proc_call (d,n,&pr)
end
Warming up --------------------------------------
func_p 86.000 i/100ms
func_py 143.000 i/100ms
func_m 391.000 i/100ms
Calculating -------------------------------------
func_p 1.063k (±59.1%) i/s - 1.032k in 6.066881s
func_py 1.435k (± 2.6%) i/s - 7.293k in 5.083872s
func_m 3.987k (± 3.6%) i/s - 19.941k in 5.007711s
Comparison:
func_m: 3986.7 i/s
func_py: 1435.5 i/s - 2.78x slower
func_p: 1062.7 i/s - 3.75x slower
1.35x faster!!
結論: (今のRubyでrubyで書いた)
procはcallせずにyieldしろ!
(特に再帰するなら)
- 8. z
ここから裏話 資料が適当になります
なんでProc#callが遅いのか-- C level backtrace information -------------------------------------------
/usr/ruby/2.3.1/bin/ruby(rb_vm_bugreport+0x51f) [0x7f7c6570178f] vm_dump.c:688
/usr/ruby/2.3.1/bin/ruby(rb_bug_context+0xd0) [0x7f7c656dbe30] error.c:435
/usr/ruby/2.3.1/bin/ruby(sigsegv+0x3e) [0x7f7c655d5dae] signal.c:890
/lib/x86_64-linux-gnu/libpthread.so.0 [0x7f7c650b4330]
/usr/ruby/2.3.1/lib/ruby/2.3.0/x86_64-linux/fiddle.so(rb_fiddle_ptr_aset+0x130) [0x7f7c636699c0] pointer.c:587
/usr/ruby/2.3.1/bin/ruby(vm_call_cfunc+0xf6) [0x7f7c65657f76] vm_insnhelper.c:1638
/usr/ruby/2.3.1/bin/ruby(vm_call_method+0xe3) [0x7f7c65665d43] vm_insnhelper.c:2172
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x15ec) [0x7f7c6565fd9c] insns.def:1893
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(invoke_block_from_c_0+0x161) [0x7f7c65664f11] vm.c:971
/usr/ruby/2.3.1/bin/ruby(vm_invoke_proc+0xb8) [0x7f7c65664fe8] vm.c:996
/usr/ruby/2.3.1/bin/ruby(vm_call_opt_call+0xa7) [0x7f7c65665777] vm.c:1072
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x1fe9) [0x7f7c65660799] insns.def:995
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(invoke_block_from_c_0+0x161) [0x7f7c65664f11] vm.c:971
/usr/ruby/2.3.1/bin/ruby(vm_invoke_proc+0xb8) [0x7f7c65664fe8] vm.c:996
/usr/ruby/2.3.1/bin/ruby(vm_call_opt_call+0xa7) [0x7f7c65665777] vm.c:1072
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x1fe9) [0x7f7c65660799] insns.def:995
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(invoke_block_from_c_0+0x161) [0x7f7c65664f11] vm.c:971
/usr/ruby/2.3.1/bin/ruby(vm_invoke_proc+0xb8) [0x7f7c65664fe8] vm.c:996
/usr/ruby/2.3.1/bin/ruby(vm_call_opt_call+0xa7) [0x7f7c65665777] vm.c:1072
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x1fe9) [0x7f7c65660799] insns.def:995
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(invoke_block_from_c_0+0x161) [0x7f7c65664f11] vm.c:971
/usr/ruby/2.3.1/bin/ruby(vm_invoke_proc+0xb8) [0x7f7c65664fe8] vm.c:996
/usr/ruby/2.3.1/bin/ruby(vm_call_opt_call+0xa7) [0x7f7c65665777] vm.c:1072
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x1fe9) [0x7f7c65660799] insns.def:995
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(invoke_block_from_c_0+0x161) [0x7f7c65664f11] vm.c:971
/usr/ruby/2.3.1/bin/ruby(vm_invoke_proc+0xb8) [0x7f7c65664fe8] vm.c:996
/usr/ruby/2.3.1/bin/ruby(vm_call_opt_call+0xa7) [0x7f7c65665777] vm.c:1072
/usr/ruby/2.3.1/bin/ruby(vm_call_method+0xe3) [0x7f7c65665d43] vm_insnhelper.c:2172
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x1fe9) [0x7f7c65660799] insns.def:995
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(invoke_block_from_c_0+0x161) [0x7f7c65664f11] vm.c:971
/usr/ruby/2.3.1/bin/ruby(vm_invoke_proc+0xb8) [0x7f7c65664fe8] vm.c:996
/usr/ruby/2.3.1/bin/ruby(vm_call_opt_call+0xa7) [0x7f7c65665777] vm.c:1072
/usr/ruby/2.3.1/bin/ruby(vm_call_method+0xe3) [0x7f7c65665d43] vm_insnhelper.c:2172
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x1fe9) [0x7f7c65660799] insns.def:995
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(ruby_exec_internal+0xc4) [0x7f7c6550c0e4] eval.c:245
/usr/ruby/2.3.1/bin/ruby(ruby_run_node+0x2d) [0x7f7c6550fcad] eval.c:310
/usr/ruby/2.3.1/bin/ruby(main+0x4b) [0x7f7c6550bd4b] addr2line.c:179
-- Ruby level backtrace information ----------------------------------------
test.rb:73:in `<main>'
test.rb:15:in `func_p'
test.rb:13:in `block in func_p'
test.rb:13:in `block in func_p'
test.rb:13:in `block in func_p'
test.rb:13:in `block in func_p'
test.rb:13:in `block in func_p'
test.rb:12:in `block in func_p'
test.rb:70:in `hook'
test.rb:70:in `[]='
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(invoke_block_from_c_0+0x161) [0x7f7c65664f11] vm.c:971
/usr/ruby/2.3.1/bin/ruby(vm_invoke_proc+0xb8) [0x7f7c65664fe8] vm.c:996
/usr/ruby/2.3.1/bin/ruby(vm_call_opt_call+0xa7) [0x7f7c65665777] vm.c:1072
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x1fe9) [0x7f7c65660799] insns.def:995
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f7c656643d1] vm.c:1650
call毎にCのマシンスタックが沢山積み重なる
- 10. z
なんでyieldが速いのか
-- C level backtrace information -------------------------------------------
/usr/ruby/2.3.1/bin/ruby(rb_vm_bugreport+0x51f) [0x7f0977a0978f] vm_dump.c:688
/usr/ruby/2.3.1/bin/ruby(rb_bug_context+0xd0) [0x7f09779e3e30] error.c:435
/usr/ruby/2.3.1/bin/ruby(sigsegv+0x3e) [0x7f09778dddae] signal.c:890
/lib/x86_64-linux-gnu/libpthread.so.0 [0x7f09773bc330]
/usr/ruby/2.3.1/lib/ruby/2.3.0/x86_64-linux/fiddle.so(rb_fiddle_ptr_aset+0x130) [0x7f09759719c0] pointer.c:587
/usr/ruby/2.3.1/bin/ruby(vm_call_cfunc+0xf6) [0x7f097795ff76] vm_insnhelper.c:1638
/usr/ruby/2.3.1/bin/ruby(vm_call_method+0xe3) [0x7f097796dd43] vm_insnhelper.c:2172
/usr/ruby/2.3.1/bin/ruby(vm_exec_core+0x15ec) [0x7f0977967d9c] insns.def:1893
/usr/ruby/2.3.1/bin/ruby(vm_exec+0x81) [0x7f097796c3d1] vm.c:1650
/usr/ruby/2.3.1/bin/ruby(ruby_exec_internal+0xc4) [0x7f09778140e4] eval.c:245
/usr/ruby/2.3.1/bin/ruby(ruby_run_node+0x2d) [0x7f0977817cad] eval.c:310
/usr/ruby/2.3.1/bin/ruby(main+0x4b) [0x7f0977813d4b] addr2line.c:179
-- Ruby level backtrace information ----------------------------------------
test.rb:74:in `<main>'
test.rb:34:in `func_py'
test.rb:26:in `sub'
test.rb:32:in `block in func_py'
test.rb:26:in `sub'
test.rb:32:in `block in func_py'
test.rb:26:in `sub'
test.rb:32:in `block in func_py'
test.rb:26:in `sub'
test.rb:32:in `block in func_py'
test.rb:26:in `sub'
test.rb:32:in `block in func_py'
test.rb:26:in `sub'
test.rb:31:in `block in func_py'
test.rb:70:in `hook'
test.rb:70:in `[]='
DEFINE_INSN
invokeblock
(CALL_INFO ci)
(...)
(VALUE val) // inc += 1 - ci->orig_argc;
{
struct rb_calling_info calling;
calling.argc = ci->orig_argc;
calling.block_handler = VM_BLOCK_HANDLER_NONE;
calling.recv = GET_SELF();
val = vm_invoke_block(th, GET_CFP(), &calling, ci);
if (val == Qundef) {
RESTORE_REGS();
NEXT_INSN();
}
頑張って最適化した人がいるから
ruby –dump=insn
すると、yieldはinvokeblockという
命令になっていることがわかる。