Cache is
  King
stevesouders.com/docs/html5dev-cacheisking-20121015.pptx
Disclaimer: This content does not necessarily reflect the opinions of my employer.
Cache
Network
JavaScript
Cache
Experiment Setup
Baseline:
 WebPagetest.org
 Alexa world top 1000
 IE9
 DSL: 1.5 Mbps down, 384 Kbps up,
    50ms RTT
 empty cache (“first view”)
 median of 3 tests
 window.onload
Long Pole in the Tent
Fast Network:
 FIOS: 20 Mbps down, 5 Mbps up, 4ms RTT

No JavaScript:
 disable JavaScript
Primed Cache:
 “repeat view”
7.65   4.74   4.13   3.46
7.65   4.74   4.13   3.46
7.65   4.74   4.13   3.46
7.65   4.74   4.13   3.46
Caching 101: simple GET
 GET /scripts/main.js HTTP/1.1
 HTTP/1.1 200 OK
  Content-Type: text/javascript
   Content-Length: 204528

   function(){…

next time:
 GET /scripts/main.js HTTP/1.1
 HTTP/1.1 200 OK
  Content-Type: text/javascript
   Content-Length: 204528

   function(){…

How can we avoid downloading 200K if the
 file hasn’t changed?
Caching 101: Conditional GET
 GET /scripts/main.js HTTP/1.1
 HTTP/1.1 200 OK
  Content-Type: text/javascript
   Content-Length: 204528
   Last-Modified: Mon, 24 Sep 2012 21:14:35 GMT
   Etag: “3Rsttw”

   function(){…
next time:
 GET /scripts/main.js HTTP/1.1
  If-Modified-Since: Mon, 24 Sep   2012 21:14:35 GMT
   If-None-Match: “3Rsttw”

 HTTP/1.1 304 Not Modified
304 response saves us 200K – yay!
Is there a way to avoid checking EVERY time?
Caching 101: max-age
 GET /scripts/main.js HTTP/1.1
 HTTP/1.1 200 OK
  Content-Type: text/javascript
   Content-Length: 204528
   Last-Modified: Mon, 24 Sep 2012 21:14:35 GMT
   Cache-control: max-age=31536000

   function(){…

next time (within 1 year):
 [nothing]

max-age eliminates HTTP request
How avoid caching, e.g., dynamic responses?
Caching 101: no-cache
 GET /scripts/inbox.js HTTP/1.1
 HTTP/1.1 200 OK
  Content-Type: text/javascript
   Content-Length: 1328
   Cache-control: no-cache, must-revalidate, max-age=0

   function(){…

next time:
 GET /scripts/inbox.js HTTP/1.1
 HTTP/1.1 200 OK
  Content-Type: text/javascript
   Content-Length: 1417
   Cache-control: no-cache, must-revalidate, max-age=0

   function(){…
Be explicit! Use max-age or no-cache.
Top 1K

                  no-cache or must-revalidate



       10%   4%



14% of requests have Cache-Control:
  no-cache or must-revalidate
24% have no Cache-Control header at all
27% have no max-age (heuristic caching)
                             http://httparchive.org/interesting.php
Top 300K

                  no-cache or must-revalidate



        9%   4%



13% of requests have Cache-Control:
  no-cache or must-revalidate
44% have no Cache-Control header at all
48% have no max-age (heuristic caching)
                             http://httparchive.org/interesting.php
Top 50K-300K




slow increase in adoption
                            http://httparchive.org/trends.php
Heuristic Caching
RFC2616:
[in the absence of max-age] the cache
MAY compute a freshness lifetime using
a heuristic. […]if the response does
have a Last-Modified time, the
heuristic expiration value SHOULD be
no more than some fraction of the
interval since that time. A typical
setting of this fraction might be 10%.

recall 48% of requests have no max-age!
What’s (10% of) the typical interval?
6% heuristic max-age < 1 day
30% heuristic max-age > 3 days
but why don’t they have max-age?!
8% “unknown” checked once per session (IE9)
www.airbnb.com/
    72 requests

   81% have Cache-Control

   10% expire in < 1 day

   78% have Last-Modified

40 days median L-M interval
www.pinterest.com/
    131 requests

   87% have Cache-Control

     2% expire in < 1 day

   94% have Last-Modified

151 days median L-M interval
www.stackmob.com/
     52 requests

   25% have Cache-Control

     1% expire in < 1 day

   81% have Last-Modified

241 days median L-M interval
www.mozilla.org/
    32 requests

   31% have Cache-Control

   16% expire in < 1 day

   84% have Last-Modified

24 days median L-M interval
www.zendesk.com/
    70 requests

   94% have Cache-Control

   59% expire in < 1 day

   69% have Last-Modified

59 days median L-M interval
www.catch.com/
     52 requests

   19% have Cache-Control

   50% expire in < 1 day

   69% have Last-Modified

151 days median L-M interval
www.intel.com/
    90 requests

   66% have Cache-Control

   84% expire in < 1 day

   81% have Last-Modified

12 days median L-M interval
www.boston.com/
    69 requests

   69% have Cache-Control

   71% expire in < 1 day

   73% have Last-Modified

61 days median L-M interval
www.time.com/
    171 requests

   35% have Cache-Control

   71% expire in < 1 day

   69% have Last-Modified

166 days median L-M interval
www.usatoday.com/
   127 requests

   29% have Cache-Control

   67% expire in < 1 day

   79% have Last-Modified

29 days median L-M interval
www.telegraph.co.uk/
    179 requests

   28% have Cache-Control

   84% expire in < 1 day

   77% have Last-Modified

106 days median L-M interval
www.tmz.com/
   439 requests

   51% have Cache-Control

   48% expire in < 1 day

   59% have Last-Modified

25 days median L-M interval
be explicit


always specify Cache-Control:
max-age OR no-cache
20% of page views done with “empty cache”
40-60% of daily users experience “empty cache”
             www.yuiblog.com/blog/2007/01/04/performance-research-part-2/
approximately 70% of users do not have full
caches [so 30% do have a full cache]
for those users who filled up their cache…how
many hours of "active" browsing does it take
to fill the cache? 25% in 1 hour, 50% in 4
hours, and 75% in 10 hours.
7% of users will clear their cache…once per
week… 19% of users will experience fatal
cache corruption at least once per week, thus
requiring nuking the whole cache.
               plus.google.com/103382935642834907366/posts/XRekvZgdnBb
blaze.io/mobile/understanding-mobile-cache-sizes/
app cache
offline apps, longer cache
<!doctype html>
<html
 manifest=“myapp.appcache”>
myapp.appcache:
  CACHE MANIFEST
  # Revision: 1.28
  CACHE:
  /images/logo.gif
  NETWORK:
  /login.html
  FALLBACK:
  /index.html /offline.html
  Content-Type: text/cache-manifest
app cache gotchas
html docs w/ manifest are cached
404 => nothing is cached
size: 5MB+
must rev manifest to update resources
update is served on 2nd reload (?!?!)
push app
logo.gif =
               user loads app
               app cache is empty
                                      app
               fetch manifest
               fetch logo.gif
                                      cache
               app cache =
               user sees
                                      reload
push app       user loads app       user loads app again
logo.gif =     app cache =          app cache =
rev manifest   user sees            user sees
               fetch manifest       fetch manifest (304)
               fetch logo.gif
               app cache =
load twice workaround
window.applicationCache.addEventListener('upda
teready',
 function(e) {
   if ( window.applicationCache.status ==
          window.applicationCache.UPDATEREADY) {
      if ( confirm(“Load new content?”) ) {
         ...

http://www.webdirections.org/blog/get-offline/
http://www.html5rocks.com/en/tutorials/appcache/beginner/
localStorage
window.localStorage:
   setItem()
   getItem()
   removeItem()
   clear()
also sessionStorage
all popular browsers, 5MB max
warning: it’s synchronous
http://dev.w3.org/html5/webstorage/
http://diveintohtml5.org/storage.html
a day’s worth of Google Chrome dev channel
weekend traffic on desktop Chrome
Time in ms to prime localStorage from disk:

    Percentile   Windows             Mac            Linux
       50th           0                0               0
       75th             2               0               0
       90th            40              17              17
       95th           160              57             160
       99th         1200             890            1200



                 insouciant.org/tech/time-to-load-localstorage-into-memory/
localStorage as cache
1st doc: write JS & CSS blocks to localStorage
   mres.-0yDUQJ03U8Hjija: <script>(function(){...

set cookie with entries & version
   MRES=-0yDUQJ03U8Hjija:-4EaJoFuDoX0iloI:...

later docs: read JS & CSS from localStorage
   document.write( localStorage.getItem(mres.-
     0yDUQJ03U8Hjija) );

do checksum before eval

stevesouders.com/blog/2011/03/28/storager-case-study-bing-google/
lists.w3.org/Archives/Public/public-webcrypto-comments/2012Aug/0076.html
smarter browsers
bigger cache
smarter purging
prioritized websites
Preferred Caching
igvita.com/2012/06/04/chrome-networking-dns-prefetch-and-tcp-preconnect/
personalized
  browsing
takeaways
gather more cache stats for
 your users
be explicit! use max-age or
 no-cache
augment with localStorage &
 appcache
lobby browser vendors
http://httparchive.org/
Steve Souders
                                             @souders
stevesouders.com/docs/html5dev-cacheisking-20121015.pptx

Cache is King

  • 1.
    Cache is King stevesouders.com/docs/html5dev-cacheisking-20121015.pptx Disclaimer: This content does not necessarily reflect the opinions of my employer.
  • 5.
  • 22.
  • 23.
    Experiment Setup Baseline: WebPagetest.org Alexa world top 1000 IE9 DSL: 1.5 Mbps down, 384 Kbps up, 50ms RTT empty cache (“first view”) median of 3 tests window.onload
  • 24.
    Long Pole inthe Tent Fast Network: FIOS: 20 Mbps down, 5 Mbps up, 4ms RTT No JavaScript: disable JavaScript Primed Cache: “repeat view”
  • 26.
    7.65 4.74 4.13 3.46
  • 27.
    7.65 4.74 4.13 3.46
  • 28.
    7.65 4.74 4.13 3.46
  • 29.
    7.65 4.74 4.13 3.46
  • 30.
    Caching 101: simpleGET  GET /scripts/main.js HTTP/1.1  HTTP/1.1 200 OK Content-Type: text/javascript Content-Length: 204528 function(){… next time:  GET /scripts/main.js HTTP/1.1  HTTP/1.1 200 OK Content-Type: text/javascript Content-Length: 204528 function(){… How can we avoid downloading 200K if the file hasn’t changed?
  • 31.
    Caching 101: ConditionalGET  GET /scripts/main.js HTTP/1.1  HTTP/1.1 200 OK Content-Type: text/javascript Content-Length: 204528 Last-Modified: Mon, 24 Sep 2012 21:14:35 GMT Etag: “3Rsttw” function(){… next time:  GET /scripts/main.js HTTP/1.1 If-Modified-Since: Mon, 24 Sep 2012 21:14:35 GMT If-None-Match: “3Rsttw”  HTTP/1.1 304 Not Modified 304 response saves us 200K – yay! Is there a way to avoid checking EVERY time?
  • 32.
    Caching 101: max-age GET /scripts/main.js HTTP/1.1  HTTP/1.1 200 OK Content-Type: text/javascript Content-Length: 204528 Last-Modified: Mon, 24 Sep 2012 21:14:35 GMT Cache-control: max-age=31536000 function(){… next time (within 1 year):  [nothing] max-age eliminates HTTP request How avoid caching, e.g., dynamic responses?
  • 33.
    Caching 101: no-cache GET /scripts/inbox.js HTTP/1.1  HTTP/1.1 200 OK Content-Type: text/javascript Content-Length: 1328 Cache-control: no-cache, must-revalidate, max-age=0 function(){… next time:  GET /scripts/inbox.js HTTP/1.1  HTTP/1.1 200 OK Content-Type: text/javascript Content-Length: 1417 Cache-control: no-cache, must-revalidate, max-age=0 function(){… Be explicit! Use max-age or no-cache.
  • 34.
    Top 1K no-cache or must-revalidate 10% 4% 14% of requests have Cache-Control: no-cache or must-revalidate 24% have no Cache-Control header at all 27% have no max-age (heuristic caching) http://httparchive.org/interesting.php
  • 35.
    Top 300K no-cache or must-revalidate 9% 4% 13% of requests have Cache-Control: no-cache or must-revalidate 44% have no Cache-Control header at all 48% have no max-age (heuristic caching) http://httparchive.org/interesting.php
  • 36.
    Top 50K-300K slow increasein adoption http://httparchive.org/trends.php
  • 37.
    Heuristic Caching RFC2616: [in theabsence of max-age] the cache MAY compute a freshness lifetime using a heuristic. […]if the response does have a Last-Modified time, the heuristic expiration value SHOULD be no more than some fraction of the interval since that time. A typical setting of this fraction might be 10%. recall 48% of requests have no max-age! What’s (10% of) the typical interval?
  • 38.
    6% heuristic max-age< 1 day 30% heuristic max-age > 3 days but why don’t they have max-age?! 8% “unknown” checked once per session (IE9)
  • 40.
    www.airbnb.com/ 72 requests 81% have Cache-Control 10% expire in < 1 day 78% have Last-Modified 40 days median L-M interval
  • 41.
    www.pinterest.com/ 131 requests 87% have Cache-Control 2% expire in < 1 day 94% have Last-Modified 151 days median L-M interval
  • 42.
    www.stackmob.com/ 52 requests 25% have Cache-Control 1% expire in < 1 day 81% have Last-Modified 241 days median L-M interval
  • 43.
    www.mozilla.org/ 32 requests 31% have Cache-Control 16% expire in < 1 day 84% have Last-Modified 24 days median L-M interval
  • 44.
    www.zendesk.com/ 70 requests 94% have Cache-Control 59% expire in < 1 day 69% have Last-Modified 59 days median L-M interval
  • 45.
    www.catch.com/ 52 requests 19% have Cache-Control 50% expire in < 1 day 69% have Last-Modified 151 days median L-M interval
  • 46.
    www.intel.com/ 90 requests 66% have Cache-Control 84% expire in < 1 day 81% have Last-Modified 12 days median L-M interval
  • 48.
    www.boston.com/ 69 requests 69% have Cache-Control 71% expire in < 1 day 73% have Last-Modified 61 days median L-M interval
  • 49.
    www.time.com/ 171 requests 35% have Cache-Control 71% expire in < 1 day 69% have Last-Modified 166 days median L-M interval
  • 50.
    www.usatoday.com/ 127 requests 29% have Cache-Control 67% expire in < 1 day 79% have Last-Modified 29 days median L-M interval
  • 51.
    www.telegraph.co.uk/ 179 requests 28% have Cache-Control 84% expire in < 1 day 77% have Last-Modified 106 days median L-M interval
  • 52.
    www.tmz.com/ 439 requests 51% have Cache-Control 48% expire in < 1 day 59% have Last-Modified 25 days median L-M interval
  • 53.
    be explicit always specifyCache-Control: max-age OR no-cache
  • 54.
    20% of pageviews done with “empty cache” 40-60% of daily users experience “empty cache” www.yuiblog.com/blog/2007/01/04/performance-research-part-2/
  • 55.
    approximately 70% ofusers do not have full caches [so 30% do have a full cache] for those users who filled up their cache…how many hours of "active" browsing does it take to fill the cache? 25% in 1 hour, 50% in 4 hours, and 75% in 10 hours. 7% of users will clear their cache…once per week… 19% of users will experience fatal cache corruption at least once per week, thus requiring nuking the whole cache. plus.google.com/103382935642834907366/posts/XRekvZgdnBb
  • 56.
  • 57.
    app cache offline apps,longer cache <!doctype html> <html manifest=“myapp.appcache”> myapp.appcache: CACHE MANIFEST # Revision: 1.28 CACHE: /images/logo.gif NETWORK: /login.html FALLBACK: /index.html /offline.html Content-Type: text/cache-manifest
  • 58.
    app cache gotchas htmldocs w/ manifest are cached 404 => nothing is cached size: 5MB+ must rev manifest to update resources update is served on 2nd reload (?!?!)
  • 59.
    push app logo.gif = user loads app app cache is empty app fetch manifest fetch logo.gif cache app cache = user sees reload push app user loads app user loads app again logo.gif = app cache = app cache = rev manifest user sees user sees fetch manifest fetch manifest (304) fetch logo.gif app cache =
  • 60.
    load twice workaround window.applicationCache.addEventListener('upda teready', function(e) { if ( window.applicationCache.status == window.applicationCache.UPDATEREADY) { if ( confirm(“Load new content?”) ) { ... http://www.webdirections.org/blog/get-offline/ http://www.html5rocks.com/en/tutorials/appcache/beginner/
  • 61.
    localStorage window.localStorage: setItem() getItem() removeItem() clear() also sessionStorage all popular browsers, 5MB max warning: it’s synchronous http://dev.w3.org/html5/webstorage/ http://diveintohtml5.org/storage.html
  • 62.
    a day’s worthof Google Chrome dev channel weekend traffic on desktop Chrome Time in ms to prime localStorage from disk: Percentile Windows Mac Linux 50th 0 0 0 75th 2 0 0 90th 40 17 17 95th 160 57 160 99th 1200 890 1200 insouciant.org/tech/time-to-load-localstorage-into-memory/
  • 63.
    localStorage as cache 1stdoc: write JS & CSS blocks to localStorage mres.-0yDUQJ03U8Hjija: <script>(function(){... set cookie with entries & version MRES=-0yDUQJ03U8Hjija:-4EaJoFuDoX0iloI:... later docs: read JS & CSS from localStorage document.write( localStorage.getItem(mres.- 0yDUQJ03U8Hjija) ); do checksum before eval stevesouders.com/blog/2011/03/28/storager-case-study-bing-google/ lists.w3.org/Archives/Public/public-webcrypto-comments/2012Aug/0076.html
  • 64.
    smarter browsers bigger cache smarterpurging prioritized websites
  • 65.
  • 66.
  • 67.
  • 68.
    takeaways gather more cachestats for your users be explicit! use max-age or no-cache augment with localStorage & appcache lobby browser vendors
  • 69.
  • 70.
    Steve Souders @souders stevesouders.com/docs/html5dev-cacheisking-20121015.pptx

Editor's Notes

  • #2 http://www.flickr.com/photos/athrasher/2823255013/
  • #3 http://www.flickr.com/photos/madeforglorysigns/4311427541/used with permission from Golden West Sign Arts
  • #4 http://www.flickr.com/photos/59937401@N07/5857449830/
  • #5 http://www.flickr.com/photos/drewish/106431464/
  • #7 http://www.flickr.com/photos/thomasfisherlibrary/5912522676/Creator: Tyrrell, Joseph Burr, 1858-1957Title: An Indian cache near Aishihik [Yukon, Canada]Notes: Title transcribed from caption. From an album of Joseph Tyrrell’s photographs of the Klondike, 1898-1899Date: 1898
  • #23 http://www.flickr.com/photos/michaelcr/773201918/
  • #27 http://chart.apis.google.com/chart?chd=t:-1|90,0,0,0|-1|904,0,0,0&amp;chxl=0:|Baseline|No%20JS|Fast%20Network|Primed%20Cache&amp;chxt=x,y,r&amp;chs=600x300&amp;cht=lxy&amp;chco=15A50E,006600&amp;chm=N,15A50E,0,-1,12,,h::8|N**kB,006600,1,-1,12,,h::8|o,15A50E,0,-1,6|o,006600,1,-1,6&amp;chds=9,99,20,150,9,99,100,1000&amp;chts=006600,24&amp;chtt=Total+Transfer+Size+%26+Total+Requests&amp;chma=5,5,5,25&amp;chls=1,6,3|1&amp;chxr=1,100,1000,100|2,20,150,20&amp;chxs=1,006600,11.5,-0.5,lt,006600,006600|2,15A50E,11.5,-0.5,lt,15A50E,15A50E&amp;chxtc=0,4|1,4&amp;chxp=0&amp;chdl=Total+Requests|Total+Transfer+Size+%28kB%29&amp;chdlp=bv|r
  • #28 lessblockingmuchfewerrequestshttp://chart.apis.google.com/chart?chd=t:-1|90,59,0,0|-1|904,577,0,0&amp;chxl=0:|Baseline|No%20JS|Fast%20Network|Primed%20Cache&amp;chxt=x,y,r&amp;chs=600x300&amp;cht=lxy&amp;chco=15A50E,006600&amp;chm=N,15A50E,0,-1,12,,h::8|N**kB,006600,1,-1,12,,h::8|o,15A50E,0,-1,6|o,006600,1,-1,6&amp;chds=9,99,20,150,9,99,100,1000&amp;chts=006600,24&amp;chtt=Total+Transfer+Size+%26+Total+Requests&amp;chma=5,5,5,25&amp;chls=1,6,3|1&amp;chxr=1,100,1000,100|2,20,150,20&amp;chxs=1,006600,11.5,-0.5,lt,006600,006600|2,15A50E,11.5,-0.5,lt,15A50E,15A50E&amp;chxtc=0,4|1,4&amp;chxp=0&amp;chdl=Total+Requests|Total+Transfer+Size+%28kB%29&amp;chdlp=bv|r
  • #29 same # ofrequests &amp; sizeasbaseline10x fasterconnectionimaginechallengesfor mobilehttp://chart.apis.google.com/chart?chd=t:-1|90,59,91,0|-1|904,577,927,0&amp;chxl=0:|Baseline|No%20JS|Fast%20Network|Primed%20Cache&amp;chxt=x,y,r&amp;chs=600x300&amp;cht=lxy&amp;chco=15A50E,006600&amp;chm=N,15A50E,0,-1,12,,h::8|N**kB,006600,1,-1,12,,h::8|o,15A50E,0,-1,6|o,006600,1,-1,6&amp;chds=9,99,20,150,9,99,100,1000&amp;chts=006600,24&amp;chtt=Total+Transfer+Size+%26+Total+Requests&amp;chma=5,5,5,25&amp;chls=1,6,3|1&amp;chxr=1,100,1000,100|2,20,150,20&amp;chxs=1,006600,11.5,-0.5,lt,006600,006600|2,15A50E,11.5,-0.5,lt,15A50E,15A50E&amp;chxtc=0,4|1,4&amp;chxp=0&amp;chdl=Total+Requests|Total+Transfer+Size+%28kB%29&amp;chdlp=bv|r
  • #30 32requests, 163 kBimmediately after so even 10 min cache time helpshttp://chart.apis.google.com/chart?chd=t:-1|90,59,91,32|-1|904,577,927,163&amp;chxl=0:|Baseline|No%20JS|Fast%20Network|Primed%20Cache&amp;chxt=x,y,r&amp;chs=600x300&amp;cht=lxy&amp;chco=15A50E,006600&amp;chm=N,15A50E,0,-1,12,,h::8|N**kB,006600,1,-1,12,,h::8|o,15A50E,0,-1,6|o,006600,1,-1,6&amp;chds=9,99,20,150,9,99,100,1000&amp;chts=006600,24&amp;chtt=Total+Transfer+Size+%26+Total+Requests&amp;chma=5,5,5,25&amp;chls=1,6,3|1&amp;chxr=1,100,1000,100|2,20,150,20&amp;chxs=1,006600,11.5,-0.5,lt,006600,006600|2,15A50E,11.5,-0.5,lt,15A50E,15A50E&amp;chxtc=0,4|1,4&amp;chxp=0&amp;chdl=Total+Requests|Total+Transfer+Size+%28kB%29&amp;chdlp=bv|r
  • #34 nit: you do NOT need to send Cache-Control on 204 responses
  • #35 Alexa Top 1000
  • #37 Alexa Top 1000
  • #41 http://www.flickr.com/photos/auntiep/5547038689/
  • #43 Many other speaker website’s were nearly as good (and uninteresting): Adobe, The Onion, Groupon, EventBrite, Nokia
  • #49 Sidney Maestre
  • #50 Christian
  • #53 Mikito
  • #56 http://www.flickr.com/photos/marlon-bunday-mmx/4988354238/
  • #66 http://www.flickr.com/photos/gedankenstuecke/2749387908/
  • #70 flickr.com/photos/india-nepal-iran/203982474/
  • #71 flickr.com/photos/97657657@N00/1918688483/
  • #73 flickr.com/photos/presley_m/5152304885/
  • #74 flickr.com/photos/bryanpearson/564703455/
  • #76 flickr.com/photos/nelsoncruz/431244400/
  • #77 http://www.flickr.com/photos/hugovk/216375074/
  • #79 resolve 10 most visited sites at startuppreconnect to preferred search engine when focus on omnibarresolve and preconnect to subresource domains
  • #80 http://www.flickr.com/photos/bymeeni/4779608045/link prefetch is for ALL users – not necessarily where I navigatethe pattern learning has to occur on the clientbased on past behavior and present context, preload the content relevant to where I’ll go in the future
  • #81 http://www.flickr.com/photos/scottlynchnyc/7040609039/