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.4k
13
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
高度なコード
Aaron Patterson
September 22, 2014
More Decks by Aaron Patterson
See All by Aaron Patterson
Faster FFI for Ruby
tenderlove
0
74
RubyKaigi 2025: Class New, A New Approach
tenderlove
0
280
RubyKaigi Dev Meeting 2025
tenderlove
1
6.2k
Speeding up Instance Variables in Ruby 3.3
tenderlove
2
600
[Feature #20425] Speeding up delegate methods
tenderlove
3
380
RailsConf 2023
tenderlove
30
1.5k
Don't @ Me! Faster Instance Variables with Object Shapes
tenderlove
1
580
RailsConf 2022 Keynote
tenderlove
2
700
Some Assembly Required
tenderlove
1
660
Other Decks in Technology
See All in Technology
Genie Code ワークショップ 基礎編 / Genie-Code-Workshop-fundamental
databricksjapan
PRO
0
340
【試作】IoT x AIエージェント
happysamurai294
0
100
「重なり」は迎える側がつくる ― 人もAIエージェントも歓迎するプロダクトエンジニアリング ―
go0517go
PRO
0
220
AI-DLCって実際どう? 〜聞きたいこと全部聞いてみる〜
news_it_enj
0
220
IDperturb: Enhancing Variation in Synthetic Face Generation via Angular Perturbation
sansantech
PRO
0
130
[DroidKaigi 2026] Making UI specifications visible: Android UI development in the AI agent era supported by Compose Screenshot Testing and galleries
syarihu
0
540
多摩川(.dev)ランニング入門 / Tamagawa.dev#3
fujiwara3
3
330
DINO-EdgeQuery:Edge-First Polygon Decoding for Building Footprint Extraction from Satellite Imagery
lehupa
0
140
Bet AI Day 2026丨Agentは、「金融」という巨大産業の何を変えられるのか
layerx
PRO
0
710
AIで開発は速くなったのに、なぜ現場は楽にならないのか 〜あなたの組織のボトルネックを突き止めるワークショップ〜
jacopen
1
250
[RSJ26] AnoleVLA: Lightweight Vision-Language-Action Model with Deep State Space Models for Mobile Manipulation
keio_smilab
PRO
0
120
Nav2、Nav3 ... はたまた自作?〜 作って理解する Nav3 の設計意図 〜 / Nav2, Nav3 ... or Build Your Own? — Understanding Nav3's design intent by building it from scratch
yanzm
0
200
Featured
See All Featured
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
Deep Space Network (abreviated)
tonyrice
0
290
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
370
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
Between Models and Reality
mayunak
4
450
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
65k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
240
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
490
Prompt Engineering for Job Search
mfonobong
0
430
YesSQL, Process and Tooling at Scale
rocio
174
15k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
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!
͋Γ͕ͱ͏ʂ