Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

東急Ruby会議向け「rubyの細かい話」

1,725 views

Published on

東急Ruby会議向けの小噺です。
会議中に生まれた

Published in: Technology
  • Be the first to comment

東急Ruby会議向け「rubyの細かい話」

  1. 1. z 細かいrubyの 話?(仮) @taru 樽家昌也
  2. 2. z 最近読み始めた本 ▪ なんかRubyと関係ある人が訳 者にはいっているらしいよ ▪ 追記型データ構造面白いよ! ▪ 最初は二分木を用いたソートと かから・・・
  3. 3. z 準備フェーズがあると のような、関数内関数定義したくなりますよね・・・ ソートといえば def exist?(a,tree) return false if tree.empty? left,node,right=tree if (node<a) then return exist?(a,left) if (node>a) then return exist?(a,right) return node==a 単純には みたいな再帰構造だが。。。。 func x = f x init_state where f a st = hogehogehoge…
  4. 4. z Rubyで書こうとすると def func(a) def sub(a,st) hogehogehoge… end sub(a,init_state) end × 関数定義はスコープに閉じてくれない def func(a) sub = proc { …} sub.call(a,init_state) end procを使うとかけるが….
  5. 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. 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しろ! (特に再帰するなら)
  7. 7. z 第一部完 ご清聴ありがとうございました procはcallせずにyieldしろ!
  8. 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のマシンスタックが沢山積み重なる
  9. 9. z 細かい話その2 def callbug require 'fiddle' Fiddle::Pointer.new(4)[0]=0 end Cのマシンスタックを簡単に取得する たった一つじゃない簡単な方法 これで callbug を呼ぶだけ!
  10. 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という 命令になっていることがわかる。
  11. 11. z まとめ def callbug require 'fiddle' Fiddle::Pointer.new(4)[0]=0 end def proc_call (*args) yield *args end ruby –dump=insn Ruby3x3までにはなんとか・・・

×