Slide 1

Slide 1 text

1 . 0 0 0 M I L L I S E K U N D E N S T E FA N W I N T E R M E Y E R

Slide 2

Slide 2 text

1.000 ms Page Loading Time ist der Mount Everest.

Slide 3

Slide 3 text

P H P C O N F E R E N C E . C O M / 2 0 1 3 / E N

Slide 4

Slide 4 text

W W W. G O O G L E . C O M

Slide 5

Slide 5 text

0.6s 0.8s

Slide 6

Slide 6 text

I S T D A S W I C H T I G ?

Slide 7

Slide 7 text

What's the impact of slow sites? Lower conversions and engagement, higher bounce rates... Ilya Grigorik @igrigorik Make The Web Faster, Google

Slide 8

Slide 8 text

Performance Related Changes and their User Impact Web Search Delay Experiment @igrigorik • The cost of delay increases over time and persists • Delays under half a second impact business metrics • "Speed matters" is not just lip service Type of Delay Delay (ms) Duration (weeks) Impact on Avg. Daily Searches Pre-header 50 4 Not measurable Pre-header 100 4 -0.20% Post-header 200 6 -0.59% Post-header 400 6 -0.59% Post-ads 200 4 -0.30%

Slide 9

Slide 9 text

Performance Related Changes and their User Impact Server Delays Experiment • Strong negative impacts • Roughly linear changes with increasing delay • Time to Click changed by roughly double the delay @igrigorik

Slide 10

Slide 10 text

Yo ho ho and a few billion pages of RUM How speed affects bounce rate @igrigorik

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

V E R S TA N D E N ! W I E G E H T E S W E I T E R ?

Slide 15

Slide 15 text

•Caching •Prefetching •SPDY •Misc •Lazy Boy quick Fix

Slide 16

Slide 16 text

P H P, R U B Y O N R A I L S O D E R D J A N G O ? ! E G A L !

Slide 17

Slide 17 text

J P E G , G I F, P N G O D E R W E B P ? ! K L E I N V I E H N I E U N T E R S C H ÄT Z E N !

Slide 18

Slide 18 text

C A C H I N G

Slide 19

Slide 19 text

Example Plattform Raspberry Pi

Slide 20

Slide 20 text

Software Stack • Ruby on Rails 3.2 (use 4.0 if you can) • Ruby 1.9.3 (use 2.0 if you can) • Nginx • Unicorn • MySQL • Raspbian Wheezy

Slide 21

Slide 21 text

Example: Online Shop

Slide 22

Slide 22 text

The Models

Slide 23

Slide 23 text

Cart state string LineItem price decimal (8,2) quantity integer Category name string Product description text name string price decimal (8,2) DiscountGroup discount integer name string User email string ∗ encrypted_password string ∗ first_name string last_name string Rating value integer

Slide 24

Slide 24 text

The UI

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

current_user

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Bob‘s cart

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

Discounted price

Slide 33

Slide 33 text

Rate this product

Slide 34

Slide 34 text

Measure the Success

Slide 35

Slide 35 text

Watir script http://watir.com

Slide 36

Slide 36 text

require 'watir-webdriver' ! url = 'http://ip.address:3000' b = Watir::Browser.new ! # An anonymous user looks around. # ['AA', 'AB', 'AH'].each do |product_name| b.goto url Watir::Wait.until { b.link(:text, product_name).exist? } b.link(:text, product_name).click Watir::Wait.until { b.link(:text, 'Webshop').exist? } b.link(:text, 'Webshop').click end 3 different products

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

bob@aol.com

Slide 39

Slide 39 text

Login Bob b.goto url b.goto "#{url}/users/sign_in" b.text_field(:id, "user_email").set("bob@aol.com") b.text_field(:id, "user_password").set("123") b.button(:name, "commit").click

Slide 40

Slide 40 text

