Tip:
Highlight text to annotate it
X
>> NICHOLAS ZAKAS: I just wanted to start out by explaining a little bit about the book,
some of the topics, and then we're actually going to have some of the authors come up
and present the topics that they wrote about to explain a little bit more.
The authors are myself, Ross, who you heard from looking to get some help over at Flickr,
Julien Lecomte from Yahoo! Search -- is that right? -- who is also the creator of YUI Compressor,
Steven Levithan, who's not here, unfortunately; he's not local, but he knows more about strings
and regular expressions in JavaScript than anybody that I've ever met or talked to. So
that's what he ended up writing about. We have Stoyan Stefanov, also from Yahoo! Search,
and you may also know him from YSlow and a bunch of other performance related stuff he's
done here at Yahoo!. And Matt Sweeney, who's an architect here on YUI.
There are just ten chapters, and we tried to take you from the beginning of writing
your JavaScript, getting it onto the page, optimizing it for runtime, and getting it
out into the wild. These are the topics -- I'm not going to do the annoying thing and read
all the bullet points off of it. Some of the chapters are written by me, various chapters
are written by other people, and we're just going to focus on a few of those tonight.
We'll start off with Stoyan, who's going to talk about DOM scripting. Then we'll have
myself; I'll talk about responsive interfaces, how to keep the browser nice and quick. Then
Ross is going to talk a little bit about Ajax, and we'll have Julien talking about build
and deployment. Then we'll have Matt talking about tools. I did feel a little bit bad that
Steven couldn't be here, so I wanted to give him a little shout out. This is his blog -- it's
really great, lots of interesting stuff on there. He's also the co-author of the Regular
Expressions Cookbook from O'Reilly, so he really knows his stuff really, really well.
His latest blog post is about some of the mistakes that were made in the JavaScript
regular expression API, really interesting stuff.
Just before we get started with Stoyan, I just wanted to answer the question that I've
been asked a lot since this book first came out, which is: does this really matter? JavaScript
engines are getting so much faster, so should we even care about performance? The answer
is yes, because compiling JavaScript only solves some of the problems that we deal with.
If you think of it now, five years ago we had a bunch of applications on our desktop
that we thought were kind of slow, and then all of a sudden memory gets cheaper and cheaper
and processing power gets cheaper and cheaper and we expect everything to get fast. I don't
know about you, but I see these applications every day on my Dual Core Processor that are
really, really slow.
So there are still things you can do, even with all of these latest JavaScript engine
optimizations, to make your code slow. There's always going to be bottlenecks. The book tries
to cover not just things that might eventually go away in the future if JavaScript engines
catch up, but also the things that you need to keep an eye on, the things that you may
just make mistakes in as you're trying to write a ton of JavaScript. With that, we're
going to bring up Stoyan. We'll have a brief switch of computers, so please bear with us.
>> STOYAN STEFANOV: Hello everyone. Great to see you all, thanks for coming. I'm Stoyan,
and I had the honor of contributing one chapter to Nicholas' great new book. It's about the
DOM. Tonight, I want to talk about a topic which is: the DOM is slow. I'm going to go
through how slow, exactly, are we talking about? Why is it slow, what's slowing it down?
And what can we do about it?
Let's start first with one sort of bad example. What's bad here is we're accessing the DOM
element and updating its innerHTML so that we're touching it once to read the value and
once to update the value. We do that in sort of a big loop, just to see what happens. We
have here two accesses to the DOM per iteration times the number of iterations. The better
way is to just use a local variable and do all the updates to that local variable, then
once you're ready, update the DOM. That means you're only touching the variable two times.
It looks really simple.
Let's see how bad that could be. Just to give you an idea of all the graphs in the chapter,
something like this I tested across a bunch of browsers. I'm not comparing them in any
way -- it's not really about milliseconds and so on, and which one is slow and which
one is fast -- that doesn't really matter, because your application will run there and
there's nothing you can do about it. My idea was, given two ways to do the same thing,
which one will you prefer? Which one will be faster than the other one?
In this case, if you do the bad way, if you touch the DOM so many times, it's going to
be 155 times slower in IE. That's not percentages, that's really slow and bad and horrible. Then
what I wanted to check is, is it consistent across all the browsers? It turns out that
to one degree or another, that's the story across all the browsers. So it's slow everywhere.
Even if we look at the smallest bar up there, it's still 20 times slower. It's ridiculous.
Why is that? Why is it slow? I heard a comparison from John Hrvatin from Microsoft that you
have something like a bridge. When you're in your ECMAScript land you do your loops,
and functions, and objects, and with all that you're staying within the ECMAScript. Then
when you have to access an element in the DOM tree, you have to cross a bridge. Be careful,
there's a special effect here, so you don't want to miss that on this slide. And this
is a toll bridge, so every time you cross that bridge then you have to pay a certain
penalty every time. So the more you cross it, then the slower you get. This is the special
effect.
[laughter]
Going to DOM land and the toll and coming back to your JavaScript manipulation. This
was only for accessing the DOM, right, so what about when you update stuff? Because
you changed some things here and there, added a new element, and so on. In order to understand
that, let's take a look at what the browser does.
First, it gets super fetched [?] HTML tags and tries to make sense of it all and create
some DOM tree. This is pretty well understood, right? We can see the DOM tree in Firebug
and so on. Then it gets a bunch of style information -- user agent style sheets, user styles, document
page styles, inline styles -- and again, makes some sense of it, removes the syntax errors
and all that and creates some sort of a structure.
Then it proceeds to building this rendering tree. You could think of it as something like
the DOM tree, but not every element in the DOM tree corresponds to an element in the
rendering tree. So the rendering tree's just a bunch of boxes, and those boxes will be
painted on the screen eventually. For things like the head element, invisible stuff, you
don't have a corresponding element in the rendering tree, but for some other stuff like
text, you might have several lines of text and several of those boxes. Then once the
browser calculates all the layout, then it proceeds to painting it on the screen.
What happens when you update the DOM -- or any of the styles -- and you change something
on the page, it has to go through this process and re-paint a portion of the page, or the
whole page. Also, the user might affect that by resizing the window, changing font size,
and causing those re-paints. Some of the changes you do will affect the geometry of the page
-- width, height, padding, borders. In this case, the browser has to recalculate part
of that tree, some of those boxes, and get all the dimensions. This is what we call reflow.
Not all the changes require reflow, only some of them, because it's kind of an involved
process and can be expensive, so the browsers are smart enough to optimize this to avoid
doing all of this work of constantly painting and reflowing. So they actually optimize.
When you change several styles, the browser will not apply them right away, but will create
a queue of all the things you want to change. Then after you reach a certain number of items
in the queue, or after a certain timeout, it will just flush the queue and do several
changes at the same time. You can actually bypass this optimization and slow things down
by requesting computed styles, offset, basically requesting any layout information. In this
case, because the browser is kind of a single threaded thing, it then has to flush the queue
in order to give you the most accurate body, because when you change something you expect
when you request the computed style to be updated.
The DOM tree and all that was pretty well understood, but they still had no idea what
was going on with when the reflows happen, re-paint, and so on, until recently. There's
two excellent tools: one is dynaTrace, Ajax edition for IE, and the other one is Speed
Tracer for Chrome, from Google.
Here in dynaTrace, in addition to the normal waterfall, the network activity here, you
also get when rendering activities are happening, when some JavaScript is executed, CPU utilization,
and all kinds of very, very detailed information. So detailed that it's kind of hard to look
at, because there's too much of it. In this case, there is some JavaScript event and something
is being redrawn. You can actually zoom into that and this is actually zooming into that
part that was changed. You can see the blue thing is your JavaScript function being executed,
and then you can actually click on that and see the exact function that was called. Then
you see that the browser spent some time calculating the flow, which means reflowing, calculating
dimensions and whatnot, and then painting. Even in this simple example you can see how
long the painting is, much longer than whatever you could do in JavaScript.
It's good now that all the browsers try to speed up, and they're competing in JavaScript
performance in the ECMAScript sort of stuff, but it's good for them to also start optimizing
this stuff. In IE9 they've announced that they're now using the GPU -the Graphics Processing
Unit -- when it's available so that they can help with styling and reflowing and making
it faster.
This is Google Speed Tracer. Again, you have a list of events that happened. You can see
some click happened, then there was some style calculation, and eventually some painting.
Then you can expand also and see more detailed information about when style was calculated,
when the layout was calculated. So in Chrome you can have the difference between just changing
a style, like a background color, something that doesn't affect the geometry at all, versus
changing the layout.
You can see here what I was mentioning, that you can actually flush the queue and make
the browser perform everything right away. The bottom is the good example. You change
a bunch of colors at the same time and then you ask for the computed styles and the layout
information. But if you do one change, one request for a computed style, another change,
you're actually flushing the queue every time. And you can see in Speed Tracer you have style
calculation, style flow, where here it queued all the three, then when you requested the
computed style it calculated the styles and the geometry only once.
What to do about it? I'm not going to go into many details; it's all in the book. I want
to give you 10 commandments. Don't touch the DOM. It's true, not really helpful. The next
time when the client says 'OK, I want to change that color to red when there's an error',
and you say 'no, no, I'm not touching the DOM, forget about it'.
[laughter]
One thing to do is to cache the DOM references in local variables, so when you cross that
bridge you pay your toll and store that in a local variable and work with that. Again,
in the different browsers it's different. It can be anywhere from 2 times faster to
200 times faster.
Selectors API. Use selectors API when they're available, or make sure your library is using
the selectors API; just update the JavaScript library. Because that's always going to be
faster than anything you can do to find the node that you want to work with.
Caching the length for collections. Write collections when you do document get elements
by tag name, so this is an HTML collection. When you look through that, if you check if
I is less than the length of the collection, then because it's a live collection, it has
to check the document every time. So when you cache it before the loop, it don't execute
once, and it's again, 2 to 200 or 300 times faster, depending on the browser.
When you have a bunch of DOM changes, take the DOM element off the tree, perform your
changes offline, and then put it back in the tree. As opposed to when you have a table
with let's say 100 rows, you don't want to be adding one row, another row, to the live
document. You just want to create everything offline in a document fragment, or a clone
if you're changing an existing node, then put it back in the tree.
Style changes. Again, you might be causing more reflows. So when you have to update a
bunch of styles, the cleanest thing is just to update the class name. But sometimes you
can't do that; you don't know the style in advance if it's a drag and drop or something
like that. You can change the CSS tags property and pass in a string of all the stuff you
want to change.
What I talked about -- asking for layout information offsets scroll amounts, and any computed style.
In the previous example, if you saw, you're actually changing something in requesting
some totally unrelated computed style, but the browser still has to flush that queue.
Minimizing reflow areas. When you're about to change something, think about how much
this change will affect the rest of the page. Is it going to reflow the whole page, or just
a part of it? For example, if you have at the top -- it's kind of popular now with Firefox
-- it gives you messages, so people do that sometimes so that it has to animate and reflow
the whole page. It might be a good idea to position that absolutely, animate, and then
move the rest of the queue.
Using event delegation always helps. If you haven't looked at the YUI 3 delegate method,
it's really good. You can pass it a selector for the wrapper that you want to listen to,
then you pass another selector only for the elements that you're interested in. So it's
really good stuff.
You can see I've struggled to make them 10. [laughs] The last one is, again, you know,
don't be slow. So think about it. Every time you type 'document dot' a bell should ring
and say oh, I have to pay for that.
Thank you very much.
[applause]
>> NICHOLAS ZAKAS: One of the things I found interesting about JavaScript is how it affects
the user interface of the webpage, just in general. Especially now that we put so much
JavaScript on the page, I think that we don't really recognize just what sort of impact
that can have on the user. I wanted to talk a little bit about how to keep the user interface
really snappy and responsive, and to do that we have to dig into the browser a little bit.
I'll warn you ahead of time -- some of this is an oversimplification. If you want to talk
actual details, you can catch up with me afterwards. But I just wanted to get these simple examples
out there to get you guys thinking about what's really going on underneath the covers.
There's this thing which is sometimes referred to as the UI thread in browsers, or the UI
process, and it basically has two jobs. The first is to draw UI updates. Whenever anything
changes on the page, or when the page is first loaded, the UI thread goes and draws this
stuff. The stuff that Stoyan was talking about, with reflow, repaint, these are all jobs for
the UI thread to do. Interestingly, the UI thread is also responsible for executing JavaScript.
So it has two jobs instead of just one, even though it doesn't seem immediately apparent
why that would be.
The thing you need to watch out for is that only one of these jobs can actually happen
at a time. You can't update a UI while you're running JavaScript or vice versa. It just
doesn't work, and I'll explain a little bit about why that is. If there's a job that's
already executing on the UI thread and another one comes along, then that one ends up getting
queued someplace so that it can be executed in the order in which it was received. Kind
of like when you call and you're put on hold, and it says you'll be dealt with... Nobody's
even chuckling at that? OK.
So you have some basic code that's just to have a button do something. This is it, my
really big button, which actually does nothing. But there are a few things that are going
on here. There's a couple of UI updates that are going on, first when I hit the button
down and the button actually goes down, and then when I release it and it comes back up.
So it's two draw operations.
This is what is actually happening when you click on the button. There's a UI change job
that says hey, draw this button in the down state first, and then after that there's a
JavaScript execution job for the onclick handler for that button. After that there's OK, draw
the button now in the up state. These all happen behind the scenes, as soon as you click
on the button. If we think about this logically, we have the button just sitting out there,
and then there's a UI thread. Assume up here that this is sort of a timeline of the UI
thread so you can tell what's going on. Before the click, there's nothing going on. The page
is completely loaded, there's no JavaScript being executed, it's just waiting to do something.
So you click, and immediately these three jobs get added into the UI queue.
[Audience member makes inaudible comment]
>> NICHOLAS ZAKAS: Clarification, yes.
>> AUDIENCE MEMBER 1: So it's actually a JavaScript thread that's [inaudible]?
>> NICHOLAS ZAKAS: So is the question: does the JavaScript... Sorry, can you say that
again?
>> AUDIENCE MEMBER 1: Are you basically saying that there's a JavaScript thread, so is it
the browser that [inaudible]?
>> NICHOLAS ZAKAS: Let me see if I can summarize this correctly. Your question was: if there's
one JavaScript process, is the browser making that busy so JavaScript can't do stuff? No.
[Audience member makes inaudible comment]
>> NICHOLAS ZAKAS: Yes, the fact that it's being redrawn is by CSS, or just by default
browser rendering behavior. It doesn't have to do with JavaScript.
>> AUDIENCE MEMBER 2: It's still one thread?
>> NICHOLAS ZAKAS: Yes, it's still one thread, one process. Any other questions about that?
OK. There are three jobs that end up getting generated when you click on the button. I
represented them visually here as the button being in the depressed state, as some of us
get from time to time, and then the onclick handler, and then the raised state. These
go into the queue, and then they start getting executed. First it says, well, let's draw
the down state. Once that's done, then we go onto the onclick handler and execute that.
Then we draw the up state. Then after that, we go back to doing nothing and just waiting
for more jobs to come in. If you think about it, this happens mostly when you're interacting
with the page -- you click on something, there's some sort of visual change, maybe you have
some JavaScript action attached to it...
Question?
>> AUDIENCE MEMBER 3: What happens if there are multiple clicks, or clicks in different
places? Does that queue just get [inaudible]?
>> NICHOLAS ZAKAS: The question was, what happens if there are multiple clicks, or clicks
in different places? Do those all get queued up? Yes. Whenever there are other jobs that
are being executed by the UI thread, any other ones that come in end up getting queued up
and added into the UI queue.
>> AUDIENCE MEMBER 4: Just to clarify that, does it actually stop everything to add to
the queue, or does it [inaudible]?
>> NICHOLAS ZAKAS: It won't stop what it's doing in order to add stuff to the queue.
It's magic like that. It shouldn't get in the way of anything else. But you can actually
see, if you're running a very long running JavaScript and then you start interacting
with the page, you can actually get it to be non-responsive, because it's trying to
queue and trying to do what you were telling it to do before, and it really doesn't like
that.
Yes?
>> AUDIENCE MEMBER 5: If I do a setTimeout, where will that be in the queue?
>> NICHOLAS ZAKAS: If you do a setTimeout, where will that be in the queue? Skipping
a little bit ahead, but I'll answer it. When you do a setTimeout, what you're actually
saying is 'after a certain amount of time, add this to the queue'. It's not 'after a
certain amount of time, execute this'. So if you say setTimeout some function 500, what
you're actually saying is after 500 milliseconds add this job to the queue. That can actually
be really useful, and I'll show you that a little bit later.
So, UI thread. Just did three things in a row. The important thing is that there are
no UI updates while JavaScript is executing. Even though in your JavaScript you're manipulating
the DOM -- maybe you're changing classes, moving stuff around -- none of that happens
while the JavaScript is executing. It's basically batched to be done afterwards, and it kind
of makes sense if you stop and think about it. It's because when the JavaScript causes
a UI update -- this is just a really stupid example of adding a div here -- you want to
make sure that the next UI update is doing everything it's supposed to do. You can only
be assured of that if you wait until the JavaScript finishes executing.
Hey, that's what I just said. UI updates must use the latest info available. I actually
have this other example to show you. Here's another button. I'll try to make it a little
bit clearer. On this one the JavaScript handler takes a couple of seconds to execute, so you
can see this pause actually happen. When I click, it stays in the depressed state for
a little bit longer while it's executing the JavaScript and then it comes back up. Again,
the reason for that is, think maybe I change the color of the button, maybe I change the
position of the button, maybe I change something above the button that forced it to move down
in the page, and then it would have to be drawn down later. The browser doesn't want
to do a UI update until it has all of the relevant information to make sure that it's
drawing correctly.
The longer your JavaScript runs, the more unresponsive your UI becomes. I used to have
this great example of this. How many people remember Bubble Sort from Computer Science?
Oh good, a bunch of us. And do you all remember that's the first one that they taught you,
and then after that they said you should never do that because it's really, really slow and
horrible? I had this great example using a Bubble Sort that in previous browser versions
would take forever to execute. This one I actually have to race because it's Firefox
3.6 and it's kind of fast.
Basically what I have here is a drag and drop example, so all of these are draggable. You
can move them around. As soon as I click 'start' here, I'm going to start sorting an array
of 5000 items with Bubble Sort and then try to move something, and you'll see that the
stuff isn't moving, and then that's done. It's really anti-climactic when it's done
in 5 seconds, so we'll just keep doing it until people are impressed.
[laughter and applause]
Thank you!
The browser actually tries to protect you from JavaScript that goes awry with this thing
called the Runaway Script Timer. I always think of it as this funny little kid running
away, and the first time I saw Runaway Script Timer I really couldn't quite wrap my head
around what it meant.
Marcel, is that a question?
>> MARCEL DURAN: Yeah. What happens if you remove the button when it's pressed?
>> NICHOLAS ZAKAS: What happens if you remove the button when it's pressed? So onclick,
you remove the button?
>> MARCEL DURAN: Yeah.
>> NICHOLAS ZAKAS: Basically, there's still a UI update that comes after that, but that
UI update is to erase the button.
>> MARCEL DURAN: Oh, [inaudible]
>> NICHOLAS ZAKAS: Yep, go ahead.
>> AUDIENCE MEMBER 6: What if you call an Ajax web service [inaudible]?
>> NICHOLAS ZAKAS: OK, the question was, what happens if when you click on that you make
an Ajax request that's asynchronous, then what happens? I'm trying to think of the best
way to explain this.
Being asynchronous basically means don't block stuff. At least, that's my very, very simple
explanation. Asynchronous: don't block other stuff from happening. As soon as you fire
off that request, that JavaScript job is finished, it doesn't have to do anything else. When
the response comes back then a new JavaScript job is created and inserted into the queue,
and then that gets executed at the next available point in time. You're not actually blocking
anything. It's a little bit like setTimeout in that that JavaScript job isn't getting
added until a later point in time.
Yes?
>> AUDIENCE MEMBER 7: I have a question regarding batch update... [inaudible]
>> NICHOLAS ZAKAS: OK, the question was, Stoyan just was up here and was saying hey, make
sure that you're batching all of your UI updates together. Then I said hey, the browser is
batching all of those UI updates together. Who's right? The answer is: always me.
[laughter]
But in this case, we're both right. The browser tries to do what's logical, and it tries to
batch up the updates in whatever's the most logical way. What you can end up doing sometimes
is creating too many of those steps. If you're going through and changing, as Stoyan was
showing, into multiple styles, then you're creating a bunch of steps to be batched up
instead of just one. Then there are some other things you can do that actually trigger the
browser to stop and do a calculation of what the UI updates will be even before the JavaScript
ends up finished. That can slow things down as well, and Stoyan explained that really
well in the book. But good question.
I'm glad people are laughing now, because that would have made this really painful.
The Runaway Script Timer, its job is to prevent you from doing something stupid. It's to prevent
you from writing code that takes so long to execute that the browser stops responding,
so you could potentially end up with memory issues and whatnot. That's the Runaway Script
Timer. Apparently I said a bunch of this stuff already.
There are two types of limits. One is execution time -- that's the total amount of time that
the UI thread has spent processing a single JavaScript job. The other one is the total
number of statements that have been executed in JavaScript. These are both affected by
the new JavaScript engines that come out, because you can execute more within the same
amount of time and you can also execute more statements within a certain amount of time.
They always pop up as scary dialog to the user. Opera is the interesting one because
it doesn't have a runaway timer; it does some smart things to protect you from bringing
down the browser. But here's the scary dialog from Internet Explorer. Have people seen this
before? Yes, very scary. Firefox is also scary. Safari, a little bit scary. Chrome, really
kind of upsetting I think is the word for that.
[laughter]
Chrome actually does something interesting. It doesn't have a separate Runaway Script
Timer, but it ties into the general crash protection mechanism, and that makes unhappy
folders left and right.
The limits we're actually looking at here are that Internet Explorer has a limit of
5 million statements. If you feel like hacking the registry you can actually change that,
but I wouldn't recommend it. Firefox, last time I checked, was about 10 seconds. I'll
admit that I did not check that in Firefox 3.6 before I came up here. Safari was 5 seconds.
Chrome I was really unsure. I could get it to crash if I really tried hard, but hopefully
that doesn't happen normally. Then Opera, of course, none.
How long is too long? It's a really interesting question that I will answer in just a second.
Yes?
>> AUDIENCE MEMBER 8: Is the time limit based on a single example, or the entire queue?
>> NICHOLAS ZAKAS: The question was, is the time limit based on a single job, or the entire
queue? It's based on a single job. You could have a bunch of JavaScript jobs that are queued
up, and they don't affect each other. One can take one second, one can take two seconds,
and so on.
OK, so how long is too long? Jakob Nielsen -- who's this usability guy, I don't know
if you guys have heard of him or not -- he wrote this in his book that actually about
0.1 seconds, or 100 milliseconds, is about the limit for having the user feel that the
system is reacting instantaneously, meaning that no special feedback is necessary except
to display the result. In layman's terms, that means if it takes longer than 100 milliseconds
for the UI to do something, the user thinks that something might be broken. You might
say, well, that's a really short amount of time, are you sure that that's it?
Ben and Dion from Ajaxian did this really cool demo last year at Velocity, and I thought
I would steal that and show it to you because it was such a good example. Here are a bunch
of buttons that have a bunch of delays associated with them as to how long the JavaScript onclick
will actually execute. You can see as you go down, there's no delay, pretty cool. 50
milliseconds, don't notice anything. 100 milliseconds, don't really notice anything. But then I think
it starts becoming a little bit more obvious. By the time you get to 500 milliseconds, you
can really tell that something is going on. Keeping these in mind can help you determine
how long you should let your JavaScript run before giving it a break.
JavaScript should not execute for longer than 100 milliseconds to ensure responsive UI.
What we're really talking about here is perceived performance of the UI. You don't want things
to be pausing and jagged when they're trying to move around. My recommendation, though,
is to keep it under 50 milliseconds at a time, because why even try to get close to that?
Just give stuff a break, allow the browser some time to catch up.
If you remember my drag and drop demo from earlier using Bubble Sort where I couldn't
drag this stuff, I was basically doing that with regular Bubble Sort. So we have a loop
inside of a loop, and that's a big pain. Basically, the process looks like this. You have an array
and you have some items, and you basically iterate over the array and process each one
in some way. You could take this and turn it into some other sort of processing mechanism.
But basically, what you can do is use timers and setTimeout to help break up the jobs.
JavaScript timers are really cool. Everybody at some point in time has used setTimeout,
I'm sure. But basically, it schedules a new JavaScript execution job for some time in
the future. When that delay is up, the job is added into the queue, but it still has
to wait its turn before it's going to get executed.
I just want to really point out that this does not guarantee that your code is going
to get executed after a certain amount of time, at all. It just guarantees that it gets
added into the queue so that it will get executed eventually. I think some people still mistakenly
believe that setTimeout means 'execute this in 500 milliseconds', or 'execute this in
1 second', and that's not the case.
If you have a lot of complex processing that's going on, you can use these timers to your
advantage to split up the functionality into smaller chunks. In the case of a Bubble Sort
what you're basically saying is: I'm going to sort a little bit now, and later on I'm
going to sort a little bit more.
This function is a little bit longer, but it basically does the same thing as the previous
one. It does it using timers. I'm not going to get too much into the details of what the
code is doing, I'm just going to give you a brief overview. Basically, it starts the
job and marks what time it started, then periodically it's going to check to see how much time has
elapsed. If more than 50 milliseconds has elapsed, it's going to stop, set a timeout,
and then call the same function again to finish it. In this way, you're getting the most out
of your JavaScript execution job. If you were to set a timeout for each item it actually
takes much longer. This way you're at least getting as much done as you possibly can in
a reasonable amount of time, and then delaying and doing a little bit more.
If I apply this to my Bubble Sort example here, this has the same thing. I have a bunch
of stuff that I can drag and drop around, and as soon as I hit 'start' it's going to
start sorting. But because I'm using timers to do that, it actually frees up the UI to
continue to do UI updates as I'm interacting with it.
The thing that you'll notice is that it's taking a little bit longer to complete...
Oh, there's a round of applause? Thank you. It took a little bit longer to do that because
you split it up, but the goal was really to make sure that the UI wasn't frozen, so that
goal was achieved. When you're working on your web applications, you really have to
stop and think: how much of this has to get done right now, and how much of it has to
get done eventually? If some of it has to get done eventually then you can actually
push it off until later using a technique like this.
There's this cool thing coming up called Web Workers. Have you guys heard about this?
>> AUDIENCE MEMBER: Yep, it's in HTML5.
>> NICHOLAS ZAKAS: Yes, HTML5! Yeah!
[laughter]
Except they actually split it out, so it's a separate spec now. None-the-less, it's still
really cool.
The idea behind Web Workers... This is actually a screenshot of the spec for effect, and it
has no useful information on it whatsoever, but it's up there if you want to read it.
I actually forget what my next slide is. Let me look at that first. Oh, my bullets are
broken. What a shame.
Web Workers are a way to do asynchronous JavaScript execution. Basically, what we're talking about
is doing the JavaScript execution off of the UI thread, so don't interfere with any of
the UI updates, don't interfere with any of the other JavaScript that might affect the
UI, I just go do it someplace else.
It's completely data-driven, and what that means is that you can't access the DOM from
within a Web Worker, and you can't access other parts that are related to the DOM. You
can't access Window, you can't access location... Actually, you can access location. Little
trick thrown in there. When you pass data into a Web Worker the data is serialized,
and it comes out the other side as an execution job on a separate thread. That all gets run
in just one big batch, but it's not interfering with the UI, it's not interfering with other
JavaScript.
As I said, no access to the DOM, no access to the BOM. The completely separate execution
environment lets you do pretty much whatever you want without worrying that it's going
to affect the user experience. Unfortunately I didn't have enough time to re-do my Bubble
Sort example with Web Workers, but if I had, you would have seen it execute just as quickly
as the first example but without freezing the UI. There's a lot of intricacies to using
Web Workers. They're not quite fully baked yet. But they're still fun to play around
with, and they're definitely something that's going to be important in the future.
This is how basic Web Workers are set up. You create a new worker object and you pass
in a JavaScript file that has code to be executed within that worker. Then you post data into
the worker by using post message, which is similar to cross I-frame communication. Then
when a message arrives back from the worker -- the worker can say 'I'm done, and I'm going
to send a message back to you' -- there's an on message event handler that you can assign
to.
This example, the second part here, is actually inside code.js. What I've said is: when this
worker receives a message, just take the data that was passed in and pass it right back.
On message for the worker, I'm receiving the string Nicholas and the data property on the
event object, and then I'm just posting it back to the window using post message with
hello prepended to it, and then alerting that out. Not very exciting in this case, but just
so you could have a simple example. You can pass in any type of serializable data and
do whatever you want with it inside the worker.
Web Workers support right now is kind of limited. There's also some inconsistencies across browsers.
But it's still a lot of fun to play with.
In summary, the UI thread is used for both JavaScript execution and UI updates. UI cannot
be updated while JavaScript is executing, and vice versa. Try to keep your JavaScript
execution time to 50 milliseconds or less, and you can use timers or Web Workers to offload
some of that until later, if you're trying to do too much.
That's it for me. That's all my contact information if you'd like to talk afterwards. That's it.
[applause]
>> ROSS HARMES: Hi, I'm Ross Harmes. I work at Flickr. I did the chapter in the book on
Ajax. Instead of giving you guys a whole overview on Ajax performance optimizations, I'm going
to focus on one really small bit which I think is actually the most interesting part of the
whole chapter, which is multipart XHR.
How did multipart XHR come about? Well, a few months ago Digg came out with a really
interesting blog post. Anyone from Digg here tonight? OK. Super interesting blog post on
a way they're using XHR to optimize their pages. They have a problem which is really
similar to a problem we have at Flickr -- they have really long pages with tons of small
images. The thing is that these images you see, they're all buddy icons. They're totally
unique to each user; we can't sprite them, we can't really do anything in order to get
them to be faster on the page. They're just, in this case, hundreds and hundreds of images.
What their solution to this problem was is to take all these images -- and in fact, any
sort of resource at all: HTML, CSS, JavaScript -- on the server side, feed it into PHP or
a language, turn it into a string, and then send it back to the browser in one request.
Instead of having 500 requests for 500 images, you have a single one. Obviously this speeds
it up hugely. It's an unbelievable performance improvement.
Here's what it looks like. In the server you're going to request your list of resources. This
could be CSS, this could be JavaScript. In this case it would just be a long list of
buddy icons: image1.jpg, image2.jpg. The server will go in and read all those files and convert
them into some sort of string. In the case of images it uses base64 encoding. In the
case of JavaScript and CSS it's just using plain text. It rolls all those strings together
with some sort of delimiter -- in this case, we're using some sort of MIME boundary which
works just fine -- and then we're sending this to the client side.
What the client side does is it gets one massive string, and it then does what the client side
does pretty well, which is to split on string and gets back each of these resources. For
each different one -- in this case, image.jpg -- it fires off a listener. We have a listener
for jpg, a listener for a gif, a listener for HTML, and then does something with the
payload it gets.
Here's a diagram. Let's say on the server side we're requesting two jpgs, a gif, HTML,
CSS, and JavaScript. We request this, the PHP goes through, reads all these. In the
case of the image it's going to convert it into a base64 string, and then we're going
to send it all as one string back to the client side. The client side's going to take this
one string, split it, and then do something with each resource. For the jpg stuff, it's
going to insert it into an image tag using base64 data URI. For the JavaScript it's going
to execute it. For the CSS it's going to put it into a style. But the HTML is going to
put it on the page wherever you like.
What are the benefits of this? Obviously, the huge benefit is to HTTP requests. Instead
of having 500, 1000, whatever, we'll just now have one. This is true for any number
of any type of resource. Instead of just concatenating your CSS together and your JavaScript together,
you can concatenate both of them together into one file. It gives you the benefits of
spreading images without actually knowing ahead of time which images you need. In terms
of the Digg user page and the Flickr photo page this is a huge benefit, because we have
absolutely no idea ahead of time what images we need.
It allows you to pull in all the HTML, all the CSS, all the JavaScript for a module on
a page in one go. Let's say when a user clicks a button you're going to load some modal dialog.
You can bring in all the pieces you need for that in one request.
Yes, question?
>> AUDIENCE MEMBER 9: How do you delimit the images?
>> ROSS HARMES: I'll get into that, but basically it's just a random string. Whatever you choose
to use. The question was how do you delimit the images, and we just use a string. Anything
works.
So, performance numbers. The first time I ran this I was completely shocked how much
faster it was. I figured OK, the images are really small, the HTTP request doesn't really
matter that much. But in a normal browser -- I think this was Firefox -- we saw a 10
time improvement. For loading 100 images, it took about a millisecond and a half per
image. That's pretty ridiculous. We saw under a second for 500 images, versus 11 and a half
seconds. That's the difference between a usable page and one where the user will go somewhere
else.
For the mobile side, we did a test on the iPhone 3G and the EDGE, and it's a difference
of 4 seconds versus 49 seconds. In the case of EDGE it was 30 versus 124 seconds. [laughs]
I mean, no user's going to wait 124 seconds.
As I mentioned, Digg were the first ones to talk about this. They have an implementation
out there; you can go look at it right now. It's super beta, a little buggy. They have
this weird adherence to RFC 2046 which means they're using MIME boundaries as their delimiters,
which is just a long string, which makes it a little more structure, less payload. And
they only pass one header per payload, and this header has to be the MIME type. This
means you can pass anything you want -- any type of image, any type of CSS -- but you
have to have the header with it, and it means you have no way of pulling apart these images
on the client side. If I pass 10 images, the only thing I know is that they're an image,
I don't know where on the page I've got to plug it into. So order is the only way you
have of identifying which resource is which.
We've been playing around a little bit, and we have a slightly different implementation.
It's based off of Digg's, and it's also super beta. It really just throws away this RFC
2046 structure and uses control characters as delimiters. Control characters won't appear
in any sort of base64 encoded image. They hopefully won't be in your JavaScript, otherwise
it's not going to work. But they're really small, they're just a character. Instead of
a long, 20 character string, we just have a single character delimiter. We also allow
multiple headers. You can have the MIME type, you can have NID, you can have really any
sort of key value pairs that you need to pass with this data in order to know where to put
it in your page. It also fixes a few really minor bugs in Digg's implementation. Nothing
major, but just a few edge cases.
Small problems with this approach. It sounds too good to be true, and in a lot of ways
it really is. The small problems you're going to run into... The first one is Safari's tiny
call stack limit. This is implemented as a recursive function, and because of that, Safari
2 will halt after 90 recursive executions. That's the length of the call stack. In Safari
3, which most people are on these days, that's up to 490, so you're really fine. 490 resources
is a lot. That really gets you through most pages.
The second small problem is that it requires JS. These days I guess every page requires
JS, but at Flickr we really try to have a workable page without JS. You have to have
some weird fallbacks in this case. We went through one case where we were having a cookie
set to say whether it was JS enabled so we knew on the server side whether we could use
this or not. But depending on how your framework operates, this may not be a problem.
The big problems: these are the things that will actually stop you from using this. Caching.
It does not do any sort of caching, so you get this massive string back from the server
where you have all these images and no way of fitting these images back into the cache
so that next time the page loads you have them there. This is sort of a big deal. Ideally,
you would fetch all these images and then through some sort of API feed them back into
the cache. But that way doesn't exist.
Yeah?
>> AUDIENCE MEMBER 9: Are you doing a POST request?
>> ROSS HARMES: The question is, are we doing a POST request? I believe it doesn't matter.
You can use POST, yeah.
[Audience member makes inaudible comment]
>> ROSS HARMES: It's possible. Yeah, I don't know. You can use get or POST with this, it
doesn't really matter.
So cache is a big problem there.
Images don't work in IE. This isn't necessarily true... Let me get your question in a second.
The problem is that we're using base64 encoded images and we're passing them into the actual
DOM, into the page, as a data URI. This works fine in all browsers except for IE, of course.
There are work-arounds for this. There's a thing called MHTML, and there's a great post
about this I couldn't find in time for this presentation, and it may be a great work-around.
It's basically a way of feeding base64 encoded images as a string into IE.
Sorry?
[Audience member makes inaudible comment]
>> ROSS HARMES: I don't know.
There's another possibility Hedger was doing on some really interesting stuff with base64
images and email. It's ancient, ancient RFC that actually still works in IE, so it's possible
that's going to work in IE. But for right now, we haven't found a really great and easy
solution.
Yeah, question?
>> AUDIENCE MEMBER 11: What about the HTTP5 pipelining, because this is basically a reinvention
of that? Why aren't people pushing the pipeline and making it work?
>> ROSS HARMES: The question is, what about HTTP pipelining? Isn't this just basically
redoing it? In a way, yeah. It actually turns out to be a little faster than pipelining.
Pipelining is just when you have multiple requests at the same time, and this is just
one big request. In actuality, I'm not sure there's much difference, but I found better
performance with this, actually. Good question.
How are we planning on using this awesome thing that probably won't work in half of
our users? We're not going to use it on our main site. We just don't have a way, yet,
around IE. But if you have a site that just basically caters to high end browsers, Firefox,
WebKit -- say you're like the gentleman in the front row that has the iPad -- that's
a perfect example of where this might be useful. We're going to try to use it on our mobile
site. We have a version of the site that is just basically optimized for smart phones,
for iPads, Android, WebOS, iPhone devices, and we have another one we can fall back to
so we don't have to worry about supporting devices that can't really handle the MXHR.
What we're actually doing is detecting JS. If JS is there then we're going to not load
the page with any images, have one big request, insert them all on the page, and save just
a ton of time on photo pages with a lot of comments.
The big reason this works so well in the mobile environment is that caching is not a big deal.
If you've ever been cruising around on your iPhone or iPad you'll notice it basically
does no caching whatsoever. You go to a page, you go to another one, you hit back, and it
reloads the whole page over again. So really we're not losing anything by not having caching
there. They also can handle all aspects of the MXHR. They can do XHR streaming, they
can maybe get the data in midstream and slice it up as it's coming in. So everything works.
But of course, this is part of our mobile site, and we're actually hiring for that right
now. So if this sounds interesting to you -- if you want to be the guy to implement
MXHR for Flickr -- come work in our ridiculous work environment. All the info, all the contact
stuff, is at flickr.com/jobs. We've got a ton of stuff on there.
So that's it. You can find the code here. If you have any questions, ask them now, feel
free to email me. I'm happy to answer them. Yeah?
>> AUDIENCE MEMBER 12: Do you cache local storage?
>> ROSS HARMES: The question is, do we cache local storage? That's a really interesting
idea, and it's one we actually thought about. Why you have local storage is it's essentially
a way of recreating browser cache, because all you're getting is strings anyway, and
you can throw strings into it. It's totally possible. It's a little crazy, but there's
no reason it wouldn't work. You could absolutely do that.
>> JULIEN LECOMTE: I wrote this chapter and I'm going to try to cover it a little bit
in this presentation. It's not the sexiest topic to talk about when it comes to performance,
but I think it's one of the most neglected areas, which is why I decided to contribute
this chapter to the book.
You can't talk about building applications without at least mentioning one of these tools.
Make, obviously, which came to us from the Unix world. Make uses what's called a make
file, which is a text file with this special format that the text file specifies how to
derive the target program from each of its dependencies. Apache Ant is another build
tool which is implemented in Java. It uses XML to describe the build process. This makes
it available on many different platforms, and it makes the build descriptor file portable.
I'm going to talk very quickly about Ant, because I have a few examples later that use
an Ant built file. An Ant built file is composed of target elements. Each target contains tasks,
and targets can list other targets as dependencies. Ant comes with a lot of native tasks right
out of the box. Two examples are copy -- for example, to copy a set of files in Java, for
example, to compile a set of Java files.
There are many other tools, of course. Ruby folks like to use Rake, which uses a Ruby-style
syntax to describe the build process, which also makes it platform independent. At the
end of the day, just use whatever you're most familiar with, as long as it gets the job
done.
The first thing you can do in your build process is to combine JavaScript files. The goal is
to reduce the number of HTTP components required to render the page. I think that's rule number
one in the Yahoo! Exceptional Performance list. Let's see how you can do this using
a make file. This is a very trivial example where you just concatenate two files, a.js
and b.js, using the cat utility. That's really simple.
Now let's see a slightly more complicated example using Ant. As you can see here, I
declare a target -- its name is js.concatenate -- inside of which I use the concat task.
I specify the destination file, and then the files that I'm going to concatenate. Here
I use a file list and a file set. Using a combination of a file list and a file set
allows me to handle dependencies, which means that I can put at the top of my concatenated
file the things that the bottom will require. This seems a little scary, but it's actually
really simple once you get the hang of it.
Next thing you can do as part of your build process is to minify your JavaScript files.
JavaScript minification is the process by which your JavaScript file is stripped of
everything that is not absolutely essential to its execution. JSMin is the first widely
used JavaScript minification tool, still very popular today. The YUI Compressor is a tool
that I contributed to the YUI project. The main feature of the YUI Compressor over JSMin
is that it obfuscates local variable names, so it yields a slightly better compression
ratio. You can see Nicholas' presentation on how to actually make better use of the
YUI Compressor. I think it's on the YUI Theater.
The latest tool, the new kid on the block, is Google's Closure Compiler, which is kind
of where I wanted to take the YUI Compressor a few years ago. They do a better job of actually
analyzing the code and figuring out what is not useful at the end. It yields higher savings
than the other tools, but you have to be aware that it heavily obfuscates the target file
and that can make debugging a little tricky. So it's kind of a compromise. You get smaller
files, allowing for faster download but maybe higher cost trying to figure out what's wrong
when something goes wrong.
Using make, this is how you would invoke the YUI Compressor. Very basic. On the left side
is the target file, and on the right side is the requirement. Then the command line
runs the YUI Compressor.
This is a slightly more complicated example using Ant. Again, it looks scary, but it's
actually really simple. I declare a property at the top... I actually stole this from the
YUI Builder thing on GitHub. I declare a macro called YUIC, and that macro takes three parameters:
SRC, DST, and ARGs, and then it just runs YUI Compressor. At the very end, I actually
invoke that macro, specifying the source file as foo.js. This [xx] file is foo.min.js, and
the arguments, variables, and of course that stuff.
As part of the build and deployment process, serving JavaScript files gzipped is probably
one of the most important things you can do. Web browsers usually send an Accept Encoding
HTTP request header. This lets the web server know what kind of encodings it supports. This
is primarily used to allow a document to be compressed, which enables faster downloads
and therefore a better user experience. Possible values which are registered are gzip compressed,
deflate, and identity. The web server then chooses from that list the most appropriate
encoding method and notifies the web browser of its decision using the content encoding
HTTP response header.
Gzip is by far the most popular encoding. If you use Apache, Apache 1x uses mod_gzip
and Apache 2x requires mod_deflate. All it takes for you to enable gzip encoding is to
insert those modules and configure them. There's a lot of documentation on the web on how to
do this.
Be aware that research that was done at Yahoo! and at Google and other companies has shown
that about 15 per cent of our web traffic in the US is not compressed. The reason is
that we just don't get the Accept Encoding HTTP request header. It's either missing,
or garbled, and it's related to proxies or to [xx] software. So it's actually important
to make sure that your code is as small as possible even before gzipping, because of
this. Here are the kind of savings you can get by using the YUI Compressor and gzipping.
You go from 120k to 19k over the wire.
The next thing is caching. You want to cache your assets. Caching on the web is based on
the Expires HTTP response header. Below is an example on how to do this using Apache.
Caching addresses repeating visitors. Web servers use the Expires HTTP response header
to let clients know how long a resource can be cached. One year in the future is the best
value you can use. You shouldn't use a value that's longer than one year.
Mobile phones, like the iPhone for example, have cache size limitations, so be aware of
this. In this case it might actually be a good idea to split files, so you kind of have
a compromise here where you have more HTTP requests but you take advantage of cache-ability,
so it all depends on your application and the kind of usage it gets. You can also use
the HTML5 offline application cache, which is enabled in Firefox 3.5, Safari 4, and iPhone
OS starting with 2.1.
The next thing is, if you use caching you have to make sure that your users get the latest
version of the content when you, let's say, fix a bug or introduce a new feature. The
best way to do this is to rename your static assets. I personally prefer to use a timestamp.
This is how you can do this using Ant. You can automate the renaming and the timestamping
of your static assets using your favorite build tool.
Finally, something that's really simple but will make a huge difference is to use a CDN.
CDN stands for Content Delivery Network. A CDN is a network of computers distributed
geographically across the internet that are responsible for delivering content to end
users. They achieve much better performance than you could do on your own by serving content
from the location that's closest to the user, which decreases network latency. Also, if
the node that's closest to the user fails then another one will take over. It will be
a little slower, but very reliable.
There are a lot of many third party CDN providers. Also, some bigger companies maintain their
own CDN. Popular JavaScript libraries like YUI are all able to access via CDN. For example,
the YUI library is served directly from the Yahoo! Network via the YUI.yahooapis.com domain.
There you go. That's how you can get in contact with me. The next speaker is Matt. Matt is
a rock star YUI architect.
[applause]
>> MATT SWEENEY: I'm just going to whip through this pretty quick. The book has more details;
it's fairly straightforward. We're going to cover a couple of classes of performance tools.
The first will be Network Analyzers. These are things you can do to optimize the delivery
of assets to your page, make sure that your servers are performing optimally, that you're
combining all your assets correctly, etcetera.
Here's a handful of the tools that fall into this category that we'll be dipping into a
little bit deeper. The second set of tools, profilers, these are tools that you can use
to measure the runtime execution of the scripts during the life cycle of your page view session,
etcetera. Some crossover there between the previous set of tools.
OK, the first one we have here, YSlow, is pretty tried and true. Hopefully most of you
guys have run this a few times. This was developed internally at Yahoo! way back by Steve Souders,
and then refined by our man Stoyan over here. This'll give you kind of an overall score
of how you're doing delivering the content to your page. It'll give you some kind of
scorecard. Hopefully you're doing a little better than these guys. Won't point any fingers.
But it will break it down based on what are you doing, what should you be doing, give
you some nice little tips and things here. Fairly straightforward. This is based on lots
of research over the years that's boiled down to a handful of performance rules that hopefully
you guys have all burned into your brains at this point.
That gives you a nice little breakdown here of how the weight of your page is distributed.
You can see what an empty cache looks like versus a primed cache, which will sort of
help you fine-tune some of your loading strategies. If you have 23 JavaScript files you probably
have a lot of room for improvement there. It's a Firefox specific tool. Yeah, that's
about all I've got to say on that one.
Next up, page speed. This is a great tool that came out of Google. This guy will let
it overlap with YSlow, essentially kind of help you understand what's going on with the
delivery of your assets and things. One of the really cool features it has here is it
will run through and tell you which functions on this page haven't been called before the
onLoad event fires. Those are potential deferral candidates, so you might think about lazy
loading those or bootstrapping those after the onLoad has fired, the idea there being
that there's nothing you need to build up that initial view and ideally that's maybe
something that could be optional for the user for that particular view.
It'll also tell you -- and here's the report, by the way, of what it might look like -- which
functions were never called, which can be really valuable too. It could be that depending
on the state of the page and what the user's doing that sometimes the function will get
called and sometimes it won't, but it's nice to audit that kind of stuff every once in
a while and make sure you're not just loading dead code into the page.
Here's the activity view. I won't get too deep into the nitty gritty here, but one of
the interesting things you can see is the red blocks indicate the time spent parsing
your script, and the dark blue blocks indicate how long that script took to execute. That
can also give you some clues around what's going on. If you have really long parsed steps
you might consider breaking that file up into smaller files. If you have really long execution
phases, you may consider deferring some of that, or seeing how you can not necessarily
instantiate all of that code upfront. Again, this is a Firefox via Firebug addon. Like
I said, a couple of the nice features there.
OK, now we have Firebug's Net Panel and Net Tab, which gives you similar data. This one's
a nice little waterfall diagram of how these assets are being loaded, and it can give you
some clues as to which assets might be blocking some of the other assets, potentially slowing
down the page. You may want to consider asynchronously loading or bootstrapping some of those post-onLoad
potentially. Again, just clues you can use to help understand the performance of the
delivery.
It comes with Firefox. It indicates -- I don't know if you noticed here -- there's a blue
line that indicates when the DOM content load event fires, and then the red line indicates
when the onLoad event fires. It sort of helps to give you an idea of what's happening when,
as far as the loading life cycle of your page. Additionally, it'll help you understand what's
actually happening with each step of the request, how much time was spent in the DNS, how much
was sending and receiving, etcetera. http://getfirebug.com, I'm sure you have that already.
Moving right along into Web Inspector. Similar to Firebug. This one's for WebKit/Safari/Chrome.
Similar data it provides here as far as the resource view. One interesting thing is that
it'll break out the time and the size, keeping the views nice and clean there. But same basic
data, different browser subset.
Fiddler. If you've been doing this stuff for awhile, you've probably fired up Fiddler a
couple of times here and there. This is a cross browser tool that basically can monitor
all of your HTTP traffic and help you get a better handle on it, similar to the Net
Tab and the Resources Panel in Web Inspector and Firebug. It'll give you the waterfall
diagrams, break out your assets by type, let you get a quick birds eye view into 1) where
the bulk of your assets are, and 2) how those are performing over the wire.
dynaTrace. This is a really cool new tool that's currently IE only, although they're
saying Firefox is coming soon. This one is really a full spectrum, end to end network
analyzer and profiler. It gives you all kinds of great little views into what's actually
happening here. You can see, for example, it'll break out the triggers, so which events
were responsible for the bulk of the execution, etcetera. And it reports everything as all
part of the same summary, so it's really nice you can see what's happening in the network
versus in the page. It'll quickly help you focus on where exactly you need to spend your
time to make this thing faster.
OK, so moving right along into the profilers now. I don't know -- hopefully most of you
have run a profiler. Pretty straightforward. The simplest thing to do in Firebug is just
hit the profile button, do your thing, and then hit the profile button again and you'll
get a nice report with probably lots of obfuscated function names if you're profiling live code.
The nice thing is you can click on those and it'll take you right into the actual source
code you're dealing with. Dealing with profiling results is probably a separate topic here.
Some of the things to consider are the number of calls to a particular function, how much
time was spent in each particular function, etcetera.
It's got an API for profiling, which is nice, so you can either embed this in your code
or potentially invoke it from the Firebug console. So not only can you invoke various
profile sessions, you can pass them a named argument if you want to run multiple profiles
in parallel, or if you just have, say, a random block of code that you want to time, the console.time()
and console.timeEnd() will do that for you. So you don't have any explicit functions with
things you're looking for, you just want to know how long it took to execute this loop,
or this block of code.
OK, now we're onto IE developer tools. Similar features with Firebug. One interesting additional
feature they have here in addition to the flat function view -- which is sort of a top-down
sort by slowest to fastest, or sort by max call time, or sort by number of calls -- you
can do the call tree view, which allows you to drill through the entire call stack and
see as it's going though there where things are happening when, what might be masking
something else, and give you a better view into really what we're dealing with here.
Additionally -- I didn't mention this -- it'll also measure native functions. If you're ever
wondering, for example, what's faster, concating strings, or rejoin, you could measure stuff
like that. Additionally, you can export these results. For example, say you want to collect
a bunch of results over time and then plot them on a chart or something like that, it's
really easy to do that with the export feature.
The Web Inspector profiler has a lot of overlap with the IE developer tools. The flat function
view versus the call tree view, they refer to the flat function view as the heavy view.
They've also implemented the console API a la Firebug. And you can actually implement
that manually if you want to supplement that for, say, IE. I call that out in the book
in one section. So that way, if you want to have essentially a programmatic way to invoke
all your profiling and timing, you can do that.
YUI Profiler. Nicholas, the man of the hour himself, has contributed this to the YUI library.
This is a really great cross browser way to profile pretty much anything you might be
interested in, whether it's a specific function, or a method, or you want to measure every
instance of some object, you can register its constructor. Oh, register constructor
-- typo. That's register object there. Cut and paste error. Then you could also, if you
had, say, a static object, in this case not Y.node, but say Y.dom, or some other singleton
style object, just register that whole object and it will instrument every method on that
object for profiling. And it gives you an exportable results set as well -- in this
case, JSON format. Additionally, you can profile anonymous functions which can be a bit of
a challenge, although they do require manual instrumentation.
Anyway, we're through that, but we'll get back to the fun part of the night: mingling
and chatting with your fellow developers and whatnot. Just to summarize, you should really
use these tools so that you can get a deep, deep understanding of what's happening. You
should know exactly, during every life cycle portion of your page, from request up until
onLoad and well into mouse moves and clicks and all that, what's actually happening. Don't
wait for that VP to call you on Saturday night wondering why this page is so slow on dialup
at his girlfriend's house. For example.
[laughter]
Again, these tools can really help you to focus on optimizing correctly. You don't want
to waste tons of time twiddling around over here -- 'let me try this', or 'let me try
that' -- really you need to optimize the optimization here. Dive right into where the bottlenecks
are and strategically speed things up.
Thanks for coming out. If you guys want to grab me and talk about tools, or YUI, or whatever,
feel free. If you want to hit me up after the fact on Twitter -- I don't really tweet
very much, but you can always send me an email, and of course we've got the YUI Library website
where we interact with the community. I guess I will just shut her down here, and keep it
moving. Who wants a book?
[applause]