Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
高度なコード
Search
Aaron Patterson
September 22, 2014
Technology
13
1.2k
高度なコード
Aaron Patterson
September 22, 2014
Tweet
Share
More Decks by Aaron Patterson
See All by Aaron Patterson
Speeding up Instance Variables in Ruby 3.3
tenderlove
1
260
[Feature #20425] Speeding up delegate methods
tenderlove
3
170
RailsConf 2023
tenderlove
29
900
Don't @ Me! Faster Instance Variables with Object Shapes
tenderlove
1
370
RailsConf 2022 Keynote
tenderlove
2
470
Some Assembly Required
tenderlove
1
490
HexDevs 2021
tenderlove
1
360
Compacting GC for MRI
tenderlove
60
4.4k
But At What Cost?
tenderlove
9
14k
Other Decks in Technology
See All in Technology
サイバーセキュリティと認知バイアス:対策の隙を埋める心理学的アプローチ
shumei_ito
0
370
[CV勉強会@関東 ECCV2024 読み会] オンラインマッピング x トラッキング MapTracker: Tracking with Strided Memory Fusion for Consistent Vector HD Mapping (Chen+, ECCV24)
abemii
0
190
メールサーバ管理者のみ知る話
hinono
1
110
ドメイン名の終活について - JPAAWG 7th -
mikit
32
19k
Platform Engineering for Software Developers and Architects
syntasso
1
470
2024年グライダー曲技世界選手権参加報告/2024 WGAC report
jscseminar
0
330
テストコード品質を高めるためにMutation Testingライブラリ・Strykerを実戦導入してみた話
ysknsid25
6
2.3k
元旅行会社の情シス部員が教えるおすすめなre:Inventへの行き方 / What is the most efficient way to re:Invent
naospon
2
320
私はこうやってマインドマップでテストすることを出す!
mineo_matsuya
0
340
Going down the RAT hole: Deep dive into the Vuln-derland of APT-class RAT Tools
nttcom
0
440
RubyのWebアプリケーションを50倍速くする方法 / How to Make a Ruby Web Application 50 Times Faster
hogelog
2
900
[FOSS4G 2024 Japan LT] LLMを使ってGISデータ解析を自動化したい!
nssv
1
190
Featured
See All Featured
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
31
2.7k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
42
9.2k
Art, The Web, and Tiny UX
lynnandtonic
297
20k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.4k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
169
50k
Producing Creativity
orderedlist
PRO
341
39k
Gamification - CAS2011
davidbonilla
80
5k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Music & Morning Musume
bryan
46
6.2k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
Transcript
( ͡ ° ͜ ʖ ͡ °) .oO(Hi)
ຖ߃ྫ$ ͷ$ ຊޠೳྗࢼݧ
Thanks!!!
Aaron Patterson
Ϩου ϋτ
None
None
Ruby Core Rails Core
ί ϛ ο τ ͠ ͯ! ϙ Π ϯ τ
Β ͓ ͏ ʂ
Revert Commits Count Too!
More mistakes == more points!!!!
ΦΨϫ͞Μ
ΰϒͪΌΜ
νϡʔνϡʔ
None
Ϗοάσʔλ
ϝλϧʹ$ ۙ͘ͳΔͨΊʹ
Node.JS
None
Speeding up Rails 4.2
ߟ͑ࣄ
Rack
͏ऴΘΓ
Rack ָ͡Όͳ͍
Rack 2.0 ൃද͠ͳ͍* * Rack 2.0 ൃද͠·͢ɻ
the_metal http://github.com/tenderlove/the_metal
def call(env) end άϩʔόϧม R ack 1.x
ετϦʔϛϯά class Stream def each loop { yield "hello world\n"
} end end $ def call(env) [200, {}, Stream.new] end R ack 1.x
def call(request, response) end IO IO the_m etal
request IOͱ ϦΩΣετͷใ! Λ͍࣋ͬͯ·͢ɻ
responseIOͱ Ϩεϙϯεͷใ! Λ͍࣋ͬͯ·͢ɻ
શ࣮ྫ 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'
Great for HTTP/1.1 Extendable to HTTP/ 2.0
RackΛͬͯ ָΛ͠·͠ΐ͏
ߟ͑ࣄ͚ͩɻ
ಡΜͰԼ͍͞ʂ http://github.com/tenderlove/the_metal
Speeding up Rails 4.2
Adequate$ Record
Performance$ Tools
benchmark/ips
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
ग़ྗ 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
Set Include: 3047175.3 / sec
Array Include: 3899.2 / sec
IPS: Higher Is Better
Blackbox Testing
Ωϟογϡͷςετ 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
࣮ߦͷάϥϑ 10,000 ܁Γฦ࣌ؒ͢ (seconds) 0.01 0.1 1 10 100 ΩϟογϡɹαΠζ
10 elements 100 elements 1000 elements 100000 elements Ωϟογϡ 1 Ωϟογϡ 2
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 ఆ ઢܗ
۩ମతͳྫ Routes
Route දͷେ͖͞ link_to ʹؔ͋Δʁ
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
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
URL ͷ͞ ࣮ߦʹ ؔ͋Γ·͔͢ʁ
͞ΛมԽ 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
ඵ / 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
Object Allocations
GC.stat()
߹ܭͷׂΓͯ GC.stat(:total_allocated_object)
۩ମతͳྫɿ Views
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"
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
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
ςετͷ݁Ռ Request ͷΦϒδΣΫτஔ 2000 2150 2300 2450 2600 4-0-stable 4-1-stable
master 2000
Ͳ͏ͬͯ$ ӕΛ͔ͭ͘
ςετͷ݁Ռ Request ͷΦϒδΣΫτஔ 0 650 1300 1950 2600 4-0-stable 4-1-stable
master
4-0-stable ͔Β ~19%ݮΓ·ͨ͠
4-1-stable ͔Β ~14%ݮΓ·ͨ͠
allocation_tracer https://github.com/ko1/allocation_tracer
ྫ ObjectSpace::AllocationTracer.trace do 1000.times { ["foo", {}] } end $
ObjectSpace::AllocationTracer.allocated_count_table
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}
Speeding up Helpers
Profile Request / Response
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
ActiveSupport::SafeBuffer #initialize
Ͳ͜Ͱ࡞ͬͨͷʁ
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
HTML Sanitization in Rails
Ordinary String >> x = "foo" => "foo" >> x.class
=> String >> x.html_safe? => false
SafeBuffer >> x = "foo" => "foo" >> y =
x.html_safe => "foo" >> y.class => ActiveSupport::SafeBuffer >> y.html_safe? => true
ERB::Utils.h
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
Creates 2 Objects.
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
String SafeBuffer String
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
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
String String
~200 Allocations Per Request for /books/new
YMMV (Your Mileage (kilometers?) May Vary)
Speeding up Output
ERB Template <% books.each do |book| %> <tr><td> <%= book.name
%> </tr></td> <% end %>
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>
Compiled Template @output_buffer = ActionView::OutputBuffer.new @output_buffer.safe_append=' <tr> <td>'.freeze @output_buffer.append=( book.name
) HTML Literal
safe_append= class OutputBuffer def safe_append=(value) return self if value.nil? super(value.to_s)
end end W hy?
Don’t accept nil
safe_append= class OutputBuffer def safe_append=(value) super(value.to_s) end end
safe_append= class OutputBuffer def safe_append=(value) super(value) end end
safe_append= class OutputBuffer def safe_append=(value) super end end
safe_append= class OutputBuffer end ߴͳίʔυʂ
Results
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
~19% reduction since 4-0-stable
~14% reduction since 4-1-stable
·ͱΊɻ
Eliminate Objects
Ұ൪͍ίʔυ ଘࡏ͠ͳ͍ίʔυ
Limit Types
Fewer Types = Less Code
Less Code = Faster Code
Measure, measure measure measure
MEASURE!
Rails 4.2 will be the fastest ever!
͋Γ͕ͱ͏ʂ