Bob looks around too ['AA', 'AB', 'AH'].each do |product_name| b.goto url Watir::Wait.until { b.link(:text, product_name).exist? } b.link(:text, product_name).click Watir::Wait.until { b.link(:text, 'Webshop').exist? } b.link(:text, 'Webshop').click end

Slide 41

Slide 41 text

Bob rates products ['AA', 'AD', 'AI'].each do |product_name| b.goto url b.link(:text, product_name).click star_id = 'product_' + product_name + '_rating_2' b.link(:id, star_id).click end

Slide 42

Slide 42 text

Bob fills his cart [1, 3, 5].each do |product_id| b.goto url add_button_id = 'add_' + product_id.to_s b.link(:id, add_button_id).click end

Slide 43

Slide 43 text

Logout b.link(:text, 'Logout').click

Slide 44

Slide 44 text

Total: 43 webpages

Slide 45

Slide 45 text

30 sec. localhost demo

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

How fast or slow is on the Raspberry Pi?

Slide 48

Slide 48 text

0 29 58 87 116 Vanilla 116 s

Slide 49

Slide 49 text

/ Started GET "/" for x.x.x.x at 2013-02-28 21:05:34 +0000 Processing by ProductsController#index as HTML Rendered products/index.html.haml within layouts/application (3022.3ms) Rendered layouts/_navbar.html.haml (12.5ms) Rendered layouts/_footer.html.haml (2.9ms) Completed 200 OK in 3097ms (Views: 3013.3ms | ActiveRecord: 72.6ms)

Slide 50

Slide 50 text

Started GET "/" for x.x.x.x at 2013-02-28 21:05:34 +0000 Processing by ProductsController#index as HTML Rendered products/index.html.haml within layouts/application (3022.3ms) Rendered layouts/_navbar.html.haml (12.5ms) Rendered layouts/_footer.html.haml (2.9ms) Completed 200 OK in 3097ms (Views: 3013.3ms | ActiveRecord: 72.6ms) 3013.3ms

Slide 51

Slide 51 text

Fragment Caching aka Easy Money

Slide 52

Slide 52 text

Vanilla HAML Version %table.table.table-striped{:id => 'products'} %thead %tr %th Name %th Category [...] %tbody - @products.each do |product| %tr %td= link_to product.name, [...] %td= product.description

Slide 53

Slide 53 text

Russian Doll - cache [current_user, @products] do %table.table.table-striped{:id => 'products'} %thead %tr %th Name %th Category [...] %tbody - @products.each do |product| - cache [current_user, product] do %tr %td= link_to product.name, [...] %td= product.description complete table single row

Slide 54

Slide 54 text

complete table single row

Slide 55

Slide 55 text

Add these line to your Gemfile: gem 'dalli' gem 'cache_digests' ! Don‘t forget to run bundle install.

Slide 56

Slide 56 text

production.rb: config.cache_store = :dalli_store

Slide 57

Slide 57 text

A performance boost by 5 LOC?!

Slide 58

Slide 58 text

0 30 60 90 120 Vanilla Fragment Caching (4 LOC) 116 s 60 s

Slide 59

Slide 59 text

5 LOC result in 50% fewer servers!

Slide 60

Slide 60 text

But, it‘s all about the details.

Slide 61

Slide 61 text

