SlideShare uma empresa Scribd logo
1 de 127
Baixar para ler offline
WebPerformance
Why and How
Stefan Wintermeyer (@wintermeyer)
Two Hearts in
One Chest
What websites do not
have to care about
WebPerformance?
next-weeks-lottery-numbers.com
If you run the only page with this information!
What websites DO
have to care about
WebPerformance?
last-weeks-lottery-numbers.com
Because there are a lot of similar pages.
What's the impact of slow sites?
Lower conversions and engagement, higher bounce rates...
Ilya Grigorik @igrigorik
Make The Web Faster, Google
Yo ho ho and a few billion pages of RUM
How speed affects bounce rate
@igrigorik
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
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
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
< 1.000 ms Page Loading Time on
3G ist der Mount Everest.
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
Some WebPerf Problems
can’t be fixed within Phoenix.
If your page initially loads 3
MB of JavaScript it will never
be fast.
Webpage Rendering
Basics
Network
HTML
DOM
CSS
CSSOM JavaScript
Render tree
Layout
Paint
Network
HTML
DOM
CSS
CSSOM JavaScript
Render tree
Layout
Paint
Network
HTML
DOM
CSS
CSSOM JavaScript
Render tree
Layout
Paint
Network
HTML
DOM
CSS
CSSOM JavaScript
Render tree
Layout
Paint
Network
HTML
DOM
CSS
CSSOM JavaScript
Render tree
Layout
Paint
This rendering process
takes a minimum of 100 ms
which we have to subtract
from the 1,000 ms.
Download a file with
HTTP 1.1 over TCP
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
TCP Slow-Start
KB
0
55
110
165
220
Roundtrip
1. 2. 3. 4.
214KB
100KB
43KB
14KB
114KB
57KB
29KB
14KB
HTTP 2 you can parallel
download multiple files over
the same TCP connection.
Plus better header compression. Plus push, …
More on that later.
Waterfall
www.webpagetest.org
https://rubyconference.by
3G Run => https://www.webpagetest.org/result/
180412_BK_4b5755e7d0717d0f218a88edf5a691a9/
LTE Run => https://www.webpagetest.org/result/
180412_6W_0697a100d965b385b53917b77d51d081/
LTE run => https://www.webpagetest.org/result/
180316_5B_de436fb724593d7b746b9c1f89ff3c2d/
Can I interest you in a
Rails caching example?
It’s so much better in Phoenixland but we can learn from it.
Example Online Shop
$ 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
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
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
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
app/views/products/index.html.erb:
<table class="table table-striped">
[…]
<tbody>
<% @products.each do |product| %>
<tr>
<td><%= product.category.name %></td>
<td><%= product.name %></td>
<td><%= product.description %></td>
<td><%= number_to_currency(product.price) %></td>
<td>
<% product.number_of_stars.to_i.times do %>
<img src="<%= asset_path( 'star.svg' ) %>“ />
<% end %>
</td>
[…]
</tr>
<% end %>
</tbody>
</table>
Completed 200 OK in
497ms (Views: 460.4ms |
ActiveRecord: 34.1ms)
Development env.
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.
Development environment?
Are you crazy?!



