高度なコード

 高度なコード

F29327647a9cff5c69618bae420792ea?s=128

Aaron Patterson

September 22, 2014
Tweet

Transcript

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

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

  3. Thanks!!!

  4. Aaron Patterson

  5. Ϩου ϋτ

  6. None
  7. None
  8. Ruby Core Rails Core

  9. ί ϛ ο τ ͠ ͯ! ϙ Π ϯ τ

    ΋ Β ͓ ͏ ʂ
  10. Revert Commits Count Too!

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

  12. ΦΨϫ͞Μ

  13. ΰϒͪΌΜ

  14. νϡʔνϡʔ

  15. None
  16. Ϗοάσʔλ

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

  18. Node.JS

  19. None
  20. Speeding up Rails 4.2

  21. ߟ͑ࣄ

  22. Rack

  23. ΋͏ऴΘΓ

  24. Rack ͸ ָ͡Όͳ͍

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

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

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

  28. ετϦʔϛϯά class Stream def each loop { yield "hello world\n"

    } end end $ def call(env) [200, {}, Stream.new] end R ack 1.x
  29. def call(request, response) end IO IO the_m etal

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

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

  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'
  33. Great for HTTP/1.1 Extendable to HTTP/ 2.0

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

  35. ߟ͑ࣄ͚ͩɻ

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

  37. Speeding up Rails 4.2

  38. Adequate$ Record

  39. Performance$ Tools

  40. benchmark/ips

  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
  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
  43. Set Include: 3047175.3 / sec

  44. Array Include: 3899.2 / sec

  45. IPS: Higher Is Better

  46. Blackbox Testing

  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
  48. ࣮ߦͷάϥϑ 10,000 ܁Γฦ࣌ؒ͢ (seconds) 0.01 0.1 1 10 100 ΩϟογϡɹαΠζ

    10 elements 100 elements 1000 elements 100000 elements Ωϟογϡ 1 Ωϟογϡ 2
  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 ఆ਺ ઢܗ
  50. ۩ମతͳྫ Routes

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

  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
  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
  54. URL ͷ௕͞͸ ࣮ߦ଎౓ʹ ؔ܎͸͋Γ·͔͢ʁ

  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
  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
  57. Object Allocations

  58. GC.stat()

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

  60. ۩ମతͳྫɿ Views

  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"
  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
  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
  64. ςετͷ݁Ռ Request ͷΦϒδΣΫτ഑ஔ 2000 2150 2300 2450 2600 4-0-stable 4-1-stable

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

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

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

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

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

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

    ObjectSpace::AllocationTracer.allocated_count_table
  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}
  72. Speeding up Helpers

  73. Profile Request / Response

  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
  75. ActiveSupport::SafeBuffer #initialize

  76. Ͳ͜Ͱ࡞ͬͨͷʁ

  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
  78. HTML Sanitization in Rails

  79. Ordinary String >> x = "foo" => "foo" >> x.class

    => String >> x.html_safe? => false
  80. SafeBuffer >> x = "foo" => "foo" >> y =

    x.html_safe => "foo" >> y.class => ActiveSupport::SafeBuffer >> y.html_safe? => true
  81. ERB::Utils.h

  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
  83. Creates 2 Objects.

  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
  85. String SafeBuffer String

  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
  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
  88. String String

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

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

  91. Speeding up Output

  92. ERB Template <% books.each do |book| %> <tr><td> <%= book.name

    %> </tr></td> <% end %>
  93. Compiled Template @output_buffer = output_buffer || ActionView::OutputBuffer.new;@output_buffer.safe_append='<h1>Listing books</h1> $ <table>

    <thead> <tr> <th>Name</th> <th colspan="3"></th> </tr> </thead> $ <tbody> '.freeze; @books.each do |book| @output_buffer.safe_append=' <tr> <td>'.freeze;@output_buffer.append=( book.name );@output_buffer.safe_append='</td> <td>'.freeze;@output_buffer.append=( link_to 'Show', book );@output_buffer.safe_append='</ td> </tr> '.freeze; end @output_buffer.safe_append=' </tbody> </table> $ <br>
  94. Compiled Template @output_buffer = ActionView::OutputBuffer.new @output_buffer.safe_append=' <tr> <td>'.freeze @output_buffer.append=( book.name

    ) HTML Literal
  95. safe_append= class OutputBuffer def safe_append=(value) return self if value.nil? super(value.to_s)

    end end W hy?
  96. Don’t accept nil

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

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

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

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

  101. Results

  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
  103. ~19% reduction since 4-0-stable

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

  105. ·ͱΊɻ

  106. Eliminate Objects

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

  108. Limit Types

  109. Fewer Types = Less Code

  110. Less Code = Faster Code

  111. Measure, measure measure measure

  112. MEASURE!

  113. Rails 4.2 will be the fastest ever!

  114. ͋Γ͕ͱ͏ʂ