•Each row and each table (#index). •Each #show content. •The shopping cart. •The navigation bar and the footer.

Slide 62

Slide 62 text

Don‘t just use a plain russian doll cache. ! Cluster the dolls!

Slide 63

Slide 63 text

Cluster of 10 Cluster of 10

Slide 64

Slide 64 text

2 hours of optimizing save another 15 s.

Slide 65

Slide 65 text

0 30 60 90 120 Vanilla Fragment Caching 116 s 45 s

Slide 66

Slide 66 text

IMPORTANT: You need a clean data structure!

Slide 67

Slide 67 text

HTTP Caching

Slide 68

Slide 68 text

Web browsers and proxies don‘t want to fetch webpages twice. ! They use Last-Modified and Etag to avoid that.

Slide 69

Slide 69 text

The idea of Last-Modified

Slide 70

Slide 70 text

Web browser: „My user wants to fetch xyz.html. I cached a copy last week. Is that still good?“

Slide 71

Slide 71 text

Web server: „xyz.html hasn‘t changed since last week. Go a head with your copy!“ ! aka 304 Not Modified

Slide 72

Slide 72 text

The curl version for Etag

Slide 73

Slide 73 text

> curl -I http://0.0.0.0:3000/ 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/ HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 X-Ua-Compatible: IE=Edge Etag: "fa8fc1e981833a6885b583d351c4d823"

Slide 74

Slide 74 text

> curl -I http://0.0.0.0:3000/ 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/ HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 X-Ua-Compatible: IE=Edge Etag: "fa8fc1e981833a6885b583d351c4d823"

Slide 75

Slide 75 text

Set the Etag class ProductsController < ApplicationController # GET /products def index @products = Product.limit(25) ! fresh_when :etag => [current_user,
 @products.order(:updated_at).last] end ! [...]


Slide 76

Slide 76 text

Set the Etag class ProductsController < ApplicationController # GET /products def index @products = Product.limit(25) ! fresh_when :etag => [current_user,
 @products.order(:updated_at).last] end ! [...]


Slide 77

Slide 77 text

> curl -I http://0.0.0.0:3000/ -c cookies.txt HTTP/1.1 200 OK Etag: "4d348810e69400799e2ab684c0ef4777" ! ! > curl -I http://0.0.0.0:3000/ -b cookies.txt HTTP/1.1 200 OK Etag: "4d348810e69400799e2ab684c0ef4777" The cookie is needed for the CSRF-Token.

Slide 78

Slide 78 text

> curl -I http://0.0.0.0:3000/ -b cookies.txt --header 'If-None-Match: "4d348810e69400799e2ab684c0ef4777"' HTTP/1.1 304 Not Modified Etag: "4d348810e69400799e2ab684c0ef4777" 304!

Slide 79

Slide 79 text

The beauty of HTTP caching is that the view isn‘t rendered.

Slide 80

Slide 80 text

0 30 60 90 120 Vanilla Fragment Caching HTTP Cache 45 s 35 s

Slide 81

Slide 81 text

Not good enough?

Slide 82

Slide 82 text

Writing the initial cache wastes a lot of time.

Slide 83

Slide 83 text

Let‘s preheat the cache in off business hours!

Slide 84

Slide 84 text

0 30 60 90 120 Vanilla Fragment Caching HTTP Cache Preheater 26 s

Slide 85

Slide 85 text

Use the night to preheat your cache. ! And don‘t be afraid of brute force!

Slide 86

Slide 86 text

A U T O B A H N

Slide 87

Slide 87 text

The fastest page is delivered by Nginx without ever contacting Ruby on Rails.

Slide 88

Slide 88 text

ᵓᴷᴷ Gemfile ᵓᴷᴷ [...] ᵓᴷᴷ public ᴹ ᵓᴷᴷ 404.html ᴹ ᵓᴷᴷ 422.html ᴹ ᵓᴷᴷ 500.html ᴹ ᵓᴷᴷ favicon.ico ᴹ ᵋᴷᴷ robots.txt ᵓᴷᴷ [...] That‘s already done for the files in the public directory.

Slide 89

Slide 89 text

Is there an easy way to save complete pages which are rendered by the Rails framework?

Slide 90

Slide 90 text

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 4.0

Slide 91

Slide 91 text

Brute Force is your friend! ! During the night the server has a hard time to stay awake any way.

Slide 92

Slide 92 text

Tricky part: How to delete out of date gz files?

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

0 30 60 90 120 Vanilla Fragment Caching HTTP Cache Preheater Page Caching 19 s 6.1 x faster!

Slide 95

Slide 95 text

caches_page vs. !current_user.nil? ! ???

Slide 96

Slide 96 text