Hold your horses!
I’ll switch to production
environment later.
Fragment Caching
$ rails dev:cache
Development mode is now being cached.
By default caching is disabled in dev env.
1. Step
Single Row
app/views/products/index.html.erb:
<tbody>
<% @products.each do |product| %>
<% cache product do %>
<tr>
<td><%= product.category.name %></td>
<td><%= product.name %></td>
<td><%= product.description %></td>
<td><%= number_to_currency(product.price) %></td>
<td>
<% product.number_of_stars.to_i.times do %>
<img src="<%= asset_path( 'star.svg' ) %>" />
<% end %>
</td>
[...]
</tr>
<% end %>
<% end %>
</tbody>
app/views/products/index.html.erb:
<tbody>
<% @products.each do |product| %>
<% cache product do %>
<tr>
<td><%= product.category.name %></td>
<td><%= product.name %></td>
<td><%= product.description %></td>
<td><%= number_to_currency(product.price) %></td>
<td>
<% product.number_of_stars.to_i.times do %>
<img src="<%= asset_path( 'star.svg' ) %>" />
<% end %>
</td>
[...]
</tr>
<% end %>
<% end %>
</tbody>
app/models/review.rb:
class Review < ApplicationRecord
belongs_to :user
belongs_to :product, touch: true
end
Product
description text
name string
price decimal (8,2)
Review
rating integer
User
email string
first_name string
last_name string
password_digest string
Total Views Activerecord
Vanilla 497ms 460 ms 34 ms
Fragment Cache Row 79 ms 74,6 ms 1 ms
2. Step
The Complete Table
=> Russian Doll
app/views/products/index.html.erb:
<tbody>
<% cache @products do %>
<% @products.each do |product| %>
<% cache product do %>
<tr>
<td><%= product.category.name %></td>
<td><%= product.name %></td>
<td><%= product.description %></td>
<td><%= number_to_currency(product.price) %></td>
<td>
<% product.number_of_stars.to_i.times do %>
<img src="<%= asset_path( 'star.svg' ) %>" />
<% end %>
</td>
[…]
</tr>
<% end %>
<% end %>
<% end %>
</tbody>
app/views/products/index.html.erb:
<tbody>
<% cache @products do %>
<% @products.each do |product| %>
<% cache product do %>
<tr>
<td><%= product.category.name %></td>
<td><%= product.name %></td>
<td><%= product.description %></td>
<td><%= number_to_currency(product.price) %></td>
<td>
<% product.number_of_stars.to_i.times do %>
<img src="<%= asset_path( 'star.svg' ) %>" />
<% end %>
</td>
[…]
</tr>
<% end %>
<% end %>
<% end %>
</tbody>
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
Use the Database!
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
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
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
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
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.
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
HTTP Caching
Web browsers and proxies
don‘t want to fetch identical
resources multiple times.
The idea of Etags and
Last-Modified
Web browser:
„My user wants to fetch xyz.html.
I cached a copy last week.
Is that still good?“
Web server:
„xyz.html hasn‘t changed since
last week.
Go a head with your copy!“
aka 304 Not Modified
> curl -I http://0.0.0.0:3000/products
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Ua-Compatible: IE=Edge
Etag: "9a779b80e4b0ac3c60d29807e302deb7"
[...]
> curl -I http://0.0.0.0:3000/products
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Ua-Compatible: IE=Edge
Etag: "fa8fc1e981833a6885b583d351c4d823"
> curl -I http://0.0.0.0:3000/products
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Ua-Compatible: IE=Edge
Etag: "9a779b80e4b0ac3c60d29807e302deb7"
[...]
> curl -I http://0.0.0.0:3000/products
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Ua-Compatible: IE=Edge
Etag: "fa8fc1e981833a6885b583d351c4d823"
Set the Etag
class ProductsController <
ApplicationController
# GET /products
def index
@products = Product.all
fresh_when :etag => @products
end
[...]

