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
1.3k
13
Share
高度なコード
Aaron Patterson
September 22, 2014
More Decks by Aaron Patterson
See All by Aaron Patterson
RubyKaigi 2025: Class New, A New Approach
tenderlove
0
240
RubyKaigi Dev Meeting 2025
tenderlove
1
4.8k
Speeding up Instance Variables in Ruby 3.3
tenderlove
2
530
[Feature #20425] Speeding up delegate methods
tenderlove
3
340
RailsConf 2023
tenderlove
30
1.4k
Don't @ Me! Faster Instance Variables with Object Shapes
tenderlove
1
530
RailsConf 2022 Keynote
tenderlove
2
660
Some Assembly Required
tenderlove
1
620
HexDevs 2021
tenderlove
1
550
Other Decks in Technology
See All in Technology
AWS認定資格は本当に意味があるのか?
nrinetcom
PRO
1
250
Azure Speech で音声対応してみよう
kosmosebi
0
140
Rapid Start: Faster Internet Connections, with Ruby's Help
kazuho
2
130
Master Dataグループ紹介資料
sansan33
PRO
1
4.6k
こんなアーキテクチャ図はいやだ / Anti-pattern in AWS Architecture Diagrams
naospon
1
390
JOAI2026講評会資料(近藤佐介)
element138
1
150
EBS暗号化に失敗してEC2が動かなくなった話
hamaguchimmm
1
140
猫でもわかるKiro CLI(CDKコーディング編)
kentapapa
1
120
インフラを Excel 管理していた組織が 3 ヶ月で IaC 化されるまで
geekplus_tech
3
200
え!?初参加で 300冊以上 も頒布!? これは大成功!そのはずなのに わいの財布は 赤字 の件
hellohazime
0
150
AI時代にデータ基盤が持つべきCapabilityを考える + Snowflake Data Superheroやっていき宣言 / Considering the Capabilities Data Platforms Should Have in the AI Era + Declaration of Commitment as a Snowflake Data Superhero
civitaspo
0
110
名刺メーカーDevグループ 紹介資料
sansan33
PRO
0
1.1k
Featured
See All Featured
Being A Developer After 40
akosma
91
590k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
320
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.7k
Designing for Timeless Needs
cassininazir
0
190
The Illustrated Children's Guide to Kubernetes
chrisshort
51
52k
Ten Tips & Tricks for a 🌱 transition
stuffmc
0
98
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
110
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
250
The untapped power of vector embeddings
frankvandijk
2
1.7k
The Spectacular Lies of Maps
axbom
PRO
1
690
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
0
200
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!
͋Γ͕ͱ͏ʂ