caches_page is good to cache customized user content too. ! It just takes more thinking.

Slide 97

Slide 97 text

Let us assume a user base of 10,000,000 people.

Slide 98

Slide 98 text

/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 ᐅ

Slide 99

Slide 99 text

/tmp ᐅ du -hs talks.gz 28K talks.gz 28K * 10,000,000 = 0,26 TB

Slide 100

Slide 100 text

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.

Slide 101

Slide 101 text

Nginx will happily read a cookie and find the pre- rendered page in a given directory structure.

Slide 102

Slide 102 text

To setup a complex page_cache system is a lot of work. You have to tackle not only Rails but Nginx too. It does increase the snappiness of your application but might not be worth the effort for small systems.

Slide 103

Slide 103 text

0 30 60 90 120 Vanilla Fragment Caching HTTP Cache Preheater Page Caching 19 s Any chance to get a single digit here?

Slide 104

Slide 104 text

Add Ember.js to your software stack! ! http://emberjs.com rails new testapp -m http://emberjs.com/edge_template.rb

Slide 105

Slide 105 text

0 30 60 90 120 Vanilla Fragment Caching HTTP Cache Preheater Page Caching Ember.js 8 s 14.5 x faster!

Slide 106

Slide 106 text

W H AT E R FA L L

Slide 107

Slide 107 text

http://www.webpagetest.org/result/131029_MB_MVS/

Slide 108

Slide 108 text

Daumen-Regel: Nie länger als ein Browserfenster.

Slide 109

Slide 109 text

C D N

Slide 110

Slide 110 text

C D N S Man sollte wissen was man tut! ! Sonst bringt der Einsatz von Content Delivery Networks (CDN) nichts.

Slide 111

Slide 111 text

I N L I N I N G

Slide 112

Slide 112 text

I N L I N I N G • Es kann Sinn machen CSS und JavaScript in die HTML- Seite zu packen. • Beispiele: • Sehr wenig CSS oder JavaScript. • Schnelle Ladezeit bei der ersten Seite. • Einsatz von TurboLinks.

Slide 113

Slide 113 text

I N L I N I N G V O N B I L D E R N Auch das kann Sinn machen. Man sollte aber hier darauf achten, wie lange der Browser braucht das Bild zu „entpacken“. Auf langsamen Geräten kann das länger dauern, als der Download einer eigenen Datei. ! Vorsicht!

Slide 114

Slide 114 text

P R E L O A D I N G U N D P R E F E T C H I N G

Slide 115

Slide 115 text

P R E L O A D I N G U N D P R E F E T C H I N G

Slide 116

Slide 116 text

M A N U A L D N S - P R E F E T C H 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.“

Slide 117

Slide 117 text

P R E F E T C H ! 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 : " ACCEPT-RANGES: BYTES“ H E A D E R

Slide 118

Slide 118 text

P R E R E N D E R !

Slide 119

Slide 119 text

S P D Y

Slide 120

Slide 120 text

http://en.wikipedia.org/wiki/SPDY

Slide 121

Slide 121 text

D E F I N I E R E N S I E A M A N FA N G D E R A R B E I T E I N E W U N S C H L A D E Z E I T.

Slide 122

Slide 122 text

Der erste Eindruck sprich die erste Seite ist am wichtigsten. Aber auch weitere Seiten müssen schnell zu laden sein.

Slide 123

Slide 123 text

L A Z Y B O Y Q U I C K F I X

Slide 124

Slide 124 text

A L L E S V E R S TA N D E N , A B E R V O R M W E I H N A C H T S G E S C H Ä F T K E I N E Z E I T M E H R ?

Slide 125

Slide 125 text

https://developers.google.com/speed/pagespeed/

Slide 126

Slide 126 text

S H E R PA S H E L F E N G E R N E A U F D E N M O U N T E V E R E S T.

Slide 127

Slide 127 text

stefan.wintermeyer@amooma.de first name last name | twitter | github company name lang e-mail