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.3k
高度なコード
Aaron Patterson
September 22, 2014
Tweet
Share
More Decks by Aaron Patterson
See All by Aaron Patterson
RubyKaigi 2025: Class New, A New Approach
tenderlove
0
38
RubyKaigi Dev Meeting 2025
tenderlove
1
2.5k
Speeding up Instance Variables in Ruby 3.3
tenderlove
2
410
[Feature #20425] Speeding up delegate methods
tenderlove
3
260
RailsConf 2023
tenderlove
30
1.1k
Don't @ Me! Faster Instance Variables with Object Shapes
tenderlove
1
450
RailsConf 2022 Keynote
tenderlove
2
550
Some Assembly Required
tenderlove
1
570
HexDevs 2021
tenderlove
1
460
Other Decks in Technology
See All in Technology
新卒から4年間、20年もののWebサービスと向き合って学んだソフトウェア考古学 - PHPカンファレンス新潟2025 / new graduate 4year software archeology
oguri
2
360
Bill One 開発エンジニア 紹介資料
sansan33
PRO
4
12k
やさしいClaude Code入門
minorun365
PRO
32
24k
Data Hubグループ 紹介資料
sansan33
PRO
0
1.7k
カンファレンスのつくりかた / The Conference Code: What Makes It All Work
tomzoh
8
930
研究開発部メンバーの働き⽅ / Sansan R&D Profile
sansan33
PRO
3
17k
Introduction to Bill One Development Engineer
sansan33
PRO
0
240
大事なのは、AIの精度だけじゃない!〜1円のズレも許されない経理領域とAI〜
jun_nemoto
11
5.2k
継続戦闘能⼒
sansantech
PRO
0
220
実践Kafka Streams 〜イベント駆動型アーキテクチャを添えて〜
joker1007
0
350
SmartHRの複数のチームにおけるMCPサーバーの活用事例と課題
yukisnow1823
2
1.2k
“⾞が通れるほど⼤きな”セキュリティーホールを抑えながらログインしたい
taiseiue
0
160
Featured
See All Featured
Raft: Consensus for Rubyists
vanstee
137
7k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
5
620
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
52
2.8k
Building a Scalable Design System with Sketch
lauravandoore
462
33k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.8k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
19
1.3k
Measuring & Analyzing Core Web Vitals
bluesmoon
7
460
Mobile First: as difficult as doing things right
swwweet
223
9.6k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
840
Bash Introduction
62gerente
614
210k
A Tale of Four Properties
chriscoyier
159
23k
How to train your dragon (web standard)
notwaldorf
92
6k
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!
͋Γ͕ͱ͏ʂ