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

CPS & CTO

CPS & CTO

社内LT大会 0x64物語 #08 Functional Programming 資料

HORINOUCHI Masato

May 19, 2016
Tweet

More Decks by HORINOUCHI Masato

Other Decks in Programming

Transcript

  1. Con$nua$on-passing style ͱ͸ ܧଓ౉͠ελΠϧ (CPS: Con)nua)on-passing style) ͱ͸ɺϓϩάϥ Ϝͷ੍ޚΛܧଓΛ༻͍ͯཅʹද͢ϓϩάϥϛϯάελΠϧͷ͜ͱͰ ͋Δɻ

    ܧଓ౉͠ελΠϧͰॻ͔Εͨؔ਺͸ɺ௨ৗͷ௚઀ελΠϧ (direct style) ͷΑ͏ʹ஋Λʮฦ͢ʯ͔ΘΓʹɺʮܧଓʯΛҾ਺ͱͯ͠ཅʹड ͚औΓɺͦͷܧଓʹܭࢉ݁ՌΛ౉͢ɻܧଓͱ͸ɺؔ਺ͷܭࢉ݁ՌΛ ड͚औΔͨΊͷʢҰൠʹ͸ݩͷؔ਺ͱ͸ผͷʣؔ਺ͷ͜ͱͰ͋Δɻ ܧଓ౉͠ελΠϧ - Wikipedia ͔ΒҾ༻
  2. factorial (Scheme / CPS) (define (fact/cps n cont) # ←

    Ҿ਺ cont ͕ܧଓ (if (zero? n) (cont 1) (fact/cps (- n 1) (lambda (x) (cont (* n x)))))) (fact/cps 10 (lambda (x) x))
  3. factorial (Scheme / direct style / tail recursion) (define (fact_tail

    n acc) (if (zero? n) acc (fact_tail (- n 1) (* n acc)))) (fact_tail 10 1)
  4. benchmark result (1,0003mes 1000!) • non tail call • 0.767s

    • cps • 0.854s • tail call • 0.819s • ·͔͞ͷඇ຤ඌݺग़൛͕࠷΋଎͍ɻ • CPS ͸ݺͼग़͠ຖʹ lambda object ੜ੒͢Δ͔Β஗͍ͷ͔ͳɻ
  5. Stack Overflow (Scheme) • ֤ύλʔϯͱ΋ 100000! Ͱ΋ Stack Overflow ͠ͳ͍ɻ

    • Scheme ͸຤ඌݺग़࠷దԽΛߦͳ͏ͷͰ CPS൛ͱ຤ඌݺग़൛͕ overflow ͠ͳ͍ͷ͸Θ͔Δɻ • ඇ຤ඌݺग़൛Ͱ΋ overflow ͠ͳ͍ͷ͸ɺCPSม׵Λࣗಈతʹߦ ͳ͍ͬͯΔͷͩΖ͏͔?
  6. factorial (Ruby / direct style / recursion) def fact(n) if

    n == 0 1 else n * fact(n - 1) end end fact(10)
  7. factorial (Ruby / CPS) def fact_cps(n, cont) if n ==

    0 cont.call 1 else fact_cps(n - 1, -> (x) { cont.call(n * x) }) end end fact_cps(10, -> (x) { x })
  8. factorial (Ruby / direct style / tail recursion) def fact_tail(n,

    acc = 1) if n == 0 acc else fact_tail(n - 1, n * acc) end end fact_tail(10)
  9. benchmark TIMES = 1000 FACT = 1000 Benchmark.bm 16 do

    |r| r.report 'non tail call' do TIMES.times do fact(FACT) end end r.report 'cps' do TIMES.times do fact_cps(FACT, -> (x) { x }) end end r.report 'tail call' do TIMES.times do fact_tail(FACT) end end end
  10. benchmark result (1,0003mes 1000!) user system total real non tail

    call 0.570000 0.010000 0.580000 ( 0.575446) cps 1.220000 0.060000 1.280000 ( 1.280935) tail call 0.650000 0.060000 0.710000 ( 0.705097) • Scheme൛ͱಉ༷ͷ܏޲ʹͳͬͨɻ
  11. Stack Overflow (Ruby) • 5000! • 3ύλʔϯͱ΋໰୊ͳ͠ • 10000! •

    CPS൛͕ stack level too deep • 11000! • 3ύλʔϯͱ΋ stack level too deep
  12. Tail Call Op)miza)on ͱ͸ ຤ඌݺग़͠࠷దԽͱ͸ɺ຤ඌݺग़͠ͷίʔυΛɺ໭ΓઌΛอଘ͠ͳ͍δϟϯ ϓʹม׵͢Δ͜ͱʹΑͬͯɺελοΫͷྦྷੵΛແ͘͠ɺޮ཰ͷ޲্ͳͲΛਤ Δख๏Ͱ͋Δɻ ཧ࿦తʹ͸ɺܧଓ౉͠ʹΑΔ goto ͱಉ౳ͷδϟϯϓॲཧͰ͸ɺखଓ͖ͷݺग़

    ͠ݩͷ৘ใΛอଘ͢Δඞཁ͕ͳ͍͜ͱʹؼண͢Δɻ͜ͷ৔߹ return ͸ goto ͷಛघͳέʔεͰɺδϟϯϓઌ͕ݺग़͠ݩʹ౳͍͠έʔεͰ͋Δɻ຤ඌ࠷దԽ ͕͋Ε͹ɺखଓ͖ͷ࠶ؼΛߦͳ͏࣌Ͱ΋ɺ݁Ռ͸ϧʔϓͱ౳Ձͳॲཧखॱͱ ͳΓɺͲΕ΄Ͳਂ͍࠶ؼΛߦͳͬͯ΋ελοΫΦʔόʔϑϩʔΛى͜͞ͳ͍ɻ ຤ඌ࠶ؼ - Wikipedia ͔ΒҾ༻
  13. Tail Call Op)miza)on in Ruby • YARV ͩͱΦϓγϣϯͰ TCO ༗ޮʹͰ͖Δɻ

    RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true, trace_instruction: false }
  14. benchmark result (1,0003mes 1000! / TCO) user system total real

    non tail call 0.570000 0.010000 0.580000 ( 0.575446) cps 1.220000 0.060000 1.280000 ( 1.280935) tail call 0.650000 0.060000 0.710000 ( 0.705097) ↓ TCO on user system total real non tail call 0.560000 0.020000 0.580000 ( 0.578095) cps 1.130000 0.050000 1.180000 ( 1.183501) tail call 0.640000 0.040000 0.680000 ( 0.688400) • CPS Ͱ໿ 8%վળɺ຤ඌݺग़Ͱ 4%վળ
  15. Stack Overflow (Ruby / TCO) • 10000! • 3ύλʔϯͱ΋໰୊ͳ͠ •

    11000! • ඇ຤ඌݺग़൛͕ stack level too deep • 12000! • CPS൛͕ stack level too deep ← ͳͥ? • 1000000! ·Ͱࢼ͕ͨ͠ɺ຤ඌݺग़൛Ͱ͸ Stack Overflow ͠ͳ͍ (10෼͔͔ͬͨ)
  16. TCO Problems • (1) backtrace: Elimina3ng method frame means elimina3ng

    backtrace. • (2) settracefunc(): It is difficult to probe "return" event for tail-call methods. • (3) seman3cs: It is difficult to define tail-call in document (half is joking, but half is serious) Tail call op)miza)on: enable by default? ͔ΒҾ༻
  17. ·ͱΊ • CPS ʹஔ͖׵͑Δ͚ͩͰ͸଎͘ͳΒͳ͍ɻ • YARV ͩͱ TCO ༗ޮԽ͕Ͱ͖Δɻ •

    TCO ʹؔ܎ͳ͘ 1.upto(FACT).inject(:*) ͩͱ Stack Overflow ͠ͳ͍ͷ͸ώϛπɻ