7. What's the impact of slow sites?
Lower conversions and engagement, higher bounce rates...
Ilya Grigorik @igrigorik
Make The Web Faster, Google
8. Yo ho ho and a few billion pages of RUM
How speed affects bounce rate
@igrigorik
9. Usability Engineering 101
Delay User reaction
0 - 100 ms Instant
100 - 300 ms Feels sluggish
300 - 1000 ms Machine is working...
1 s+ Mental context switch
10 s+ I'll come back later...
Stay under 250
ms to feel "fast".
Stay under 1000
ms to keep users
attention.
@igrigorik
10. Web Search Delay
Experiment
Type of Delay Delay (ms)
Duration
(weeks)
Impact on Avg.
Daily Searches
Pre-header 100 4 -0.20 %
Pre-header 200 6 -0.59%
Post-header 400 6 0.59%
Post-ads 200 4 0.30%
Source: https://www.igvita.com/slides/2012/webperf-crash-course.pdf
11. For many, mobile is the one and only internet
device!
Country Mobile-only users
Egypt 70%
India 59%
South Africa 57%
Indonesia 44%
United States 25%
onDevice Research
@igrigorik
12. < 1.000 ms Page Loading Time on
3G ist der Mount Everest.
13. The (short) life of our 1000 ms budget
3G (200 ms
RTT)
4G(80 ms RTT)
Control plane (200-2500 ms) (50-100 ms)
DNS lookup 200 ms 80 ms
TCP Connection 200 ms 80 ms
TLS handshake (200-400 ms) (80-160 ms)
HTTP request 200 ms 80 ms
Leftover budget 0-400 ms 500-760 ms
Network overhead
of one HTTP
request!
@igrigorik
14. Some WebPerf Problems
can’t be fixed within Phoenix.
If your page initially loads 3
MB of JavaScript it will never
be fast.
23. Latency
clientZeit
0 ms
80 ms
160 ms
240 ms
320 ms
10 TCP Segmente (14.600 Bytes)
20 TCP Segmente (29.200 Bytes)
40 TCP Segmente (15.592 Bytes)
server
SYN
ACK
ACK
GET
SYN,ACK
ACK
ACK
40. $ rails new shop
$ cd shop
$ rails g scaffold Category name
$ rails g scaffold Product category:references
name description
price:decimal{8,2}
$ rails g scaffold User email first_name
last_name password_digest
$ rails g scaffold Review user:references
product:references
rating:integer
$ rails db:migrate
41. Shop domain model
Category
name string
Product
description text
name string
price decimal (8,2)
Review
rating integer
User
email string
first_name string
last_name string
password_digest string
42. app/models/product.rb:
class Product < ApplicationRecord
belongs_to :category
has_many :reviews
def number_of_stars
if reviews.any?
reviews.average(:rating).round
else
nil
end
end
end
43. db/seeds.rb:
Category.create(name: "A")
Category.create(name: "B")
Category.create(name: "C")
100.times do
Product.create(name: Faker::Food.dish,
description: Faker::Food.description,
category: Category.all.sample,
price: rand(20))
end
50.times do
user = User.create(first_name: Faker::Name.first_name,
last_name: Faker::Name.last_name)
products = Product.all
3.times do
Review.create(user: user,
product: products.sample,
rating: rand(5))
end
end
46. Completed 200 OK in
497ms (Views: 460.4ms |
ActiveRecord: 34.1ms)
Development env.
47. The (short) life of our 1000 ms budget
3G (200 ms
RTT)
4G(80 ms RTT)
Control plane (200-2500 ms) (50-100 ms)
DNS lookup 200 ms 80 ms
TCP Connection 200 ms 80 ms
TLS handshake (200-400 ms) (80-160 ms)
HTTP request 200 ms 80 ms
Leftover budget 0-400 ms 500-760 ms
Network overhead
of one HTTP
request!
@igrigorik
497ms
And we don’t have product images yet.
65. app/models/product.rb:
class Product < ApplicationRecord
belongs_to :category
has_many :reviews
def number_of_stars
if reviews.any?
reviews.average(:rating).round
else
nil
end
end
end
66. app/models/product.rb:
class Product < ApplicationRecord
belongs_to :category
has_many :reviews
end
$ rails g migration AddNumberOfStarsToProduct
number_of_stars:integer
$ rails db:migrate
67. app/models/product.rb:
class Review < ApplicationRecord
belongs_to :user
belongs_to :product, touch: true
after_create :recalculate_product_rating
after_destroy :recalculate_product_rating
private
def recalculate_product_rating
rating = product.reviews.average(:rating).round
if rating != self.product.number_of_stars
product.update_attribute(:number_of_stars, rating)
end
end
end
68. Total Views Activerecord
Vanilla 497ms 460 ms 34 ms
Fragment Cache Row 79 ms 74,6 ms 1 ms
Fragment Cache Table 53 ms 49,5 ms 0,6 ms
Plus Database Improvents 40 ms 39 ms 0,5 ms
Production Env. 35 ms 34 ms 0,5 ms
69. Warning:
Fragment Caching is
slower the 1st request!
Why? Rails checks if the Fragment Cache exists.
When it doesn’t it renders the view and writes the cache
which is time consuming.
70. Need More Speed?
You are at the right place! ;-)
Have a look at http://phoenixframework.org
Phoenix takes 5 ms for the same page.
BTW: without caching
78. Set the Etag
class ProductsController <
ApplicationController
# GET /products
def index
@products = Product.all
fresh_when :etag => @products
end
[...]
79. > curl -I http://0.0.0.0:3000/products -c
cookies.txt
HTTP/1.1 200 OK
Etag: "4d348810e69400799e2ab684c0ef4777"
> curl -I http://0.0.0.0:3000/products -b
cookies.txt
HTTP/1.1 200 OK
Etag: "4d348810e69400799e2ab684c0ef4777"
The cookie is needed for the CSRF-Token.
81. Win-Win of a 304
• The Browser doesn’t have to
download everything.
• The Server doesn’t have to
render the view which is the
most time consuming bit.
87. The fastest page is
delivered by Nginx
without ever contacting
Phoenix or Ruby on Rails.
88. ├── Gemfile
├── [...]
├── public
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ ├── favicon.ico
│ └── robots.txt
├── [...]
That‘s already
done for the
files in the
public directory.
89. Add caches_page to your
controller to save views as static
gz files in your public directory:
caches_page :index, :show,
:gzip => :true
Add gem actionpack-page_caching for Rails 5.2
90. Brute Force is your friend!
During the night the server has a
hard time to stay awake any way.
98. 28K * 10,000,000 = 0,26 TB
Harddrive space is cheap.
By saving the files non-gz and using
a data deduplication file system you
just need 5-10% of the 0,26 TB.
Nginx can gzip the files on the fly.
99. Nginx will happily read a
cookie and find the pre-
rendered page in a given
directory structure.
103. Long story short:
In most cases where a CDN made
sense with HTTP/1.1 it doesn’t
make sense any more.
Just deliver everything from your
Rails server!
116. Heroku is good for a quick start
but has never been a good choice
for good WebPerformance. Bare
Metal is the way to go if you need
maximum WebPerformance.
BTW: It’s cheaper too.
117. P R E L O A D I N G U N D
P R E F E T C H I N G
118. P R E L O A D I N G U N D P R E F E T C H I N G
<link rel="dns-prefetch"...
<link rel="prefetch"...
DNS pre-resolution
TCP pre-connect
prefresh
preloader
119. M A N U A L D N S - P R E F E T C H
<link rel="dns-prefetch" href="//abc.com">
http://www.chromium.org/developers/design-documents/dns-prefetching
„Most common names like google.com and yahoo.com are resolved so
often that most local ISP's name resolvers can answer in closer to 80-120ms.
If the domain name in question is an uncommon name, then a query may
have to go through numerous resolvers up and down the hierarchy, and the
delay can average closer to 200-300ms.“
120. P R E F E T C H
<link rel="prefetch" href=„http://abc.com/important.js">
http://www.whatwg.org/specs/web-apps/current-work/#link-type-prefetch
„The prefetch keyword indicates that preemptively fetching and caching the
specified resource is likely to be beneficial, as it is highly likely that the user
will require this resource.“
T I P P : " A C C E P T - R A N G E S : B Y T E S “ H E A D E R
121. Y O U C A N T E L L N G I N X T O
P U S H T H O S E F I L E S V I A H T T P / 2 .