> 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.
> curl -I http://0.0.0.0:3000/products -b
cookies.txt --header 'If-None-Match:
"4d348810e69400799e2ab684c0ef4777"'
HTTP/1.1 304 Not Modified
Etag: "4d348810e69400799e2ab684c0ef4777"
304!
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.
Not good enough?
Writing the initial cache
wastes a lot of time.
Let‘s preheat the cache
in off business hours!
Cron is your friend.
Use the night to
preheat your cache.
And don‘t be afraid
of brute force!
A U T O B A H N
The fastest page is
delivered by Nginx
without ever contacting
Phoenix or Ruby on Rails.
├── Gemfile
├── [...]
├── public
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   ├── favicon.ico
│   └── robots.txt
├── [...]
That‘s already
done for the
files in the
public directory.
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
Brute Force is your friend!
During the night the server has a
hard time to stay awake any way.
Tricky part:
How to delete out of date
gz files?
after_update :expire_cache
before_destroy :expire_cache
private
def expire_cache
ActionController::Base.expire_page(Rails.application.routes.url_h
elpers.company_path(self))
ActionController::Base.expire_page(Rails.application.routes.url_h
elpers.companies_path)
end
app/models/product.rb
caches_page
vs.
!current_user.nil?
???
caches_page is good to cache
customized user content too.
It just takes more thinking.
Let us assume a user base
of 10,000,000 people.
/tmp ᐅ wget http://www.railsconf.com/2013/talks
--2013-04-27 21:04:24-- http://www.railsconf.com/2013/talks
Resolving www.railsconf.com... 107.20.162.205
Connecting to www.railsconf.com|107.20.162.205|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘talks’
[ <=> ] 74,321 258KB/
s in 0.3s
2013-04-27 21:04:25 (258 KB/s) - ‘talks’ saved [74321]
/tmp ᐅ du -hs talks
76K talks
/tmp ᐅ gzip talks
/tmp ᐅ du -hs talks.gz
28K talks.gz
/tmp ᐅ
/tmp ᐅ du -hs talks.gz
28K talks.gz
28K * 10,000,000 = 0,26 TB
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.
Nginx will happily read a
cookie and find the pre-
rendered page in a given
directory structure.
HTTP/1.1 vs. HTTP/2
HTTP/2 provides an average
WebPerformance improvement
of 20%.
It’s a no-brainer.
You have to use it!
CDNs, HTTP/2 and Rails 5.2
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!
Image Formats
It is still an often underused way of saving bandwidth.
JPEG, PNG or WebP
https://caniuse.com/#feat=webp
CSS
Less CSS = Faster Webpages
Do not underestimate the WebPerformance impact of
optimized CSS.
Webfonts
If possible don’t use
Webfonts.
Apache vs. Nginx
It doesn’t matter!
Just use your favorite
one.
Brotli vs. gzip
Always offer both!
Brotli can be used to
save bandwidth and
CPU-Resources.
Heroku vs. Bare Metal
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.
P R E L O A D I N G U N D
P R E F E T C H I N G
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
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.“
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
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 .
The Most
Important Tool?
Set a Time Budget!
If you run out of your time budget you have to
cancel features on your website.
Is WebPerformance really so hard?
The WebPerf Bible. => https://hpbn.co
@wintermeyer
sw@wintermeyer-consulting.de
last name | twitter | github
e-mail

Mais conteúdo relacionado

Mais procurados

Mais procurados (20)

Real World Web Standards
Real World Web StandardsReal World Web Standards
Real World Web Standards
 
Its timetostopstalling mot_paris
Its timetostopstalling mot_parisIts timetostopstalling mot_paris
Its timetostopstalling mot_paris
 
DevFest Makerere html5 presentation by caesar mukama
DevFest Makerere html5 presentation by caesar mukamaDevFest Makerere html5 presentation by caesar mukama
DevFest Makerere html5 presentation by caesar mukama
 
Migrating existing monolith to serverless in 8 steps
Migrating existing monolith to serverless in 8 stepsMigrating existing monolith to serverless in 8 steps
Migrating existing monolith to serverless in 8 steps
 
AD1387: Outside The Box: Integrating with Non-Domino Apps using XPages and Ja...
AD1387: Outside The Box: Integrating with Non-Domino Apps using XPages and Ja...AD1387: Outside The Box: Integrating with Non-Domino Apps using XPages and Ja...
AD1387: Outside The Box: Integrating with Non-Domino Apps using XPages and Ja...
 
Plop
PlopPlop
Plop
 
High Performance Websites By Souders Steve
High Performance Websites By Souders SteveHigh Performance Websites By Souders Steve
High Performance Websites By Souders Steve
 
High Performance Web Sites
High Performance Web SitesHigh Performance Web Sites
High Performance Web Sites
 
Don't make me wait! or Building High-Performance Web Applications
Don't make me wait! or Building High-Performance Web ApplicationsDon't make me wait! or Building High-Performance Web Applications
Don't make me wait! or Building High-Performance Web Applications
 
Its timetostopstalling swp_munich
Its timetostopstalling swp_munichIts timetostopstalling swp_munich
Its timetostopstalling swp_munich
 
Performance Implications of Mobile Design
Performance Implications of Mobile DesignPerformance Implications of Mobile Design
Performance Implications of Mobile Design
 
