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 full-size slide

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

    View full-size slide

  3. Aaron
    Patterson

    View full-size slide

  4. Ruby Core
    Rails Core

    View full-size slide

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

    View full-size slide

  6. Revert Commits
    Count Too!

    View full-size slide

  7. More mistakes ==
    more points!!!!

    View full-size slide

  8. νϡʔνϡʔ

    View full-size slide

  9. Ϗοάσʔλ

    View full-size slide

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

    View full-size slide

  11. Speeding up
    Rails 4.2

    View full-size slide

  12. Rack ͸
    ָ͡Όͳ͍

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. ׬શ࣮ྫ
    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 full-size slide

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

    View full-size slide

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

    View full-size slide

  23. ߟ͑ࣄ͚ͩɻ

    View full-size slide

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

    View full-size slide

  25. Speeding up
    Rails 4.2

    View full-size slide

  26. Adequate$
    Record

    View full-size slide

  27. Performance$
    Tools

    View full-size slide

  28. benchmark/ips

    View full-size slide

  29. 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 full-size slide

  30. ग़ྗ
    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 full-size slide

  31. Set Include:
    3047175.3 / sec

    View full-size slide

  32. Array Include:
    3899.2 / sec

    View full-size slide

  33. IPS:
    Higher Is Better

    View full-size slide

  34. Blackbox Testing

    View full-size slide

  35. Ωϟογϡͷςετ
    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 full-size slide

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

    View full-size slide

  37. 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 full-size slide

  38. ۩ମతͳྫ
    Routes

    View full-size slide

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

    View full-size slide

  40. 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 full-size slide

  41. 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 full-size slide

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

    View full-size slide

  43. ௕͞ΛมԽ
    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 full-size slide

  44. ඵ / 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 full-size slide

  45. Object
    Allocations

    View full-size slide

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

    View full-size slide

  47. ۩ମతͳྫɿ Views

    View full-size slide

  48. 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 full-size slide

  49. 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 full-size slide

  50. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide


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

    View full-size slide

  58. 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 full-size slide

  59. Speeding up
    Helpers

    View full-size slide

  60. Profile
    Request / Response

    View full-size slide

  61. 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 full-size slide

  62. ActiveSupport::SafeBuffer
    #initialize

    View full-size slide

  63. Ͳ͜Ͱ࡞ͬͨͷʁ

    View full-size slide

  64. 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 full-size slide

  65. HTML Sanitization
    in Rails

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  68. ERB::Utils.h

    View full-size slide

  69. 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 full-size slide

  70. Creates 2 Objects.

    View full-size slide

  71. 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 full-size slide

  72. String
    SafeBuffer
    String

    View full-size slide

  73. 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 full-size slide

  74. 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 full-size slide

  75. String
    String

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  78. Speeding up
    Output

    View full-size slide

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

    <%= book.name %>

    <% end %>

    View full-size slide

  80. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  83. Don’t accept nil

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  88. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  91. Eliminate
    Objects

    View full-size slide

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

    View full-size slide

  93. Fewer Types =
    Less Code

    View full-size slide

  94. Less Code =
    Faster Code

    View full-size slide

  95. Measure, measure
    measure measure

    View full-size slide

  96. Rails 4.2 will be
    the fastest ever!

    View full-size slide

  97. ͋Γ͕ͱ͏ʂ

    View full-size slide