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

高度なコード

Aaron Patterson
September 22, 2014

 高度なコード

Aaron Patterson

September 22, 2014
Tweet

More Decks by Aaron Patterson

Other Decks in Technology

Transcript

  1. ( ͡
    ° ͜
    ʖ ͡
    °) .oO(Hi)

    View Slide

  2. ຖ೥߃ྫ$
    ͷ$
    ೔ຊޠೳྗࢼݧ

    View Slide

  3. Thanks!!!

    View Slide

  4. Aaron
    Patterson

    View Slide

  5. Ϩου ϋτ

    View Slide

  6. View Slide

  7. View Slide

  8. Ruby Core
    Rails Core

    View Slide

  9. ί
    ϛ
    ο
    τ
    ͠
    ͯ!
    ϙ
    Π
    ϯ
    τ
    ΋
    Β
    ͓
    ͏
    ʂ

    View Slide

  10. Revert Commits
    Count Too!

    View Slide

  11. More mistakes ==
    more points!!!!

    View Slide

  12. ΦΨϫ͞Μ

    View Slide

  13. ΰϒͪΌΜ

    View Slide

  14. νϡʔνϡʔ

    View Slide

  15. View Slide

  16. Ϗοάσʔλ

    View Slide

  17. ϝλϧʹ$
    ۙ͘ͳΔͨΊʹ

    View Slide

  18. Node.JS

    View Slide

  19. View Slide

  20. Speeding up
    Rails 4.2

    View Slide

  21. ߟ͑ࣄ

    View Slide

  22. Rack

    View Slide

  23. ΋͏ऴΘΓ

    View Slide

  24. Rack ͸
    ָ͡Όͳ͍

    View Slide

  25. Rack 2.0
    ͸ൃද͠ͳ͍*
    * Rack 2.0 ͸ൃද͠·͢ɻ

    View Slide

  26. the_metal
    http://github.com/tenderlove/the_metal

    View Slide

  27. def call(env)
    end
    άϩʔόϧม਺
    R
    ack
    1.x

    View Slide

  28. ετϦʔϛϯά
    class Stream
    def each
    loop { yield "hello world\n" }
    end
    end
    $
    def call(env)
    [200, {}, Stream.new]
    end
    R
    ack
    1.x

    View Slide

  29. def call(request, response)
    end
    IO
    IO
    the_m
    etal

    View Slide

  30. request ͸IOͱ
    ϦΩΣετͷ৘ใ!
    Λ͍࣋ͬͯ·͢ɻ

    View Slide

  31. response͸IOͱ
    Ϩεϙϯεͷ৘ใ!
    Λ͍࣋ͬͯ·͢ɻ

    View Slide

  32. ׬શ࣮ྫ
    TheMetal.create_server(->(req, res) {
    res.write_head 200, 'Content-Type' => 'text/plain'
    res.write "Hello World\n"
    res.finish
    }).listen 9292, '0.0.0.0'

    View Slide

  33. Great for HTTP/1.1
    Extendable to HTTP/
    2.0

    View Slide

  34. RackΛ࢖ͬͯ
    ָΛ͠·͠ΐ͏

    View Slide

  35. ߟ͑ࣄ͚ͩɻ

    View Slide

  36. ಡΜͰԼ͍͞ʂ
    http://github.com/tenderlove/the_metal

    View Slide

  37. Speeding up
    Rails 4.2

    View Slide

  38. Adequate$
    Record

    View Slide

  39. Performance$
    Tools

    View Slide

  40. benchmark/ips

    View Slide

  41. benchmark/ips
    require 'benchmark/ips'
    require 'set'
    $
    list = ('a'..'zzzz').to_a
    set = Set.new list
    $
    Benchmark.ips do |x|
    x.report("set access") { set.include? "foo" }
    $
    x.report("ary access") { list.include? "foo" }
    end
    G
    EM

    View Slide

  42. ग़ྗ
    Calculating -------------------------------------
    set access 68622 i/100ms
    ary access 395 i/100ms
    -------------------------------------------------
    set access 3047175.3 (±12.7%) i/s - 14959596 in 5.018692s
    ary access 3899.2 (±7.1%) i/s - 19750 in 5.096118s
    ߹
    ܭ
    IPS

    View Slide

  43. Set Include:
    3047175.3 / sec

    View Slide

  44. Array Include:
    3899.2 / sec

    View Slide

  45. IPS:
    Higher Is Better

    View Slide

  46. Blackbox Testing

    View Slide

  47. Ωϟογϡͷςετ
    cache1 = Cache1.new
    cache2 = Cache2.new
    $
    cache1["x"] = Object.new
    cache2["x"] = Object.new
    $
    Benchmark.ips do |x|
    x.report("cache1") { cache1["x"] }
    x.report("cache2") { cache2["x"] }
    end

    View Slide

  48. ࣮ߦͷάϥϑ
    10,000 ܁Γฦ࣌ؒ͢ (seconds)
    0.01
    0.1
    1
    10
    100
    ΩϟογϡɹαΠζ
    10 elements 100 elements 1000 elements 100000 elements
    Ωϟογϡ 1
    Ωϟογϡ 2

    View Slide

  49. Cacheͷ࣮૷
    class Cache1
    def initialize
    @cache = {}
    end
    def [] k; @cache[k]; end
    def []= k,v; @cache[k] = v; end
    end
    $
    class Cache2
    def initialize
    @cache = []
    end
    def [] k; x, = @cache.assoc(k); x; end
    def []= k,v; @cache << [k, v]; end
    end
    ఆ਺
    ઢܗ

    View Slide

  50. ۩ମతͳྫ
    Routes

    View Slide

  51. Route දͷେ͖͞͸
    link_to ʹؔ܎͸͋Δʁ

    View Slide

  52. route ΛೖΕΔ
    class MyTest
    routes = ActionDispatch::Routing::RouteSet.new
    routes.draw {
    resources(:articles)
    N.times do |num|
    resources num.to_s.to_sym
    end
    }
    end
    10, 100, 1000

    View Slide

  53. Sec /100k calls
    9.5
    9.7
    9.9
    10.1
    10.3
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
    link_to

    View Slide

  54. URL ͷ௕͞͸
    ࣮ߦ଎౓ʹ
    ؔ܎͸͋Γ·͔͢ʁ

    View Slide

  55. ௕͞ΛมԽ
    class MyTest
    routes = ActionDispatch::Routing::RouteSet.new
    link = N.times.map(&:to_s).join '/'
    $
    routes.draw {
    get "/#{link}/:id", :as => :article,
    :controller => :articles,
    :action => :show
    }
    end
    10, 100, 1000

    View Slide

  56. ඵ / 10ສճͷݺͼग़͠
    9
    10.5
    12
    13.5
    15
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
    link_to

    View Slide

  57. Object
    Allocations

    View Slide

  58. GC.stat()

    View Slide

  59. ߹ܭͷׂΓ౰ͯ
    GC.stat(:total_allocated_object)

    View Slide

  60. ۩ମతͳྫɿ Views

    View Slide

  61. Request Benchmark
    task :allocated_objects do
    app = Ko1TestApp::Application.instance
    app.app
    do_test_task(app)
    env = rackenv "/books/new"
    do_test_task(app, env.dup)
    before = GC.stat :total_allocated_object
    TEST_CNT.times {
    do_test_task(app, env.dup)
    }
    after = GC.stat :total_allocated_object
    puts (after - before) / TEST_CNT
    end
    "/books/new"

    View Slide

  62. Request Benchmark
    task :allocated_objects do
    app = Ko1TestApp::Application.instance
    app.app
    do_test_task(app)
    env = rackenv "/books/new"
    do_test_task(app, env.dup)
    before = GC.stat :total_allocated_object
    TEST_CNT.times {
    do_test_task(app, env.dup)
    }
    after = GC.stat :total_allocated_object
    puts (after - before) / TEST_CNT
    end

    View Slide

  63. Request Benchmark
    task :allocated_objects do
    app = Ko1TestApp::Application.instance
    app.app
    do_test_task(app)
    env = rackenv "/books/new"
    do_test_task(app, env.dup)
    before = GC.stat :total_allocated_object
    TEST_CNT.times {
    do_test_task(app, env.dup)
    }
    after = GC.stat :total_allocated_object
    puts (after - before) / TEST_CNT
    end

    View Slide

  64. ςετͷ݁Ռ
    Request ͷΦϒδΣΫτ഑ஔ
    2000
    2150
    2300
    2450
    2600
    4-0-stable 4-1-stable master
    2000

    View Slide

  65. Ͳ͏΍ͬͯ$
    ӕΛ͔ͭ͘

    View Slide

  66. ςετͷ݁Ռ
    Request ͷΦϒδΣΫτ഑ஔ
    0
    650
    1300
    1950
    2600
    4-0-stable 4-1-stable master

    View Slide

  67. 4-0-stable ͔Β
    ~19%ݮΓ·ͨ͠

    View Slide

  68. 4-1-stable ͔Β
    ~14%ݮΓ·ͨ͠

    View Slide

  69. allocation_tracer
    https://github.com/ko1/allocation_tracer

    View Slide


  70. ObjectSpace::AllocationTracer.trace do
    1000.times {
    ["foo", {}]
    }
    end
    $
    ObjectSpace::AllocationTracer.allocated_count_table

    View Slide

  71. Output
    {:T_NONE=>0,
    :T_OBJECT=>0,
    :T_CLASS=>0,
    :T_MODULE=>0,
    :T_FLOAT=>0,
    :T_STRING=>1000,
    :T_REGEXP=>0,
    :T_ARRAY=>1000,
    :T_HASH=>1000,
    :T_ZOMBIE=>0}

    View Slide

  72. Speeding up
    Helpers

    View Slide

  73. Profile
    Request / Response

    View Slide

  74. TOTAL (pct) SAMPLES (pct) FRAME
    813 (9.5%) 813 (9.5%) ActiveSupport::SafeBuffer#initialize
    699 (8.1%) 350 (4.1%) block in ActiveRecord::Read#read_attribute
    486 (5.7%) 298 (3.5%) ActionController::UrlFor#url_options
    670 (7.8%) 274 (3.2%) ActionDispatch::Journey::Format#evaluate
    773 (9.0%) 253 (2.9%) ActionDispatch#parameterize_args
    1172 (13.6%) 220 (2.6%) ActiveRecord::Persistence#instantiate
    213 (2.5%) 213 (2.5%) block in SQLite3::Statement#each
    208 (2.4%) 208 (2.4%) ActiveSupport::SafeBuffer#html_safe?
    204 (2.4%) 204 (2.4%) ActionDispatch::UrlFor#routes_generation?
    245 (2.9%) 191 (2.2%) block (2 levels) in Class#class_attribute

    View Slide

  75. ActiveSupport::SafeBuffer
    #initialize

    View Slide

  76. Ͳ͜Ͱ࡞ͬͨͷʁ

    View Slide

  77. Tag Options
    def tag_option(key, value, escape)
    if value.is_a?(Array)
    value = escape ?
    safe_join(value, " ") :
    value.join(" ")
    else
    value = escape ? ERB::Util.h(value) : value
    end
    %(#{key}="#{value}")
    end

    View Slide

  78. HTML Sanitization
    in Rails

    View Slide

  79. Ordinary String
    >> x = "foo"
    => "foo"
    >> x.class
    => String
    >> x.html_safe?
    => false

    View Slide

  80. SafeBuffer
    >> x = "foo"
    => "foo"
    >> y = x.html_safe
    => "foo"
    >> y.class
    => ActiveSupport::SafeBuffer
    >> y.html_safe?
    => true

    View Slide

  81. ERB::Utils.h

    View Slide

  82. ERB::Utils.h
    def html_escape(s)
    s = s.to_s
    if s.html_safe?
    s
    else
    s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE).html_safe
    end
    end

    View Slide

  83. Creates 2 Objects.

    View Slide

  84. Tag Options
    def tag_option(key, value, escape)
    if value.is_a?(Array)
    value = escape ?
    safe_join(value, " ") :
    value.join(" ")
    else
    value = escape ? ERB::Util.h(value) : value
    end
    %(#{key}="#{value}")
    end

    View Slide

  85. String
    SafeBuffer
    String

    View Slide

  86. Extract Method
    def unwrapped_html_escape(s) # :nodoc:
    s = s.to_s
    if s.html_safe?
    s
    else
    s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE)
    end
    end
    $
    def html_escape(s)
    unwrapped_html_escape(s).html_safe
    end

    View Slide

  87. Update Callers
    def tag_option(key, value, escape)
    if value.is_a?(Array)
    value = escape ?
    safe_join(value, " ") :
    value.join(" ")
    else
    value = escape ?
    ERB::Util.unwrapped_html_escape(value) :
    value
    end
    %(#{key}="#{value}")
    end

    View Slide

  88. String
    String

    View Slide

  89. ~200 Allocations
    Per Request
    for /books/new

    View Slide

  90. YMMV
    (Your Mileage (kilometers?) May Vary)

    View Slide

  91. Speeding up
    Output

    View Slide

  92. ERB Template
    <% books.each do |book| %>

    <%= book.name %>

    <% end %>

    View Slide

  93. Compiled Template
    @output_buffer = output_buffer ||
    ActionView::OutputBuffer.new;@output_buffer.safe_append='Listing books
    $



    Name



    $

    '.freeze; @books.each do |book|
    @output_buffer.safe_append='
    '.freeze;@output_buffer.append=( book.name );@output_buffer.safe_append='
    '.freeze;@output_buffer.append=( link_to 'Show', book );@output_buffer.safe_append='
    td>

    '.freeze; end
    @output_buffer.safe_append='

    $


    View Slide

  94. Compiled Template
    @output_buffer = ActionView::OutputBuffer.new
    @output_buffer.safe_append='
    '.freeze
    @output_buffer.append=( book.name )
    HTML
    Literal

    View Slide

  95. safe_append=
    class OutputBuffer
    def safe_append=(value)
    return self if value.nil?
    super(value.to_s)
    end
    end
    W
    hy?

    View Slide

  96. Don’t accept nil

    View Slide

  97. safe_append=
    class OutputBuffer
    def safe_append=(value)
    super(value.to_s)
    end
    end

    View Slide

  98. safe_append=
    class OutputBuffer
    def safe_append=(value)
    super(value)
    end
    end

    View Slide

  99. safe_append=
    class OutputBuffer
    def safe_append=(value)
    super
    end
    end

    View Slide

  100. safe_append=
    class OutputBuffer
    end
    ߴ౓ͳίʔυʂ

    View Slide

  101. Results

    View Slide

  102. Allocations Per Request
    0
    275
    550
    825
    1100
    T_STRING T_ARRAY T_HASH T_NODE T_DATA OTHER
    4-0-stable 4-1-stable master

    View Slide

  103. ~19% reduction
    since 4-0-stable

    View Slide

  104. ~14% reduction
    since 4-1-stable

    View Slide

  105. ·ͱΊɻ

    View Slide

  106. Eliminate
    Objects

    View Slide

  107. Ұ൪଎͍ίʔυ͸
    ଘࡏ͠ͳ͍ίʔυ

    View Slide

  108. Limit Types

    View Slide

  109. Fewer Types =
    Less Code

    View Slide

  110. Less Code =
    Faster Code

    View Slide

  111. Measure, measure
    measure measure

    View Slide

  112. MEASURE!

    View Slide

  113. Rails 4.2 will be
    the fastest ever!

    View Slide

  114. ͋Γ͕ͱ͏ʂ

    View Slide