Its timetostopstalling gdgdusseldorf
Its timetostopstalling gdgdusseldorfIts timetostopstalling gdgdusseldorf
Its timetostopstalling gdgdusseldorf
 
AD113 Speed Up Your Applications w/ Nginx and PageSpeed
AD113  Speed Up Your Applications w/ Nginx and PageSpeedAD113  Speed Up Your Applications w/ Nginx and PageSpeed
AD113 Speed Up Your Applications w/ Nginx and PageSpeed
 
Page Performance
Page PerformancePage Performance
Page Performance
 
Building Faster Websites
Building Faster WebsitesBuilding Faster Websites
Building Faster Websites
 
Mobile App Performance, Dublin MOT
Mobile App Performance, Dublin MOTMobile App Performance, Dublin MOT
Mobile App Performance, Dublin MOT
 
High Performance JavaScript - WebDirections USA 2010
High Performance JavaScript - WebDirections USA 2010High Performance JavaScript - WebDirections USA 2010
High Performance JavaScript - WebDirections USA 2010
 
Patterns and practices for building resilient serverless applications
Patterns and practices for building resilient serverless applicationsPatterns and practices for building resilient serverless applications
Patterns and practices for building resilient serverless applications
 
What is HTML 5?
What is HTML 5?What is HTML 5?
What is HTML 5?
 
Build Better Responsive websites. Hrvoje Jurišić
Build Better Responsive websites. Hrvoje JurišićBuild Better Responsive websites. Hrvoje Jurišić
Build Better Responsive websites. Hrvoje Jurišić
 

Semelhante a WebPerformance: Why and How? – Stefan Wintermeyer

Raiders of the Fast Start: Frontend Performance Archaeology PerfmattersConf 2018
Raiders of the Fast Start: Frontend Performance Archaeology PerfmattersConf 2018Raiders of the Fast Start: Frontend Performance Archaeology PerfmattersConf 2018
Raiders of the Fast Start: Frontend Performance Archaeology PerfmattersConf 2018
Katie Sylor-Miller
 
Tips for a Faster Website
Tips for a Faster WebsiteTips for a Faster Website
Tips for a Faster Website
Rayed Alrashed
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperf
New Relic
 
Capacity Management from Flickr
Capacity Management from FlickrCapacity Management from Flickr
Capacity Management from Flickr
xlight
 
Performance Optimization of Rails Applications
Performance Optimization of Rails ApplicationsPerformance Optimization of Rails Applications
Performance Optimization of Rails Applications
Serge Smetana
 
Socket applications
Socket applicationsSocket applications
Socket applications
João Moura
 
Magento performancenbs
Magento performancenbsMagento performancenbs
Magento performancenbs
varien
 

Semelhante a WebPerformance: Why and How? – Stefan Wintermeyer (20)

Stress Test as a Culture
Stress Test as a CultureStress Test as a Culture
Stress Test as a Culture
 
Raiders of the Fast Start: Frontend Performance Archaeology PerfmattersConf 2018
Raiders of the Fast Start: Frontend Performance Archaeology PerfmattersConf 2018Raiders of the Fast Start: Frontend Performance Archaeology PerfmattersConf 2018
Raiders of the Fast Start: Frontend Performance Archaeology PerfmattersConf 2018
 
