Google I/O 2012 - High Performance HTML5

Uploaded by GoogleDevelopers on 10.07.2012

>>Steve Souders: Okay. Is that my cue to go? Okay. We're having a current discussion about
letting people come in and sit up front, so I hope we can do that.
I'm really excited to see everyone that showed up, but I have a cold shower announcement
to make. I'm not giving anything away. So although that's not entirely true. I'm
giving away information and knowledge, but nothing that physically you can take home
and give to your kids if that's what they're expecting.
So I'm going to talk about HTML5 from a performance perspective, but first I'm going to start
off and give a little background on my work in working on Web performance and kind of
the motivation. I think probably a lot of people -- how many people here build Web sites?
Yeah, okay, great. So I think there's something in the DNA of
engineers in general, and especially programmers, to be efficient and optimize.
I think as developers we all like our code -- we feel proud if people -- if we can say
about our code that it's really fast and efficient. And so at the beginning I'm going to talk
about how we can try to work to instill that interest and buy-in for performance across
other parts of the organization outside of engineering.
So there will be a little preamble on that and then we'll dive into the HTML5 stuff.
I just want to mention the slides are up on my Web site, If you go there
there's a link to it. You can check that out. Okay. So let's get started -- oh, the other
thing I want to mention is really more than what I say and more than the words on the
slides, I really want people to pay attention to the photos in the background of the slides.
[ Laughter ] >>Steve Souders: Come on, we're going to -- loosen
up a little bit. Let's go. I mean, like the story of my life, like I
take slow things and make them fast, like is that very appropriate or what?
I need more feedback. [ Applause ]
>>Steve Souders: Yeah, okay, there we go. I worked at Yahoo for a long time, eight years.
I was going to be there one year and I was there eight years. And about four and a half
years ago I joined Google, which was really my goal when I first started on Web performance
because I knew the people at Google really cared about making things fast, and it's true.
So I've been there four and a half years. I do mostly open source stuff, so I did -- I
created YSlow, Cuzillion, Sprite Me, Hammerhead, Browserscope. Lindsey Simon now runs that
project, very successful project. Jdrop, which is a great concept, but not such a successful
project, but you should check it out. HTTP Archive, another one I don't have time to
talk about today, but check it out, I wrote "High Performance Web sites," "Even
Faster Web sites." I taught at Stanford based on the books and
I run the velocity conference. Oh, I have to update. We just had it this week in fact.
I just finished that on Wednesday. So I've been doing this performance stuff
for quite a listening time now, seven years, I think, but when I started back in 2004 I
kind of felt like the lone voice in the woods. So really I'm sure there were people that
were working on it back then, but when I searched for information about making Web sites faster,
I really didn't find very much. There was a great blog post from David Hyatt that actually
really opened my eyes to where I should look for making Web sites faster.
I should mention that when I talk about performance, I mean that from the end user's perspective.
My goal when I started this in 2004 was to make the user experience fast.
Now, there's another aspect of performance which I think of more as efficiency or scalability
on back end servers. How can we handle 10X the current number of users with the same
hardware? Those kinds of efficiency optimizations are also performance, but when I say performance,
I really mean speed, speed of the user experience. So when I first started working on this there
really wasn't that much information out there and I would try to go -- I started documenting
things, writing books, blogging, trying to go to other teams and convince them to focus
on performance. And it's kind of like QA or writing unit tests.
Like try to sell unit tests -- time to build unit tests to someone from marketing or sales.
That's kind of hard to do, because it's hard to visually see the benefit of that.
So at the beginning it was kind of the same thing with talking about make the Web site
faster. Yeah, we need like a couple of people to work for at least six months. And you won't
see anything different on the site, it will just feel faster. And believe me, that's really
critical. And they're like no, really we want you to
add more flash. [Laughter].
>>Steve Souders: And so fast forward to 2010, future of Web apps. We have Fred Wilson. How
many people know Fred Wilson? Like one of the most prolific bloggers. He's
the number one VC out of New York City, out of Union Square, very techy guy. But really
his business is investment, funding startup companies.
Here he is speaking at a tech conference about his 10 golden principles of successful Web
apps. What's number one? Speed. He says, First and foremost we believe that speed is more
than a feature, speed is the most important feature.
And he goes on to talk about with their portfolio companies they track all of the company's
performance, speed, page load time for real users. And that in their experience the data
shows that when the speed drops off, the success of the business drops off.
And that's why they do this for all the portfolio companies that they work with.
And so I think that's to me like Web performance has arrived. We have a VC talking at a tech
conference about speed, like this was beautiful to me. I start almost every slide deck with
this slide. And actually, a partner of Fred's spoke at
the Velocity conference this week. And in addition -- if that's not enough to
have the number one VC out of New York City talking about performance, I'm going to run
through a bunch of case studies here that correlate performance speed to the success
of the business. So here's -- about two years ago Bing and
Google Search got up on stage at the same time and talked about experiments they had
run. Bing introduced intentionally a delay in serving search results. They took it up
to two seconds. At a two-second delay they saw all their goodness metrics drop off. Most
significantly revenue per user dropped off four percent.
And of course, these companies are doing this on a very small sample size.
So Google took this up to 400 milliseconds at the max. Didn't see that much dropoff in
searches per user, although at Google scale that's a fairly significant dropoff.
The thing that was really interesting about this study was after they removed the delay,
they kept that user group segmented and tracked their usage. And it took I think six weeks
for the usage, the searches per day, and other goodness metrics, to resume where they were
before the experiment started. So the user's awareness of what the experience
is like is something that gets ingrained and lives with them and it takes a long time to
build that and to recover from delays. So it's important to stay on top of keeping your
Web site fast. And Yahoo ran a similar study. And for full
page traffic, that means traffic where the onload event actually fires, a 400-millisecond
delay saw a five to nine percent dropoff in traffic.
Here Mozilla -- this is about a year old. Mozilla, when you want to upgrade to Firefox
and you're in Internet Explorer, you get the IE landing page, and they made that IE Firefox
landing page 2.2 seconds faster, and they saw a 15% lift in Firefox downloads. So for
them that's their business metric, being a nonprofit.
And this was a seminal study from Velocity a few years ago. Shopzilla did a tremendous
performance effort, taking their site from seven seconds to about three seconds, and
they saw conversion rate go up seven to 12 percent depending on the country.
This is very significant. They saw the unique users from search engine marketing more than
double. And this was the first study that showed another benefit in addition to the
business metrics is operating costs going down.
So here we have the number of servers needed to run the Web site after this optimization
dropped. And that kind of makes sense because like one of the optimizations is connecting
scripts together, so instead of making seven requests to my server, I'm only making one.
Turn on Gzip so the amount of time it takes for the response to get off the server box
is lowered and so the server can handle a higher QPS.
And then following that a good friend of mine, Bill Scott, when he was at Netflix, they had
-- they didn't have Gzip turned on for some reason. I can go into it after if people want
to ask. And he turned it on one day and the [Indiscernible]
said the Web site is down, all we're serving is the error page.
And he said why do you think that? And they said the traffic in our data server
has dropped in half. He said, Nope, we're serving everything, it's
just that we turned on Gzip. So this is probably six figures a month in a data center that
they saved. Pretty significant. Here's another example, turned
on -- added an expires header so that their resources through their CDN could be cached
and they saw their CDN traffic drop by a third. So again, this is a six-figure maybe high
five-figure savings per month from CDN costs. And then not that recently anymore, this was
about a year and a half ago, Google announced that speed or latency was going to be taken
into consideration for page rank. And that was caveated, it's one of two hundred signals,
it only effects about one percent of search results, but still it's a consideration and
page rank is really important. All of these things about a year, year and
a half ago I saw coming together and so I coined a new catch phrase for this emerging
industry called WPO. It stands for Web Performance Optimization. And actually as I mentioned,
I keep mentioning, we just had Velocity this week, there were a number. Sequoia, Battery
Ventures, Excel, Union Square were all there. This industry is really growing up. There's
a number of startups in this Web performance space now.
And the elevator pitch is it drives traffic to your site. We see that with search engine
marketing, with page rank. It improves the user experience. You would think that making
Web sites faster the user would get in and out more quickly, and it's just the opposite.
All of these studies show that page views per session, session length all increase because
the users are having a better experience. It increases revenue. We saw stats on that
with conversions, downloads, and it reduces operating costs.
So this all sounds pretty good, right? Web PO is really taking off.
All right, that's the preamble. How many people here think fast Web sites are good?
All right. So hopefully if you're having trouble selling that -- if this is stuff that you
want to work on and you're having trouble selling that back home, you can take some
of these case studies and show people back home that this is a good investment. You'll
actually get a positive ROI. And maybe these will give a little guidance on what you can
track on the business side to actually correlate performance improvements with the goals of
the business. All right. So let's transition into the meat
of the topic. I don't want to debate about what HTML5 is. There's the spec. I also mean
CSS3. It's the new stuff. So I'm going to highlight a few things around
HTML5 and CSS3 that I think are really important to pay attention to from a performance perspective.
So the first is if you want to optimize something you need to be able to measure it. And right
now what we'd really like to measure is the user's perception of speed. Currently there's
no JavaScript handler for brain activity. So what we've been using as an industry -- as
a proxy for that is the window onload event. Now, the problem with that is, for example,
if I do a Google search and I click on a search result that takes me to Whole Foods, and I
want to measure -- from Whole Foods' perspective, they want to know how long it took for the
user to click that link to when Whole Foods was ready. And the problem is tracking the
start time. How do you track the time that the user started by clicking on that link?
Because that's the user's perception time. And what you can do is at the top of the Whole
Foods page you can put a script lock that takes a time measurement, but that totally
ignores the time it takes for the request to go up to the Whole Foods server, for it
to stitch together all of the content and send that back down to the browser.
So about two years ago Google and Microsoft formed this Web performance working group
as part of the W3C, and the first task was to create the Web timing spec, which is actually
three specs, navigation timing, user timing, resource timing.
How many people here use navigation timing to measure Web sites?
Good. It's not good that there were so few hands up. It's good because I'm going to talk
about it a little bit. So those of three and I'm going to talk about
each one in a little bit more detail. They're also working on a few other specs, page visibility
I'll touch on quickly. Script-base animations I'm not going to spend too much time on, but
people are doing a lot of animation with JavaScript and they're working on making that better.
And efficient script yielding, since the browsers are single threaded, if you have long running
JavaScript it can really degrade the user experience. So what can scripts do to mitigate
that. But let me talk about the timing specs. So
navigation timing actually is out. The spec is done and it's available today. If you look
at window.performance, there's this timing object. And you can do things like from the
timing object you can get the navigation start and the load event end, take the difference.
And the nice thing is that the Web timing spec gives us two main things. This is the
first one. This is the time at which the -- the epic time at which the user clicked on that
link on Google. So now the user can -- the Web site owner can get the full time from
when the click happened to when the onload event or whatever event you wanted to. And
in fact if you put a little time stamp at the top of the page you can break that into
what I call the back end time, the time from the click to when the first byte arrived and
the time from the first byte to when the full page loaded.
And there's a bunch of other timings in there. You can get DNS time and connection time.
There are a bunch of time values that you can get in there.
So this has been out -- the spec was finished probably about six months ago, maybe a little
longer. And I think IE 9 -- no, I think Chrome was
the first to implement it. But it's in IE 9 and up, Firefox 7 and up. They had some
bugs in the Firefox 7 in the early days, but those are all fixed now. It's on Android.
And I heard a month ago it's on Blackberry, but I haven't tested that.
And here's a good URL from Tony Gentilcore that kind of explains how you could use it
and some examples of getting different time values.
So one thing that's really cool is Google is taking -- for page rank is taking time
into consideration, but when they announce that -- I'm really proud of the work from
the make the Web faster team at Google, where it wasn't like okay, we're going to hold you
responsible for it, good luck. We also rolled out several tools.
In Webmaster tools you can go in there and you can see how your site compares its speed,
compares to the rest of the industry. And really the folks who were going to get
penalized from a page rank perspective are going to be in the lower 10%, maybe even lower
than that. So maybe you're not awesome, but if you're in the top 50% you probably don't
have too much to worry about there. So that's a little help, but the other thing that's
nice to get more granularity, if you use Google analytics, we added this time measurement
capability in Google Analytics. I think we call it site speed -- yeah, site speed.
Yeah, this screen dump is a little old. I think the UI is a little difference now. But
the nice thing is when we first launched it you had to add a little bit more to your Google
Analytics snippet, and then about three or four months ago we turned it on for everyone.
So even if you had no awareness of the site speed, time measurement capability, you've
been gathering data for about five months now, four or five months. So you could go
to Google Analytics and go and find this site speed tab and you could get charts, and it's
the usual Google Analytics slice and dice capabilities by browser, by geographic region,
by page, and you can see what your performance is.
This is my Web site, so I don't know what was going on there, but that was awhile ago.
See, now it's fast. Well, in November it was fast.
Thank you for that. I appreciate that. [ Laughter ]
>>Steve Souders: Oh, I think I probably -- now it's on by default. Oh, it's only in browsers
that support nav timing, so we really need mobile browsers to start supporting this more.
Oh, it's sampled. So by default it's a one percent sampling rate.
If you have a huge Web site, like one percent is probably fine. And the guys have done a
really smart thing. Like they don't start it at midnight and if you're a huge Web site
you've used your one percent by 12:15. They, like, dole it out throughout the day. But
if you're a smaller Web site, you can change it with some Google Analytics commands to
cover 100% of your Web site, but it's limited, I think, to 10,000 measurements a day. But
again, those will be sampled throughout the day, so you won't get biases based on peak
time and stuff like that. And then I'm just going to talk really quick
about the other two parts of the timing spec. There's resource timing. And this is -- there's
drafts of this available, but this hasn't been nailed down. I don't think there's any
browser that supports it yet because the draft is -- the spec is still being defined.
But this -- if you think about it, navigation timing is basically this same information,
but it's at the page level. It's really high. And what we're doing with resource timing
is we're bringing that down to every HTTP request in the page. So for every HTTP request
in the page you can get DNS lookup time, connection time, total time for that to come back. This
is going to be a great mechanism for if you're using a CDN to track how your CDN -- or maybe
you're testing out multiple CDN's, to see how their performance is going.
It does have to worry about security safeguards. So you could have some kind of hack where,
you know, on my Web site I have the logo from some nefarious Web site. And when you land
on my Web site I can actually see what the DNS time is. And if the DNS time is zero,
I know that you've been to this other Web site. So there are some security safeguards
with HTTP headers. Web sites can allow or not allow cross-domain tracking of resource
timing. And then there's also user timing, which is
really important for people building Web2O apps, AJAXy stuff. Something like Gmail that
is open for several hours, there's no concept of a page load time. There's only one of those
when you start it up. So this is, if you're doing HXE stuff like
compose, this will give you commands that you can use to say the user is about to do
a compose operation, let me -- let me mark a start time and then when the compose is
all done, you can mark an end time, you can give it a label, and so it lets you define
and track any timing episodes that you care about in the Web apps that you are building.
So it basically is like nav timing but it's much more flexible and lets you define the
start and stop times whenever you want in your code.
The other one that I mentioned that they are working on is page visibility. This one is
important from a performance perspective. Mostly from a -- from an altruistic perspective,
but it could also affect, you know, what you are doing on your Web site. And so basically
what the spec let's you do is detect when your page is visible. So like one thing I
do in the morning when I come in, since I can't stand to wait for Web sites to load,
is I have this script and I -- you know, I boot up my machine, I load the script, it
loads my 30 Web sites that I read every morning and then I go get breakfast and when I come
back, all of the pages are loaded and I can just go through all of the tabs. But only
the top tab is visible. And inevitably when I come back, if I left my sound on, my office
manager, you know, giving me a dirty look, because there's some hidden tab that's playing
video. And the sound -- or maybe multiple tabs that are playing video and the sound
is just annoying and it's like really? Like you started the video, even though like the
tab is not even visible? Like why did you do that?
And the page visibility API introduces a way to detect whether or not the tab of the window
is visible. And so this is also really important, like,
if you are tracking metrics, like page impressions. If the user opened the tab but they're never
going to go to it, but you counted it as an impression, that's a miscount. Same thing
with ads. Again, the example of video. You might be doing something like showing them,
you know, stock updates or the latest messages from friends or you might be rotating through
a carousel of photos and they're not looking at it. So not only are you kind of -- like
you might rotate through a carousel of photos that started with their favorite photo, but
by the time they opened the tab they are down to their 5757th favorite photo and you have
kind of ruined the experience for yourself. So you could use it there.
Um, the other thing that you can do is -- this API can be used for pre-fetching resources.
So the Web site owner can decide and -- the Web site owner can decide if they want to
pre-fetch resources maybe for the next page and also if the page should be prerendered.
This is something that Chrome does. So Chrome can actually prerender a page in the background.
But one of the problems with doing that is, again, if there are ad impression counts or
page impression counts that are firing, and the page is being rendered in the background,
and the user actually never sees it, that's a miscount. So the page visibility API gives
people controlling those metrics the ability to only fire the metrics when it makes sense.
And I think this is only in Chrome right now. But what it looks like is you can say WebKit
hidden is the property, you can look at to see whether or not the tab is hidden and this
is an event handler you can attach to to detect when the page does become visible.
So we had script defer in IE for years, but now with HTML5 the async and defer attributes
are officially supported across almost every browser out there. I'm not going to go into
it here but in fact I've talked for years and written extensively about the impact that
scripts have on the user experience. So I mentioned earlier how the UI thread,
you know, the UI is single threaded, the browser is single threaded, so if you have JavaScript,
for example, that takes three seconds to execute, the user is clicking in the browser and they
are trying to scroll the window and nothing is happening because the JavaScript is blocking
any of that interaction. And so -- so that also happens when the script is being downloaded.
So if you have a 500 k JavaScript payload and the user has a slow connection, again,
it can take seconds for that to download. While that's downloading, the UI could be
blocked. Unless you can do your script loading asynchronously. So if you just do scriptsource
equals main.js it's going to stop the HTML parser from parsing past that script tag and
it's also going to block rendering. No browser in the world will render anything below a
script tag until that script is downloaded and parsed and executed. But you can use the
async attribute which tells the browser to start the download but to go ahead and continue
parsing the HTML and rendering the DOM elements in the page. So it gives the user feedback.
It's a better user experience. The user can see the page rendering, especially because
a lot of times we put scripts in the heads of our pages, which means the entire body
is blocked from rendering until all of those scripts are downloaded, parsed and executed.
And so this tells the browser, download it in the background, continue parsing; and as
soon as the script arrives, parse and execute it.
So one -- two tricky things about this is you can't have any document.write in this
JavaScript code because parser is already past that point. You are going to get very
bad and varied results across browsers if you do document.write in an async script.
The other challenge with async is suppose that I have three scripts, A, B, C, C depends
on B, B depends on A. But A is probably like jQuery, so might be my biggest script. C might
be really tiny, so if I load all of these async, which is the one that's going to come
back first? C. If I loaded async, it's going to be parsed and executed as soon as it comes
back and it's going to get undefined symbol errors because B might not be back and A might
not be back. You can't just willy-nilly add an async attribute to all of your script tags.
But what you can do is you can use defer. If it's JavaScript that isn't critical for
rendering content to the page, and doesn't have document.write can add the defer attribute.
It's pretty similar. It says download it in the background, let the parser continue parsing
and rendering DOM elements. After the entire page is done, parse and execute these deferred
scripts and do it in the order that they were listed in the page. And so if you have those
interdependencies across scripts, defer is a good fallback.
But -- but this still doesn't give all of the control that I think is really needed
to build a good user experience for -- for Web apps, especially on mobile.
For example, one thing that I would like to do is I would like to download maybe a big
script, 100 k, 300 k, 500 k, but not parse and execute it. It might be a script for something
like popping up a div to compose a message or address book stuff that is only needed
if the user starts typing an email message. But if I download that, as soon as it hits
the browser, the parse and execute is going to happen and it's going to lock up the UI.
So I really want to get this JavaScript down to the device, get it in cache, but parse
and execute it, depending on what the user does.
The other thing that I would like to have is a control over when the download happens.
Right now the spec doesn't say when browser should download the script. But if I've said
defer, if I've said async, certainly if I've said defer, I would like you to do this download,
after everything more important is already done, because there's a limited number of
TCP connections that the browser will make, and if you download scripts or multiple scripts,
then things like the background image of the page, the logo at the top of the page, might
actually be pushed out because you are already using the six TCP connections that most browsers
allocate to a domain. So we don't have control over those in markup,
but there are techniques that you can use to get that behavior. So this is one that
Gmail wrote about quite a while ago. Gmail mobile team. And it's a hack. I think it's
a beautiful hack. So what they did was in the page, they have
-- you know, maybe 300 k of JavaScript. I'm just making these numbers up. I don't know
how much JavaScript Gmail has. It's probably more than that. But they have a lot of JavaScript
in here and they just wrap it in -- in comment delimiters. So what happens is this will get
downloaded to the browser, it will be cached, it's already resident. But it's not parsed
and executed. So -- so let's say this is compose a new message JavaScript. It pops up in div,
it formats everything, it does error detection and things like that. So we don't really need
this JavaScript to block the UI thread for parse and execution until the user actually
clicks on the compose button, which they may never do during this session. So what we do
is we download the code because we want it on the device. When they click compose, especially
on a phone, we don't want to wait to download 300 k of JavaScript, we don't want to make
the user wait. So what we do is we download it, you can even download it in the background.
It's not going to block the UI thread when it arrives. Now the user clicks compose, you
have all of the JavaScript on the client and now you just find the DOM element for the
script tag, remove the comment delimiters and eval the code. A lot of people say eval
is evil. Doug Crawford is here today, I saw him downstairs, he'll tell you that. Certainly
eval is evil from a security perspective. If you are eval'ing code that you didn't generate,
that can produce really bad things. You wouldn't want to do this with third party widgets and
ads, et cetera. So this is code that you control, that you generated. So from a security perspective,
it's okay and the actual performance of eval is less than 10% worse than just a script
block. So from a performance perspective, it's actually down like around one or two
percent. From a performance perspective, it's fine. So you control the code, performance-wise
it's good. So this is a -- this is a really nice hack.
And it's perfect for pre-fetching JavaScript that you might need but you are not sure.
It's possible that you will never need it. So don't block the UI thread, especially for
large amounts of JavaScript. You know, this -- might require a fair amount
of rewriting on your Web site. So I wrote something called ControlJS, that's open source,
you can get it off my Web site. Several big companies -- I was just at this conference,
and I found that -- including Wal-Mart, I found several big companies that were using
this that I was really surprised at because I don't do a lot of testing.
[ Laughter ] >>Steve Souders: I trust that they've done
that. And actually, who else -- Washington Post
is using it. And I asked them to contribute back the robustness changes they had to add.
And so the thing that I like about this technique is that it's all done in markup. So we can
just change -- instead of script source, having type text script, we would say text CJS, and
instead of a source we have a data control.js source, so this means that the browser will
basically just ignore this and you can also do it with inline scripts just by changing
the type. Then you load control.js. What it does is it crawls the DOM, and it
finds these script nodes and it does the right thing. It downloads it asynchronously, it
does it in the background. Again, you would only do this with scripts that aren't critical
for generating the initial user experience and scripts that do not have any document.write.
But I think it's a little easier to get on top of. You can even add this execute false,
which says downloaded but don't parse and execute it. And then later when the user,
for example, clicks on the compose button, you can say control.js execute script. So
that's kind of a nice alternative. The other thing I like about it, how many people here
have played with -- with JavaScript loader, script loaders, lab js or YUI lowered. Some
people -- the thing that I find ironic about all of the other alternatives out there is
their goal is to help you load JavaScript asynchronously. They do that with a helper
script, but you have to load their helper script synchronously.
[ Laughter ] >>Steve Souders: Anyone else think that's
weird? Like, you know, synchronous loaded scripts are bad. So you load our scripts synchronously
and we will help you avoid that problem. [ Laughter ]
>>Steve Souders: Like -- so from the beginning, one of the requirements of ControlJS was that
you could load it asynchronously. So you would use the Google Analytics, async loading pattern
and load the script anywhere you want in a page and everything will still work.
So JavaScript is a really, really big problem. I'm -- make sure that you look at how it's
impacting your Web site and if possible adopt some of these asynchronous loading techniques.
Okay. Shifting gears, I wanted to talk about app cache, which is good for offline apps
and also for longer caching. So again, the importance of the background
photo. You are in the desert. You typically don't have a good connection. So having the
offline apps is really important. [ Laughter ]
>>Steve Souders: There we go, the groan. So building offline apps is really cool. I think
Google docs just announced that this week or last week. It really helps with the user
experience when you have a bad connection, you are flying, whatever. But also people
are using it from a performance perspective for better caching.
So this is a study that I did five or six years ago with (saying name) when we were
at Yahoo, it's been a long time, but I've talked to people at other big Web companies
who have run the same experiment and they basically get the same results. So I encourage
you to run it on your Web site. Basically we put a hidden pixel in every page, we tracked
how often people made a request with or without an if-modified-since header. If they send
the if-modified-since request, it means they have it in the cache. If they don't, it means
they don't have it in the cache. So we can track how many people coming in, how many
page views are done with a full cache, a prime cache. Our resources are in their cache or
with an empty cache, our resources are not in their cache.
I talk about in my best practices setting a far futures expires header. Turns out even
if you do that, browsers aren't going to keep things around for that long or for whatever
reason, users are going to come in and they're not going to have everything in cache that
you might expect. So what we see here is that for page views,
if you look at it from the page views perspective, about 20% of page views on any given day are
done with a -- with an empty cache. Right? My resources are not in the cache.
You can see on the first day, when we put this pixel on the page, 100% of the pages
had an empty cache because this image had never been loaded before. After about two
weeks, we hit a steady state and we can see it's about 20%. We ran this on various properties
at Yahoo that had different user metrics. This was always about the same. This number
fluctuated a little bit more, but it was always between 40 and 60%. About half of your users
come in at least once a day with an empty cache. So why are these numbers different?
Typically, users are doing multiple page views in a session. So even if they have an empty
cache, they will come in and have an empty cache page view, which will show up here and
show up here. But then they might do three or four more page views, which are going to
be a prime cache, because when they did their empty cache page load stuff got put into the
cache. So that's why these numbers are different. But it still means people anchor on negative
experience. So if half of your users were coming in every day with an empty cache, and
you are not taking that into consideration, you are like, well, yeah, it's not 300 k image
or script but it will be in the cache, for about half of your users, it's not going to
be in the cache. So you can run this experiment yourself and you will get the same results.
So the cache, even if you are doing far future expires, the cache might not be doing what
you think. And -- and here's a great study from Guy Poe over at Blaze, now Akamai, where
he shows that mobile caches are very small. This is another reason why things you think
should be in the cache won't be. This was a study I did where I found that -- I like
building Web apps and putting them on the home screen and turned out in iOS if you do
that the cache isn't used optimally. So you can use app cache for caching.
So the way they use app cache at this manifest attribute, you give it a file name, which
is your app cache. Manifest is a good name I think we all intuitively recognize what
we are going to see here. Here's your manifest file. It has to start with this cache manifest
header. We will talk about the revision number in a second, but you'll want to add that.
It's got these various sections, a cache section says these are the things that I want you
to put into app cache. The network session says these are the things that you should
never put into app cache. The fallback section says if the user is online, then use this
URL. But if they are offline, read this one from app cache. So the browser, when the user
is online, will download this one, but will also download this one and save it into app
cache. At least right now, you have to make sure to give this a very specific content
type. Text/cache-manifest. So it looks pretty good. It turns out actually it's really, really,
really hard and complicated to work with. So here are some of the gotchas that you will
probably run into. I know I did. I didn't realize -- like, I never listed my
HTML document itself, .php, .HTML in the manifest file, so I just assumed it wouldn't be put
into app cache. But if your HTML document uses the manifest
attribute, then it's going to be saved to app cache. This was really confusing for me
because I had a log-in box on the front page. And if the user came in and their app cache
was empty and they weren't logged in, the logged-out version of the page would be saved;
every time they came back, that's what they would see even after logging in. So that's
a little confusing. If anything in the manifest list 404s, nothing is saved. You get about
five meg, which is pretty good. Yeah, so this is interesting, suppose you change one of
the resources listed in the manifest file, like an image, you would think that the next
time the user opens the app, they would see that new image. They don't. You actually have
to change something in the manifest file to make it different, and that's why the revision
number was in there on the previous slide. That's what I do is I have that automatically
updated whenever I update through source code, through my source code control system, it
will automatically update that revision number, so the manifest is always touched whenever
I update a resource, and this was really confusing to me. Even if you do, you know, rev the manifest
file, the user still won't see the change until the second time they open the app. So
I think this is really interesting, and it's a real gotcha', so I wanted to walk through
it in more detail, so let's suppose you go home today and you build mobile Web app that
uses app cache, or a desktop app that uses app cache, and it's got this awesome green
logo that the designer gave you, and you list that into your app cache manifest file, so
you push that tonight, tomorrow, the first user ever downloads your app, and the app
cache is obviously empty. They've never seen this before, and so the browser fetches the
manifest file and in there the logo is listed so the browser downloads that, puts it into
app cache, and renders the app to the user so the user sees the awesome green logo. So
now suppose tomorrow night you go, you know, I really think an orange logo would be better,
so you push to your server this new orange logo, and you remember to change the version
number in the manifest file. So the logo is new, the manifest file is new. And on -- today's
Friday, so you do that Saturday night, so on Sunday, the user -- same user loads the
app again, and the browser says, okay, well, this app has a manifest file associated with
it. Do I have any of those resources in my app cache?
Oh, I do. I need logo.jif, or i have logo.jif in my app cache; it's this pretty green logo.
Even though I've already pushed an orange logo to my server, the browser is ignoring
that. That's why it can work offline. It can say I have this stuff in app cache, let me
load as much as I can without using the network. And so it displays that app cache logo to
the user. But now what it does is it's -- once it's rendered the app with the old out-of-date
logo, it fetches the manifest file, it detects that it's changed and so then it checks all
other resources in the manifest file and it gets the new version of the logo which is
orange, it saves that to app cache, so if the user reloads the app, a second time, it
will -- the browser will look in app cache and now it has the orange logo, so it renders
the orange logo to the user, it fetches the manifest file which hasn't changed and so
it's done. So it's really taken these two loads -- after I update a resource it takes
two loads for the user to see the changes. Now, there is a work around to this, it's
not that hard, but not that easy. There's this update ready part. The implementation
is pretty easy. I can track this. But it's what do I want to do with the user experience?
So in the background, the browser detected that there was a new logo, and it fires this
update ready, but are you going to tell the user to like reload the app to see the changes?
I don't know, you have to decide for yourself. Maybe if it's just images it's not that important.
If it's like an important JavaScript security privacy fix, then, yeah, maybe you want to
tell them to reload it, or you want to reload it without even telling them, just reload
the app. Another way to do improved caching because we know the browser cache doesn't
work super awesome is local storage. Really simple API. Local storage is persistent across
sessions. If you only care about sessions, you can use session storage. It's about five
meg. One warning, browser developers really are worried about this getting too much use
because it does a synchronous read. Some browsers will actually read everything out of local
storage, the first time you go to that page during the session, and if you have a lot
of stuff in local storage, again, it's single-threaded so that can slow down the user experience.
So I built this bookmark that lets you look into what is stored in local storage, and
I discovered a couple of interesting things that Bing and Google search were doing, which
I hadn't thought of, and it's very cool to help with caching. So I think this is the
Google search -- Google mobile search implementation. The first time you do a search, the HTML document
will return all of these inline script and style blocks, a lot of JavaScript and CSS,
and when it's done, it iterates over those script tags and style tags that have IDs,
and it takes the content of the block, and writes it into local storage based on the
ID of that element. So now, after I've done this first search ever on my phone, I have
blocks of JavaScript and CSS in my local storage, so now let's say I do another -- oh, and the
other thing it does is it sets a cookie that says which blocks of JavaScript and CSS have
been written into this user's local storage. So now if I do another search, this cookie
goes up to the server, and the server says, oh, well, you're doing a search, I have to
give you this block and that block and this JavaScript and this CSS. Let me see what you
already have in local storage. It looks at the cookie and says, oh, you already have
this block, I don't have to send you that, I don't have to send you this CSS, and for
-- in the case of mobile search, it drops the download size from about 110K to just
10K, because all that stuff is now in local storage. And then when the lightweight page
arrives, there's some JavaScript that will pull all of these things out of local storage
and write them into the page. So this is a really cool way to get more persistent cache.
Local storage is unaffected by other Web sites whereas the shared browser cache, if the user
visits a bunch of other Web sites that have large resources, your content might get pushed
out. So I did a quick survey using this is what this -- I'm a good JavaScript hacker.
I'm not a good UI designer. This is my awesome storage or bookmark list, and you can see
it lets you look at what's in the local storage and session storage, tells you how big it
is, how many items it is. So let's just do a quick run through the Alexa Top 10 and see
how local storage is being used. So here we have -- Google search is using it on both
mobile and desktop, so that's one. Bing is using it on mobile. Facebook is using it on
mobile. Yahoo is maybe using it a little bit, but not really. We're not going to count that.
YouTube, yeah, we'll count that. Quite a bit of content on mobile. Amazon, no. Twitter?
Yeah, okay, we'll count that. Five. LinkedIn, no, I would say. eBay, no. MSN, no. So about
five or six of the Alexa Top 10 are using local storage, so that's an indicator that,
you know, it's worth investigating. Why is it more -- maybe used more prevalently on
mobile? You know, the experience of not having something
in the cache in mobile is worse, because the network connection speed is so slow, also
the cache is smaller on mobile, so caching isn't awesome on desktop, but is much better
on mobile, so I think that's why these top properties started using local storage on
mobile, but I would expect to see this proliferate on desktop apps as well. So I want to talk
about font-face. Again, pay attention to the photos, not to what I say. So, you know, fonts,
using custom fonts can create a more compelling, beautiful experience, but people haven't really
paid attention to what it does, similar to how scripts and style sheets can block a page,
what custom font files do, and so I created this table that here's the blog post, that
shows how scripts and style sheets and fonts can affect, and, you know, "blank below in
red" means that everything using this font or below it are going to be affected. Flash
-- oh, delayed means -- blank is really bad. It means like everything in the page is blank.
Delayed means just the element that's using the font file is affected. Flash means once
the things -- things will render, but once the resource arrives it has to be redrawn,
and so it's kind of a jerky user experience, things have to be rerenderred and repainted.
So you can see the impact. Even the green "good" is not really a good experience. And
so you want to use fonts kind of carefully. And it's kind of ironic, it's kind of how
these async script loader libraries make you load their scripts synchronously. When I talk
to designers about why they're using custom fonts, even though we know it has this impact
on the user experience, they say, well, these are the most important design elements in
the page. I'm like, "so you want the most important parts of the page to take the longest
to render?" Like, that doesn't make sense to me. So despite
my warnings, custom fonts have taken off, like they've more than doubled in the last
year, this is a chart from the HTP archive. So they're taking off. The good thing is the
folks at Typekit and the Google fonts library have done a lot of good work to mitigate the
impact that custom fonts files have, but we can still go farther. In fact, I think Google
fonts library just announced custom fonts a few months ago. If you don't need -- like
if you're only using digits or other certain characters, like it's just a word that you
want to render in that font, you can actually create a smaller font. The font files are
typically like over 100K, so you can create one that is just the characters that you need.
The flash of unstyled text means you draw text and then you have to redraw it, and so
what I would propose is that browsers have like an impatient fallback, like if -- I don't
want to have this flash of my text, so I'm going to wait 250 milliseconds for the font
file. If it hasn't come back and we're drawing in a default font, and even if the font files
comes back later, I'm not going to redraw it. I'll cache that font for the next time
the user goes to the page, but I'm not going to do that, and again, I think this is a behavior
that the Google font library and Typekit loaders do automatically for you. Yeah. And also,
okay, I'm running short on time, so I'm going to go a little faster. Font should be given
a higher priority in caching. Browsers are still just starting to pay attention to that,
and even if a font is expired, I might want to render with it, and do a "if modified since
request", and then if I get back, "oh, not modified?"
It's okay, I've already rendered with a font. All I have to do is validate it. Oh, good,
I'm getting close to the end, so there's a lot more stuff I didn't talk about. We -- you
know, all of us spent a lot of time building rounded corners that downloaded a bunch of
images, so now you can do a lot of things that used to require HTP traffic, you can
do with CSS 3, so that's cool. You have to be a little careful, these incur, you know,
might incur a lot of CPU overhead or repaints. You can use SVG and canvas instead of downloading
images. Video tag could make starting up videos faster. Web sockets, if you have a very chatty
sort of app can be used. Web workers, if you have like a lot of computation that you want
to get out of the UI thread, you can use Web workers, oh, for some things that we used
to have to write a lot of JavaScript or HTML for, you can do with these new built-in controls.
Again, the photo, more, infinite number of digits. History API, we used to have to implement
this in JavaScript. Now we don't have to download all that JavaScript. Ping is a way to avoid
a redirect if you want to track something. Don't use set time-out for animation. Use
request animation frame. It will be faster. You can use the native JSON parser, and here
are some good resources to get more information about this other HTML5 stuff. So what are
the takeaways? Speed matters. Hopefully we all agree about
that. I saw you raise your hand before. So I'm going to hold you to that. Pay attention
to what's coming out of the Web performance working group out of the W3C. You can use
window.performance to track the timing on your pages, and even if you haven't done that
yet, if you're using Google Analytics, you can go and see the data there. It's already
been gathered for the last few months. JavaScript blocking the UI thread is really critical.
Try to get a lot of that stuff deferred or async, and caching is really important. I
think it's one of the most important things for a fast user experience. Browsers are improving
their cache. Mobile browsers have a lot of room to catch up on, so you can try some of
these other techniques. And be careful about using font-face. So before I close out, I
want to mention, if you like this kind of performance stuff, I'm sorry, but you should
have been at Velocity this week. We just finished that Wednesday. But if you missed that, you
could go to Web PERF days, the first ever, but -- well, actually, that was yesterday.
Okay, so you missed that one. [ Laughter ]
>>Steve Souders: Oh, okay, we've got Velocity Europe coming up in October, so you might
have to go a little farther, but -- and we'll be back next year in June for Velocity. I
also wanted to just mention this -- oh, I added this like five minutes before I came
on stage. How can I do this, like -- I don't know, I have to go through them all. So (indiscernible)
runs this performance calendar every year, and O'Reilly took the most recent performance
calendar and put it into a book, so this just came out this week, and you can get most of
this content on the blog for the performance calendar, but the -- they were updated, and
if you like books, you can get it in book form here. So that's it. Thank you very much.
[ Applause ] >>Steve Souders: So I've got about four minutes,
I would be happy to answer any questions and I think they want you to use the mic. Everyone
is heading out. >>> Would you care to expand upon font awesome?
And also to resolve some of the issues of pop-in, I found setting a fixed type for whatever
text I decide is very nice. >>Steve Souders: What's the first one? Expound
on... >>> Font awesome, using fonts instead of sprites?
>>Steve Souders: Oh, yeah, so you can create a custom font, you know, file, that has little
pictures, you know, kind of like wings -- what is it on windows? The wings font, wingdings,
yeah, so you can do that with a font, so if you have like really small images that you're
using now and you're downloading them as HTP requests instead of images, instead you can
download a font fall. It doesn't have to be huge. Maybe you have 10 of those or 20 of
those, and you can download those and use those to draw those little images, those little
sprites in the page for like maybe little buttons or things like that, and so, you know,
I think that's a great idea. You could also use sprites. Sprites are maybe a little harder
to do. You have to create the image and know all the CSS, but the one thing I would say,
if you're using any kind of font file, is try to figure out a way to do it that doesn't
block the page, like especially in IE. Custom font files block rendering of everything in
the page, so if you could do that lazily or something like that, that would be good, and
then the second one was... >>> Just a tip to deal with font snapping
in after they loaded, the ugly hack of just setting a fixed type for everything. So at
least it doesn't like reflow. >>Steve Souders: Yeah, yeah, you could do
that. Yes, another question. >>> Yeah, you said mobile browsers don't support
most of those features yet, custom, you know, desktop browsers either, so how would we test
them? >>Steve Souders: I guess I wasn't clear. Everything
I talked about is supported in mobile browsers, pretty much, and, in fact, you're going to
get -- I think that's another reason why we're seeing more HTML5 stuff on mobile and desktop,
because on desktop, there's still a lot more lingering IE6, IE7 legacy browsers out there,
so you're almost a little safer using the cutting edge stuff on mobile than you are
on desktop. The only thing I can think about is I thought there was something -- well,
the page visibility API is only in Chrome, but all the timing stuff, local storage fonts,
app cache, async and defer attributes, all of that is supported on mobile too.
>>> Thank you. >>Steve Souders: Yeah. Okay, let's go here.
>>> Let's see here. You were talking about visibility and document.webkit hidden and
how that relates with click tracking and stuff like that. One click unsubscribes and e-mails
and things like that, is that another way that we can avoid being automatically unsubscribed?
>>Steve Souders: Oh, oh, well, but how would you -- how would you -- what's the scenario
where you would render unsubscribed page without the user seeing it?
>>> Well, if they're following links and pre-loading in the background, you might follow --
>>Steve Souders: Oh, yeah, the pre-rendering I was talking about is the developer or user
has explicitly asked to open a page, but we can just know we can do it in the background.
But, so, yeah, if you're crawling a page, that could be something that you could do
is to make sure not to -- you know, you could instrument an unsubscribed link so that it
only worked if the page was visible. Yeah. >>> Thank you.
>>Steve Souders: Yeah. >>> What's your recommendation in terms of
moving the script tags physically to the bottom of the body tag versus using the defer script
tag that you described? >>Steve Souders: Yeah, I think both of those
work. One thing that we -- I only have a few seconds, so one thing we don't have time to
talk about is the importance of the onload event firing as quickly as possible, so in
both cases with defer and async and putting scripts at the bottom, you're going to block
the onload link, the onload event from firing, so really putting scripts at the bottom is
almost the same as doing defer. It really has more to do with kind of the size of your
team. A lot of times there's, you know, 20 or 200 people working on a property, and so
it's hard to get the message out to put scripts at the bottom or like someone's code is only
going to be executed at the top and be really hard to give them a foothold in the top and
give them also a foothold in the bottom and make sure that both of those are done in sync,
so if the only place like the logging or metrics team has a place to -- a foothold in the page
is at the top, they can get the same behavior using the defer attribute as just putting
the script tag at the bottom. Putting the script tag at the bottom is another lightweight
way to avoid that blocking of the UI thread. Okay. So I should wrap up. Thank you very
much. [ Applause ]