Exception `LoadError' at rubygems.rb:1194 -
cannot load such file -- rubygems/defaults/
operating_system
!
Exception `LoadError' at kernel_require.rb:55
- cannot load such file -- bundler
Slide 82
Slide 82 text
Find Swallowed
Exceptions!
Slide 83
Slide 83 text
Rack
Slide 84
Slide 84 text
def call(env)
end
LOL Global Data
Slide 85
Slide 85 text
There will be no
Rack 2.0*
* There will be a Rack 2.0
Slide 86
Slide 86 text
Ruby >= 2.0?
Slide 87
Slide 87 text
Next Generation
Slide 88
Slide 88 text
def call(env, input, output)
end
IO
IO
Slide 89
Slide 89 text
Just Thoughts.
Slide 90
Slide 90 text
Performance
Tools
Slide 91
Slide 91 text
Raw Performance
Slide 92
Slide 92 text
benchmark/ips
Slide 93
Slide 93 text
benchmark
Benchmark.bm do |x|
x.report('some test') {
N.times { some_test }
}
end
How big?
STD
LIB
Slide 94
Slide 94 text
Output
user system total real
some test 0.000000 0.000000 0.000000 ( 0.000098)
Slide 95
Slide 95 text
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
Slide 96
Slide 96 text
Output
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
Slide 97
Slide 97 text
Set Include:
3047175.3 / sec
Slide 98
Slide 98 text
Array Include:
3899.2 / sec
Slide 99
Slide 99 text
IPS:
Higher Is Better
Slide 100
Slide 100 text
Output
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
STDDEV
Slide 101
Slide 101 text
benchmark
N = 100000
!
list = ('a'..'zzz').to_a
hash = list.each_with_object({}) { |x,h| h[x] = true }
set = Set.new list
!
Benchmark.bm do |x|
x.report("set access") {
N.times { set.include? "foo" }
}
!
x.report("hash access") {
N.times { hash.include? "foo" }
}
end
STD
LIB
Slide 102
Slide 102 text
Output
user system total real
set access 0.030000 0.000000 0.030000 ( 0.030044)
hash access 0.030000 0.000000 0.030000 ( 0.032125)
Set Is Faster?
benchm
ark
Slide 103
Slide 103 text
benchmark/ips
list = ('a'..'zzz').to_a
hash = list.each_with_object({}) { |x,h|
h[x] = true
}
set = Set.new list
!
Benchmark.ips do |x|
x.report("set access") { set.include? "foo" }
x.report("hash access") { hash.include? "foo" }
end
G
EM
Slide 104
Slide 104 text
Output
Calculating -------------------------------------
set access 73910 i/100ms
hash access 73845 i/100ms
-------------------------------------------------
set access 3081455.6 (±7.6%) i/s - 15299370 in 4.999343s
hash access 3772358.3 (±7.2%) i/s - 18756630 in 5.004747s
benchm
ark/ips
Runtime Graph
Time for 10,000 iterations (seconds)
0.01
0.1
1
10
100
Cache Size
10 elements 100 elements 1000 elements 100000 elements
Cache 1
Cache 2
Slide 111
Slide 111 text
Cache Implementation
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
Constant
Linear
Slide 112
Slide 112 text
Real World
Example: Routes
Slide 113
Slide 113 text
Number Of Routes
class MyTest
routes = ActionDispatch::Routing::RouteSet.new
routes.draw {
resources(:articles)
}
end
!
article = Article.new.tap(&:save!)
!
Benchmark.ips do |x|
x.report("link_to") { test.link_to "zomg", article }
end
Slide 114
Slide 114 text
Add 10 routes
class MyTest
routes = ActionDispatch::Routing::RouteSet.new
routes.draw {
resources(:articles)
10.times do |num|
resources num.to_s.to_sym
end
}
end
Slide 115
Slide 115 text
Add 100 routes
class MyTest
routes = ActionDispatch::Routing::RouteSet.new
routes.draw {
resources(:articles)
100.times do |num|
resources num.to_s.to_sym
end
}
end
Slide 116
Slide 116 text
Add 1000 routes
class MyTest
routes = ActionDispatch::Routing::RouteSet.new
routes.draw {
resources(:articles)
1000.times do |num|
resources num.to_s.to_sym
end
}
end
Total allocations
GC.stat(:total_allocated_object)
Slide 130
Slide 130 text
Measure Allocations
Person.find id
before = GC.stat(:total_allocated_object)
N.times { Person.find id }
after = GC.stat(:total_allocated_object)
puts (after - before) / N
W
arm
up
Benchmark
Objects / Call
Count O
bjs
C
ount O
bjs
Slide 131
Slide 131 text
Real World
Example: Views
Slide 132
Slide 132 text
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"
Slide 133
Slide 133 text
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
Slide 134
Slide 134 text
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
Slide 135
Slide 135 text
Test Results
Object Allocations Per Request
2000
2150
2300
2450
2600
4-0-stable 4-1-stable master
2000
Slide 136
Slide 136 text
HOW TO LIE
WITH GRAPHS
Slide 137
Slide 137 text
Test Results
Object Allocations Per Request
0
650
1300
1950
2600
4-0-stable 4-1-stable master
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
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
Slide 162
Slide 162 text
Creates 2 Strings.
Slide 163
Slide 163 text
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
Slide 164
Slide 164 text
String -> String ->
SafeBuffer -> String
Slide 165
Slide 165 text
String -> String ->
String
Slide 166
Slide 166 text
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
Slide 167
Slide 167 text
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
Slide 168
Slide 168 text
String -> String ->
String
Slide 169
Slide 169 text
~200 Allocations
Per Request
for /books/new
Slide 170
Slide 170 text
Request Benchmark
task :allocation_tracer do
app = Ko1TestApp::Application.instance
app.app
do_test_task(app)
env = rackenv "/books/new"
do_test_task(app, env.dup)
ObjectSpace::AllocationTracer.trace do
TEST_CNT.times {
do_test_task(app, env.dup)
}
end
p ObjectSpace::AllocationTracer.allocated_count_table
end
"/books/new"
Slide 171
Slide 171 text
Request Benchmark
task :allocation_tracer do
app = Ko1TestApp::Application.instance
app.app
do_test_task(app)
env = rackenv "/books/new"
do_test_task(app, env.dup)
ObjectSpace::AllocationTracer.trace do
TEST_CNT.times {
do_test_task(app, env.dup)
}
end
p ObjectSpace::AllocationTracer.allocated_count_table
end
Slide 172
Slide 172 text
Request Benchmark
task :allocation_tracer do
app = Ko1TestApp::Application.instance
app.app
do_test_task(app)
env = rackenv "/books/new"
do_test_task(app, env.dup)
ObjectSpace::AllocationTracer.trace do
TEST_CNT.times {
do_test_task(app, env.dup)
}
end
p ObjectSpace::AllocationTracer.allocated_count_table
end
Slide 173
Slide 173 text
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