MeasureWorks - Why people hate to wait for your website to load (and how to f...
MeasureWorks - Why people hate to wait for your website to load (and how to f...MeasureWorks - Why people hate to wait for your website to load (and how to f...
MeasureWorks - Why people hate to wait for your website to load (and how to f...
 
Profilling client performance
Profilling client performanceProfilling client performance
Profilling client performance
 
Tips for a Faster Website
Tips for a Faster WebsiteTips for a Faster Website
Tips for a Faster Website
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperf
 
Capacity Management from Flickr
Capacity Management from FlickrCapacity Management from Flickr
Capacity Management from Flickr
 
Jon Arne Sæterås - Give Responsive Design a mobile performance boost
Jon Arne Sæterås - Give Responsive Design a mobile performance boost Jon Arne Sæterås - Give Responsive Design a mobile performance boost
Jon Arne Sæterås - Give Responsive Design a mobile performance boost
 
Ruby on Rails Performance Tuning. Make it faster, make it better (WindyCityRa...
Ruby on Rails Performance Tuning. Make it faster, make it better (WindyCityRa...Ruby on Rails Performance Tuning. Make it faster, make it better (WindyCityRa...
Ruby on Rails Performance Tuning. Make it faster, make it better (WindyCityRa...
 
Windy cityrails performance_tuning
Windy cityrails performance_tuningWindy cityrails performance_tuning
Windy cityrails performance_tuning
 
Performance Optimization of Rails Applications
Performance Optimization of Rails ApplicationsPerformance Optimization of Rails Applications
Performance Optimization of Rails Applications
 
Web Performance, Scalability, and Testing Techniques - Boston PHP Meetup
Web Performance, Scalability, and Testing Techniques - Boston PHP MeetupWeb Performance, Scalability, and Testing Techniques - Boston PHP Meetup
Web Performance, Scalability, and Testing Techniques - Boston PHP Meetup
 
Speed is Essential for a Great Web Experience (Canvas Conf Version)
Speed is Essential for a Great Web Experience (Canvas Conf Version)Speed is Essential for a Great Web Experience (Canvas Conf Version)
Speed is Essential for a Great Web Experience (Canvas Conf Version)
 
Web performance optimization
Web performance optimizationWeb performance optimization
Web performance optimization
 
Socket applications
Socket applicationsSocket applications
Socket applications
 
腾讯大讲堂09 如何建设高性能网站
腾讯大讲堂09 如何建设高性能网站腾讯大讲堂09 如何建设高性能网站
腾讯大讲堂09 如何建设高性能网站
 
Profiling PHP with Xdebug / Webgrind
Profiling PHP with Xdebug / WebgrindProfiling PHP with Xdebug / Webgrind
Profiling PHP with Xdebug / Webgrind
 
6 tips for improving ruby performance
6 tips for improving ruby performance6 tips for improving ruby performance
6 tips for improving ruby performance
 
Magento performancenbs
Magento performancenbsMagento performancenbs
Magento performancenbs
 
3 Tips to Deliver Fast Performance Across Mobile Web
3 Tips to Deliver Fast Performance Across Mobile Web3 Tips to Deliver Fast Performance Across Mobile Web
3 Tips to Deliver Fast Performance Across Mobile Web
 

Mais de Elixir Club

Mais de Elixir Club (20)

Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club UkraineKubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
Kubernetes + Docker + Elixir - Alexei Sholik, Andrew Dryga | Elixir Club Ukraine
 
Integrating 3rd parties with Ecto - Eduardo Aguilera | Elixir Club Ukraine
Integrating 3rd parties with Ecto -  Eduardo Aguilera | Elixir Club UkraineIntegrating 3rd parties with Ecto -  Eduardo Aguilera | Elixir Club Ukraine
Integrating 3rd parties with Ecto - Eduardo Aguilera | Elixir Club Ukraine
 
— An async template - Oleksandr Khokhlov | Elixir Club Ukraine
— An async template  -  Oleksandr Khokhlov | Elixir Club Ukraine— An async template  -  Oleksandr Khokhlov | Elixir Club Ukraine
— An async template - Oleksandr Khokhlov | Elixir Club Ukraine
 
BEAM architecture handbook - Andrea Leopardi | Elixir Club Ukraine
BEAM architecture handbook - Andrea Leopardi  | Elixir Club UkraineBEAM architecture handbook - Andrea Leopardi  | Elixir Club Ukraine
BEAM architecture handbook - Andrea Leopardi | Elixir Club Ukraine
 
You ain't gonna need write a GenServer - Ulisses Almeida | Elixir Club Ukraine
You ain't gonna need write a GenServer - Ulisses Almeida  | Elixir Club UkraineYou ain't gonna need write a GenServer - Ulisses Almeida  | Elixir Club Ukraine
You ain't gonna need write a GenServer - Ulisses Almeida | Elixir Club Ukraine
 
— Knock, knock — An async templates — Who’s there? - Alexander Khokhlov | ...
 — Knock, knock — An async templates — Who’s there? - Alexander Khokhlov  |  ... — Knock, knock — An async templates — Who’s there? - Alexander Khokhlov  |  ...
— Knock, knock — An async templates — Who’s there? - Alexander Khokhlov | ...
 
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
Performance measurement methodology — Maksym Pugach | Elixir Evening Club 3
 
Erlang cluster. How is it? Production experience. — Valerii Vasylkov | Elixi...
Erlang cluster. How is it? Production experience. —  Valerii Vasylkov | Elixi...Erlang cluster. How is it? Production experience. —  Valerii Vasylkov | Elixi...
Erlang cluster. How is it? Production experience. — Valerii Vasylkov | Elixi...
 
Promo Phx4RailsDevs - Volodya Sveredyuk
Promo Phx4RailsDevs - Volodya SveredyukPromo Phx4RailsDevs - Volodya Sveredyuk
Promo Phx4RailsDevs - Volodya Sveredyuk
 
Web of today — Alexander Khokhlov
Web of today —  Alexander KhokhlovWeb of today —  Alexander Khokhlov
Web of today — Alexander Khokhlov
 
ElixirConf Eu 2018, what was it like? – Eugene Pirogov
ElixirConf Eu 2018, what was it like? – Eugene PirogovElixirConf Eu 2018, what was it like? – Eugene Pirogov
ElixirConf Eu 2018, what was it like? – Eugene Pirogov
 
Implementing GraphQL API in Elixir – Victor Deryagin
Implementing GraphQL API in Elixir – Victor DeryaginImplementing GraphQL API in Elixir – Victor Deryagin
Implementing GraphQL API in Elixir – Victor Deryagin
 
GenServer in Action – Yurii Bodarev
GenServer in Action – Yurii Bodarev   GenServer in Action – Yurii Bodarev
GenServer in Action – Yurii Bodarev
 
Russian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
Russian Doll Paradox: Elixir Web without Phoenix - Alex RozumiiRussian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
Russian Doll Paradox: Elixir Web without Phoenix - Alex Rozumii
 
Practical Fault Tolerance in Elixir - Alexei Sholik
Practical Fault Tolerance in Elixir - Alexei SholikPractical Fault Tolerance in Elixir - Alexei Sholik
Practical Fault Tolerance in Elixir - Alexei Sholik
 
Phoenix and beyond: Things we do with Elixir - Alexander Khokhlov
Phoenix and beyond: Things we do with Elixir - Alexander KhokhlovPhoenix and beyond: Things we do with Elixir - Alexander Khokhlov
Phoenix and beyond: Things we do with Elixir - Alexander Khokhlov
 
Monads are just monoids in the category of endofunctors - Ike Kurghinyan
Monads are just monoids in the category of endofunctors - Ike KurghinyanMonads are just monoids in the category of endofunctors - Ike Kurghinyan
Monads are just monoids in the category of endofunctors - Ike Kurghinyan
 
Craft effective API with GraphQL and Absinthe - Ihor Katkov
Craft effective API with GraphQL and Absinthe - Ihor KatkovCraft effective API with GraphQL and Absinthe - Ihor Katkov
Craft effective API with GraphQL and Absinthe - Ihor Katkov
 
Elixir in a service of government - Alex Troush
Elixir in a service of government - Alex TroushElixir in a service of government - Alex Troush
Elixir in a service of government - Alex Troush
 
Pattern matching in Elixir by example - Alexander Khokhlov
Pattern matching in Elixir by example - Alexander KhokhlovPattern matching in Elixir by example - Alexander Khokhlov
Pattern matching in Elixir by example - Alexander Khokhlov
 

Último

Último (20)

A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 

WebPerformance: Why and How? – Stefan Wintermeyer

  • 1. WebPerformance Why and How Stefan Wintermeyer (@wintermeyer)
  • 3. What websites do not have to care about WebPerformance?
  • 4. next-weeks-lottery-numbers.com If you run the only page with this information!
  • 5. What websites DO have to care about WebPerformance?
  • 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.
  • 21. This rendering process takes a minimum of 100 ms which we have to subtract from the 1,000 ms.
  • 22. Download a file with HTTP 1.1 over TCP
  • 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
  • 24. TCP Slow-Start KB 0 55 110 165 220 Roundtrip 1. 2. 3. 4. 214KB 100KB 43KB 14KB 114KB 57KB 29KB 14KB
  • 25. HTTP 2 you can parallel download multiple files over the same TCP connection. Plus better header compression. Plus push, … More on that later.
  • 27. 3G Run => https://www.webpagetest.org/result/ 180412_BK_4b5755e7d0717d0f218a88edf5a691a9/
  • 28.
  • 29.
  • 30.
  • 31. LTE Run => https://www.webpagetest.org/result/ 180412_6W_0697a100d965b385b53917b77d51d081/
  • 32.
  • 33.
  • 34. LTE run => https://www.webpagetest.org/result/ 180316_5B_de436fb724593d7b746b9c1f89ff3c2d/
  • 35.
  • 36.
  • 37.
  • 38. Can I interest you in a Rails caching example? It’s so much better in Phoenixland but we can learn from it.
  • 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
  • 44. app/views/products/index.html.erb: <table class="table table-striped"> […] <tbody> <% @products.each do |product| %> <tr> <td><%= product.category.name %></td> <td><%= product.name %></td> <td><%= product.description %></td> <td><%= number_to_currency(product.price) %></td> <td> <% product.number_of_stars.to_i.times do %> <img src="<%= asset_path( 'star.svg' ) %>“ /> <% end %> </td> […] </tr> <% end %> </tbody> </table>
  • 45.
  • 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.
  • 48. Development environment? Are you crazy?!
 
 Hold your horses! I’ll switch to production environment later.
  • 50. $ rails dev:cache Development mode is now being cached. By default caching is disabled in dev env.
  • 52.
  • 53.
  • 54. app/views/products/index.html.erb: <tbody> <% @products.each do |product| %> <% cache product do %> <tr> <td><%= product.category.name %></td> <td><%= product.name %></td> <td><%= product.description %></td> <td><%= number_to_currency(product.price) %></td> <td> <% product.number_of_stars.to_i.times do %> <img src="<%= asset_path( 'star.svg' ) %>" /> <% end %> </td> [...] </tr> <% end %> <% end %> </tbody>
  • 55. app/views/products/index.html.erb: <tbody> <% @products.each do |product| %> <% cache product do %> <tr> <td><%= product.category.name %></td> <td><%= product.name %></td> <td><%= product.description %></td> <td><%= number_to_currency(product.price) %></td> <td> <% product.number_of_stars.to_i.times do %> <img src="<%= asset_path( 'star.svg' ) %>" /> <% end %> </td> [...] </tr> <% end %> <% end %> </tbody>
  • 56. app/models/review.rb: class Review < ApplicationRecord belongs_to :user belongs_to :product, touch: true end Product description text name string price decimal (8,2) Review rating integer User email string first_name string last_name string password_digest string
  • 57. Total Views Activerecord Vanilla 497ms 460 ms 34 ms Fragment Cache Row 79 ms 74,6 ms 1 ms
  • 58. 2. Step The Complete Table => Russian Doll
  • 59.
  • 60.
  • 61. app/views/products/index.html.erb: <tbody> <% cache @products do %> <% @products.each do |product| %> <% cache product do %> <tr> <td><%= product.category.name %></td> <td><%= product.name %></td> <td><%= product.description %></td> <td><%= number_to_currency(product.price) %></td> <td> <% product.number_of_stars.to_i.times do %> <img src="<%= asset_path( 'star.svg' ) %>" /> <% end %> </td> […] </tr> <% end %> <% end %> <% end %> </tbody>
  • 62. app/views/products/index.html.erb: <tbody> <% cache @products do %> <% @products.each do |product| %> <% cache product do %> <tr> <td><%= product.category.name %></td> <td><%= product.name %></td> <td><%= product.description %></td> <td><%= number_to_currency(product.price) %></td> <td> <% product.number_of_stars.to_i.times do %> <img src="<%= asset_path( 'star.svg' ) %>" /> <% end %> </td> […] </tr> <% end %> <% end %> <% end %> </tbody>
  • 63. 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
  • 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
  • 72. Web browsers and proxies don‘t want to fetch identical resources multiple times.
  • 73. The idea of Etags and Last-Modified
  • 74. Web browser: „My user wants to fetch xyz.html. I cached a copy last week. Is that still good?“
  • 75. Web server: „xyz.html hasn‘t changed since last week. Go a head with your copy!“ aka 304 Not Modified
  • 76. > curl -I http://0.0.0.0:3000/products HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 X-Ua-Compatible: IE=Edge Etag: "9a779b80e4b0ac3c60d29807e302deb7" [...] > curl -I http://0.0.0.0:3000/products HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 X-Ua-Compatible: IE=Edge Etag: "fa8fc1e981833a6885b583d351c4d823"
  • 77. > curl -I http://0.0.0.0:3000/products HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 X-Ua-Compatible: IE=Edge Etag: "9a779b80e4b0ac3c60d29807e302deb7" [...] > curl -I http://0.0.0.0:3000/products HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 X-Ua-Compatible: IE=Edge Etag: "fa8fc1e981833a6885b583d351c4d823"
  • 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.
  • 80. > curl -I http://0.0.0.0:3000/products -b cookies.txt --header 'If-None-Match: "4d348810e69400799e2ab684c0ef4777"' HTTP/1.1 304 Not Modified Etag: "4d348810e69400799e2ab684c0ef4777" 304!
  • 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.
  • 83. Writing the initial cache wastes a lot of time.
  • 84. Let‘s preheat the cache in off business hours! Cron is your friend.
  • 85. Use the night to preheat your cache. And don‘t be afraid of brute force!
  • 86. A U T O B A H N
  • 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.
  • 91. Tricky part: How to delete out of date gz files?
  • 92. after_update :expire_cache before_destroy :expire_cache private def expire_cache ActionController::Base.expire_page(Rails.application.routes.url_h elpers.company_path(self)) ActionController::Base.expire_page(Rails.application.routes.url_h elpers.companies_path) end app/models/product.rb
  • 94. caches_page is good to cache customized user content too. It just takes more thinking.
  • 95. Let us assume a user base of 10,000,000 people.
  • 96. /tmp ᐅ wget http://www.railsconf.com/2013/talks --2013-04-27 21:04:24-- http://www.railsconf.com/2013/talks Resolving www.railsconf.com... 107.20.162.205 Connecting to www.railsconf.com|107.20.162.205|:80... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [text/html] Saving to: ‘talks’ [ <=> ] 74,321 258KB/ s in 0.3s 2013-04-27 21:04:25 (258 KB/s) - ‘talks’ saved [74321] /tmp ᐅ du -hs talks 76K talks /tmp ᐅ gzip talks /tmp ᐅ du -hs talks.gz 28K talks.gz /tmp ᐅ
  • 97. /tmp ᐅ du -hs talks.gz 28K talks.gz 28K * 10,000,000 = 0,26 TB
  • 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.
  • 101. HTTP/2 provides an average WebPerformance improvement of 20%. It’s a no-brainer. You have to use it!
  • 102. CDNs, HTTP/2 and Rails 5.2
  • 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!
  • 104. Image Formats It is still an often underused way of saving bandwidth.
  • 105. JPEG, PNG or WebP
  • 107. CSS
  • 108. Less CSS = Faster Webpages Do not underestimate the WebPerformance impact of optimized CSS.
  • 110. If possible don’t use Webfonts.
  • 112. It doesn’t matter! Just use your favorite one.
  • 114. Always offer both! Brotli can be used to save bandwidth and CPU-Resources.
  • 115. Heroku vs. Bare Metal
  • 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 .
  • 123. Set a Time Budget! If you run out of your time budget you have to cancel features on your website.
  • 125. The WebPerf Bible. => https://hpbn.co
  • 127. sw@wintermeyer-consulting.de last name | twitter | github e-mail