Full Stack Web Performance
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Molnar, Nik
Modern users expect more than ever from web applications. Unfortunately, they are also consuming applications more frequently from low bandwidth and low power devices – which strains developers not only to nail the user experience, but also the application’s performance. Join Nik Molnar, co-founder of the open source debugging and diagnostics tool Glimpse, for an example-driven look at strategies and techniques for improving the performance of your web application all the way from the browser to the server. We’ll cover how to use client and server side profiling tools to pinpoint opportunities for improvement, solutions to the most common performance problems, and some suggestions for getting ahead of the curve and actually surpassing user’s expectations. This session covers a wide array of topics, most of which would be classified within the 200 level.
 All right. Thank you for everybody for being able to wake up this morning and make it out. I've been sick for the last couple of days, so I have this very sexy voice, which is why I'll be using Notepad for the rest of the presentation. We'll just read the whole thing. One guy. No, I'm not going to do that to you because I can't spell that well. My name is Nick Molnar. I'm from New York City. I've been a web developer for a little over 15 years now. Yeah, I'm honored to be here speaking to you at NDC Oslo. About three years ago, my buddy Anthony and I, we created an open source project called Glimpse. Just so I can get a feel of the room. How many people have heard of or used Glimpse? Show of hands, please. All right. Awesome. I actually have a bunch of stickers, so if you guys are interested in stickers, swing by after the talk and I can give you some Glimpse stickers. We did Glimpse about three years ago after about a year and a half of being fairly successful with that in the open source community. We got sponsored by a company named Redgate, who's also sponsoring this conference, so please swing by their booth and say hi. What that has afforded me to do is spend the last year and a half focusing solely on Glimpse. Now, Glimpse is a debugging and diagnostics platform for ASP.net, and it has a lot of uses, but one of the main use cases that people like to use Glimpse for is around performance. So, in reality, for the last year and a half, I spent a lot of time focusing on web performance and the things that we can do to make web applications faster. So, if you're anything like me now, you're super worried about this loader bar. It's making your skin crawl, and I'm just hoping that this PowerPoint presentation loads in time for us to start the presentation. Oh, good news. We loaded, so now we can be full-stack web developers. So, let's stop and talk about why performance matters really quickly. To me, it's very simple, and I'm guessing because you're in this room, you already agree that performance matters, but let's cover it really quickly just in case you're not in that place. So, performance to me is usability. There's been this whole UX movement. That's been a big thing. I think now that we need to start thinking about performance in the same breath with UX and start treating performance as a feature. So, you see this hashtag perf matters up there. That is not some crazy thing that I made up. That is actually the hashtag that people who care about web performance use on Twitter and follow. So, if you follow that, you'll get lots of great information, much of which I will give you in this talk. There's tons of studies that say that performance matters. So, Google, I'm sorry, Amazon increased their page load time by 100 milliseconds, and their sales decreased by 1%. You've heard case studies like this. Google increased their page speed by half a second, and they lost 30% of their traffic on a bounce rate. So, if you're in the e-commerce world, performance and sales correlates together very well, maybe you're not. Maybe you're a content publisher or something like that. Well, the performance of your site affects your ranking in the Google page index. So, the faster your site is, the higher the ranking you are. So, not only does it affect sales, but it affects traffic and eyeballs. And finally, we live in a world of devices now where I have limited battery life on this thing, and if your website is slow or uses up a lot of my resources, I'm not going to sit on it very long because I don't want to kill my battery, and I know of certain websites that do kill my battery. So, for all of those reasons, I think that performance matters. Here's another way of looking at it. This pyramid, maybe you've seen the my right-hand side, your left-hand side before. It's Maslalov's hierarchy of needs. Maslalov was a psychologist who many, many years ago before I was born said that human beings had these basic needs that needed to be met first, and once those basic needs were met, they could move on to fulfill more advanced needs, and that human beings flourish when they get to the top of this pyramid. So, for example, I must have physical safety. I need a roof over my head, and I need to be warm before I can start worrying about my self-esteem. And once that is met, I can move on. There's a guy named Aaron Walter, who's the UX director for a company in the states called Mailchimp, who's very popular and famous in the UX world, who has proposed a similar pyramid based off of Maslalov, which is Aaron Walter's users of software needs. So, users of software need a couple of things. First of all, they need the piece of software that they're using to be functional. It has to solve a problem. We all know that because we're developers and we solve problems every day. Next, it needs to be reliable. It needs to solve the same problem over and over and over again. You may remember when Twitter had troubles a few years ago, and every other day you would access the site, and you got the fail-wail. Twitter was not reliable, and during that period of time, I rarely ever used Twitter, just because they weren't reliable. They fixed that now. Next, software needs to be usable, and these are the kind of the order in which we should be thinking about things. There's no point in having usable software if it crashes half the time. So, usable software is easy to pick up and learn and easy to remember how to use when you come back to it later. The next layer up is performance. That's the layer that I've added in. Performance is not actually a word, thus the quotes, but I use it anyway because I'm American and we disagree with a lot of things that Webster said. So, performance software is software that does all of the other jobs, but quickly. So, let me give you an example. On my machine right now, I have two photo editing pieces of software installed. I have Photoshop and I have Paint.net. 99% of the time when I need to edit a photo, all I'm doing is cropping it. I never open Photoshop to crop a photo because Paint.net is just so much faster at doing that job. So, performance matters. And then lastly, Walter suggests once we've kind of nailed these things, we can start to make our application pleasurable. We can add personality. We can put funny little images and gifts and things like that that we see at the sites that we love like GitHub or Trello, et cetera, et cetera. So, this is why performance matters. Let's, actually there's one more thing that we really need and that's Wi-Fi. That's the basic. Just a quick show of hands. How many people were in my talk yesterday? Okay, for you people, I'm sorry. I only have like three jokes. This was the new one. It's gone now. So, I learned yesterday that self-deprecating humor is the best kind of humor around here. So, once we have Wi-Fi, we're good to go. So, the problem is we need to figure out how we're going to solve performance problems and yet we constantly have pressure from our bosses, our stakeholders, our customers who all they want is more and more features, tighter and tighter deadlines, and we need to accomplish all of these things. And so, I've found that the things at the top of that pyramid are the first things that go when you're trying to hit those deadlines or those outside pressures. So, I've come up with this attack plan and this is how we're going to kind of try to focus on the performance problem. So, the first thing we need is measurable improvements. Everything we're going to do today is really about me showing you a tool for how to measure performance because if you can't measure it, you can't improve it. Once we measure it, we'll look at ways of improving some common scenarios. Two and three, platform stability and environmental neutrality, they kind of go hand in hand together. Basically, if I summarize that, it means do oranges to oranges and apples to apples comparisons when you test your software before and after an optimization. What does that mean? Run on the same hardware. Run on the same software. And when I say run on the same software, I mean all the way through, make sure that caches are primed or emptied the way they were between the two runs. Make sure your antivirus software isn't running on one of the runs. Make sure you don't have any cron jobs going. You'll be amazed at how much you could skew results with simple things that are happening on your machine all the time. Number four is to be scenario focused. I was a consultant for a very long time before I moved full time into the world of open source software and many times my customers would come to me and say, the application is too slow. Make the application faster. I'm here to tell you, you do not make applications faster. You make scenarios faster. You make the login sequence faster. You make the purchasing path faster. You make the search faster. But you need to look at a scenario that you can run a user scenario through and measure the performance of that and make that faster. You want to have preset goals when you're doing that, especially as consultants. That's important because we're constantly thinking about the money that we're burning. Trello was a really good example a few months ago. They put out a blog post where they set this goal that they were going to make the main card rendering page 50% faster. They were going to work on it until they did it. They did it in five days. That's the luxury that Trello has. Me as a consultant, I usually don't have that kind of luxury where I say, I'm going to work on it until it's done. But instead, you can kind of flip it around and say, you know what we'll do? We're going to work on this one scenario for the next sprint, the next iteration, and we're going to make it as fast as we can. It's a fixed amount of time. You know how much budget you're burning up to make something faster. Then finally, what I think is the most important one is you want to approach performance in the order of descending granularity. So get the big wins first and then focus on the smaller wins as you go. Everything that we talk about today is going to focus on that. On the web, most of the time, remember, I'm going to say most of the time a lot today because you need to measure things, most of the time the network is going to be where your biggest problems are. That's where we're going to start today. The good news is also because that's the largest bit of problems, there's the most documentation and best practices published about that kind of stuff. Okay. So network, let's dive into this. So I have a very simple application here. It's called mascot. Because I'm American, there's a bunch of things that you probably already know about me. One, I'm fat. I like to eat. I like my baseball. I'm a huge Miami Marlins fan. They're half a game out in the National League East if anybody cares. But I know you don't. So instead of making a website about baseball, I made a website about baseball clowns, also called mascots. In America, there is a minor league baseball system. There's 160 teams each with their own mascot. So this website is dedicated to them. You can see all the individual sub leagues. I can go here to the Carolina league. We can click on the link. You can see here's all the mascots from Carolina. I also have this right here. You can click on the minor league baseball logo. You can see all of the different mascots from all of the different leagues on one page. And then finally, every year, there's a contest that the mascots have called mascot mania where they compete and fun Olympic games like pie throwing. And I don't know what else they do. They throw a lot of pies. And so, you know, there's winners of that. And so some of my favorite guys are in here. Like this gentleman right here, his name is ball of pinio. He's a half jalapeno, half baseball bat. You can't get any better than that, right? Those of you who were around yesterday also know that I'm quite fond of Roscoe the P. Ray rooster because he beats people up. And I have no clue what that has to do with baseball, but violence is good. Just joking. I'm playing the American stereotype here. So what we want to do is we want to think about how this website is affecting performance on the network. So the best tool to do that is F12. Now, F12 works in every single one of the browsers. And for almost all of this next section about networking, the F12 tools in your browser of choice will get the job done. I'm showing Chrome because that's the one that I prefer, but you can use Firefox or IE. You're going to get almost basically the same results. So what I want to do is I want to scroll over to the Texas league. I'm going to go to this network section. Let me go ahead and clear it. I'm going to refresh the page. Now, what you're seeing down here is all of the HTTP requests that were made to render this page to the screen. There's lots of information here. You can see there's, if I can come over here to filters right here, and I can say that I only want to look at images. And so you can see that I have 22 of the 27 requests that were made on this page were for images. I can show you why that's the case. The way I've constructed this page, I tried to make the mascots look like baseball cards, which means that the little team logos you see right here in the bottom right hand corner are separate images that are layered on top of the mascot image. So there's two layers there to make these baseball cards. So everything that you see there is two image requests. So that's kind of cool. We can filter and do stuff like that. We can dig into any individual request and we can see all the HTTP headers if that interests you. What I find particularly of use is the size, content, and time latency. I don't know if that's me or not. The size you'll see there on the first one is 4.0 kb. The next one is 8.1 kb. And you can see a number in gray, 3.6 and 7.8. The number in gray is the actual amount of usable content that Chrome was able to use. The number in black is the amount of content that was downloaded. So you can see that for each one we have about.4 k overhead of content that we couldn't use but that we downloaded. That is the overhead of an HTTP request that you're seeing over and over and over for all 27 of our requests. On the time, you can see 35 milliseconds with 34 underneath it. That is how long it took to download but the latency is how long we took waiting on the network to actually start the download. Now, I'm running all of this locally from my machine so the latency times aren't, the download times aren't much more than the latency because I'm getting it fresh off a disk. But you would see much worse performance in other places. There's some other cool things that you can do. You can turn on this preserve log and if I turn on preserve log and I browse to another page, the network tab keeps the first request and adds in a bunch of other stuff from the next request you can see here for local host. I can also right click in here and I'm going to copy all as Har. Har is a standardized format that's based on JSON that's used for performance experts to pass around this kind of information in a standardized format. What's really nice is if you see a performance problem on your website, you can copy the Har file, you can email it to a buddy and they can use any tool on their end like HTTP watch or Fiddler to load that information back up. So I'm just using an online tool. I just pasted the JSON that was copied and if I do a preview, you can see here are my two requests. I can move back and forth between the two of them and see how they were broken up into what was waiting on DNS, how much of it was images and what not. And I can see all of the requests and their headers. So it's a good way to preserve that information for later analysis. Not a lot of people know about Har's but I think they're very useful. So you may be saying to me, well, that's great, Nick. You just showed me all this way to do measurement but now how do I know how to improve things? Well, when you've been around the block as much as I have, you kind of get some intuition for what to do. But the Chrome team has done a couple of interesting things to help you so you don't have to understand all of this. The first that I'll show you is this audits tab. If you come to audits and you hit run, it will scan through basically all the information that was present in the Har file and it will give you recommendations. It will tell you what you need to fix. And they present them in this red, yellow, green fashion where you fix the red first. Once again, they are following descending granularity. So you can see here that enabling GZ compression and leveraging browsing caching are two of the bigger things that I should do. I have installed an extension to this auditing tab that gives me accessibility information. So you can see here things like my images should have an alt attribute. And this auditing tab does some really cool things too. You can see here that I have a bunch of unused CSS rules. If I drill into this, you can see that 15% of my screen.css file is not used. 79% of my normalized CSS file is not used. And my validation file is not used at all. So these are great places for me to go and start making performance optimizations. The Chrome team also ships a separate plugin called page speed. Why they didn't build this into the audits tab? I don't know because it's almost the same as the audits tab. But it's kind of the audits tab on steroids. So once again, plugin that I have installed, I hit start analyzing. It will give you many of the same recommendations, but a couple of extra ones. It goes a little bit farther to helping you solve those recommendations. Let me show you what I mean. So there's a recommendation here to optimize images. If I click on it, it will show me all of the images that I could optimize. And if I open up one of them, it will give me the compressed optimized image. So that is the team logo for one of the teams. All I need to do is right click on this, save as, overwrite the file that's on my disk, and boom, I've optimized that image. I didn't have to go find any external tools or anything like that. Chrome just does it for you. So that's really nice. So there's a bunch of recommendations here. Let's look at how we would actually fix these things in one of our websites. So I'm going to open up the code for this minor league baseball thing. And we're going to fix a couple of problems. The first thing is we're in web config, and we want to compress data. We want to squish it down using gzip or deflate and send over as few bytes on the wire as possible. It's important to do less. That's the first rule of performance optimization. Do less means two things when you're on the network. Fewer HTTP requests and fewer bytes over the wire. So all we have to do in.NET is change a couple of values. You can see here that I'm looking at URL compression under system web server. And I'm going to do static compression. I'm changing that to true. All of my static files, CSS, JavaScript is now going to be compressed. And all the modern browsers, and when I say modern, I mean even back from like the last eight years, know how to deal with this compression. And if they don't, ASP.NET knows not to compress. You can also do dynamic compression. This is on the HTML that you're generating on the fly using Razor or something like that. You can run into some problems with dynamic compression, so you need to test it. But for this website, it's fine. It's great. In about two seconds, I've just added compression to my entire website. The other thing that we saw as a big recommendation is to cache our content. So by me uncommenting this one line and system web server, again, I'm saying that for static content, I want to cache it on the client using HTTP headers. I'm going to use the expires header. It's going to add a header that says that any assets that are downloaded are valid until 2020. So nobody will try to redownload any of my images until 2020 unless they clear out their browser cache. They get it one time, and every time they go back to the page, it'll be fast. So that's great. Two quick updates. The next thing I'm going to show you is bundle config. This shipped recently, I think,.NET 4 and ASP.NET. This is a feature that allows you to take your JavaScript files and CSS where you might have multiple. You saw that I had at least three CSS files and merge them together at runtime into one file, which means I'm going to go from three HTTP requests down to one, which is great. And so what I've done here is I've created a, what I'd like to think of as a virtual file at tilde slash content slash CSS. That's not actually a file that exists. I just picked an arbitrary name. But what I'm telling ASP.NET to do is to take these three real files, normalize validation and screen CSS, and combine them into this one fake file. So now that I've done that, I can come into my layout. You'll see here is where I'm including those three real files. I'm going to comment that out so they're not included anymore. And instead, I'm going to tell ASP.NET to render. Once again, there's that fake file that I had, tilde content CSS. So now only one CSS file will be there, but it's really all three combined. The next thing I'm going to do is leverage a plugin that I have for Visual Studio called Web Essentials. If you're a web developer on the.NET platform, Web Essentials is aptly named because it's essential. So one of the things that I'm going to do is I'm going to come over here. We've been looking at the Texas League. And so you can see that I have my content. I have a bunch of images. I have small mascot images. These are the actual pictures of the mascots. And I'm going to go into the Texas League. And I'm going to right click on this folder. And I will go up to Web Essentials. And I will say create an image sprite. Okay, that's fine. We'll save a.sprite file, which is an XML file. You'll see this pop up. This XML file has a bit of information in it. It's really just a manifest. You're seeing all of the different PNGs that were in that folder automatically included. You can see that we're going to do a vertical orientation. And now you can see down here in this message that the sprite has been generated. So let me browse to this and explore. Here you can see my sprite that was just created. And so you can see all of the mascots are now one image stacked one on top of the other. So if we take a look at this image, you can see the size of it is 340K. I think that we can do a bit better. So I'm going to come over and I'm going to use, there's lots of tools for doing this. I like using online tools. I'm going to come to this thing called Tiny PNG. I'm going to simply drag my sprite over here. I'm just going to upload it. Tiny PNG does lossy compression. It's actually going to cut some of the bits out. I theoretically am losing some quality here. So I've looked at the before and after pictures beforehand and I can tell you that I can't tell the difference with my naked eye. So I'm good with it because as you can see, I've gotten a 72% increase in size. I went from 348K down to 98K for all of my images because that's that one sprite. So let me go ahead and I will right click on this, save the link as, and we will just replace my sprite. Great. So now this image is 95.6K. Okay. What other optimizations can we do here? Okay. Well, so now I created the sprite. The other thing that happens when you create the sprite is WebEssentials creates a CSS file, a less file, and a SAS file for whatever flavor of CSS you like to do. If I open up the CSS file, you can see, oh, that is not the file I wanted to open. If I open up the CSS file, you can see that it's made a bunch of classes where the class is the name of the mascot or the file. So you can see my buddy, Bala Pina, right there, Deeson Hornsby are right behind him. So I'm going to include that in my layout. I'm going to kind of cheat here instead of merging it in to save time. I'm just going to drop the link in there. So now we have two style sheets, but it's still less than we had before. So that's great. And I'm going to update the layout file that we used to render the page. So before, you can see that we were doing an inline style to set the background image to the mascot, right, all of this. Now that I have a real CSS file, I'm going to change it so that we do a class. And that class is going to be the mascot's slug. So the slug for Bala Pina is Bala Pina. So that will match the class name of the CSS file. Let's see what else we can do. On the page, let me go back to this page. Oh, you know what? Let me... On the page, you can see that we have this little baseball. That baseball is just a tiny image that shows up next to all my headings. I don't really need to download that over and over again. So instead, Web Essentials again gives me this little tool tip that I can go. So here's that ball. It gives me this little tool tip where I can say that I want to embed the image as a base64 data URI. When I do that, I get all this gobbly book. But that gobbly book is actually the image base64 encoded and put into the spreadsheet automatically for me. So now when somebody downloads that style sheet, they will also be downloading the baseball, and they won't have to do a separate request. So at the beginning, I don't know if you guys caught the numbers, but we are doing somewhere in the area of 70 requests... Oh, sorry, 77 requests to render the page. It was like 646K. Now that I've made all of these optimizations, I've saved everything. Let me go ahead and refresh the page. And you can see that we've dropped down dramatically from 27 requests down to 12 requests, and from 646K to 5. Huge savings in about five minutes worth of work. So I think that that's really powerful and something that you should definitely be looking at. About 80% of the problems on the Internet are due to the network. You'll also notice that I still have a lot of images. Those are all the little team images that show up on the top of the baseball card. I could sprite those up and reduce by a factor again, but I think you guys get the point. So let's go ahead and move on. So we can do less, right? Fewer HTTP requests and less bytes on the wire, but there's other strategies that we can use to help out on the network. The first of which is to procrastinate, right? If we know somebody doesn't need something right away on our page, we can give it to them later. And so there's a couple of ways that we can do this. First of all, all browsers support this script async attribute. If you've done any performance optimization, you know the best practice is to put all of your script tags as low in the page as possible. You want to have it before the end body tag. The reason for that is because the way that a browser parser works is when it hits a script tag, it stops everything. It stops painting the page to the screen, and it goes off and it downloads that script file. So you have to wait for the script to download. Once it's downloaded, it executes the entire script file. So you have to wait for that to execute. And after all that's done, it will continue to paint the page. So if you push them down to the bottom, most of the page should be painted before we have to run into that whole bottleneck. Sometimes we don't have that luxury. Sometimes we need to put like a Facebook like page somewhere on our page for our customers. And so you put a script tag somewhere in the middle. Well, Facebook and everybody else has agreed to follow this async attribute. And what ends up happening here is there's actually two parsers in all the browsers. There's a pre-parcer. The pre-parcer runs through the DOM and all it's doing is looking for things that need to be downloaded. When it sees this script tag, it will say, hey, you know what, this is async. I'm going to mark this to be downloaded at the end when everything else is downloaded. So it doesn't get downloaded in order. So it procrastinates and says, we'll download this later because it's not 100% essential if they get it a second or two later after the content. We're fine with that. The other thing that you can do, and right now this only works in IE11, is you can add this lazy load equals one attribute on images. It works the exact same way as as a sync. Basically the pre-parcer finds that and marks that as a lower priority resource to be downloaded. Funnily enough, IE is the first implement. It's a part of a specification that the W3C is working on right now called the resource prioritization specification. And eventually every element that downloads an asset, you'll be able to put lazy load on. But as of right now, it's only images and only in IE. So those are both very powerful to procrastinate. The opposite of that is you can anticipate. And this is really interesting because this works in most of the browsers. As you can see, except for the last one, we have, I'll talk about that in a second. So you can put these meta tags, these link tags in your pages, and as we move down the list, they get more and more powerful, but they will use up more and more resources from your user. So the first one is this DNS prefetch and you point to a domain name. And when the browser parser sees that, you'll probably put it in the head near the very top, it resolves the DNS of that domain. So later on, when you request an asset from that domain, you won't need to resolve it at that point. So a good example is if you're putting your images on a CDN and all of those images are at cdn.mywebsite.com, you need to resolve that host name by putting this at the top of your page, you'll save yourself about 200 milliseconds off the DNS resolution. So that's cool. Small savings, but pretty cheap. You can do a couple of these, it's not really going to affect your user. The next one down, slightly more powerful, is prefetch. Prefetch, you point to an actual asset. So you can go off and download something that you know your user is going to need later. So maybe there's an image that you're going to be showing when they click on a button that you anticipate them to click. You can go and download that so that way next time or when they click the button that's going to show the image, it's already been downloaded. You don't have to wait for that download. Now here's the trick. All that this prefetch does is make an HTTP request. So if I download some image, and there's no caching on that image, when the user clicks the button to make the image display, the browser's just going to request the image again. The browser doesn't know that it already prefetched something. So if you're using prefetch, make sure you cache whatever asset you're using. Otherwise, you're going to cost your user twice, which is not what you want to be doing. The last one is really cool, but it's not supported in Firefox. It's called pre-render. Pre-render, you can point at any page. And what essentially happens, this is the way that I like to think of it, is the browser goes off and downloads that page, and all of the assets associated with that page, and executes all the JavaScript running on that page, and it does all of this in a hidden tab. And when you click on the link on your page that would lead to the pre-rendered page, the browser takes the hidden tab and takes the tab the user's on, and it swaps them. And it's instant. So let me show you that. I'll close the tools here. On the home page, if I view source, you can see that I have this pre-render in here, and I'm pointing to the champions page, because I find that most people come to my website because they want to see who won mascot mania, as you would. So, but because I have this pre-render, let me go ahead and press shift, escape, and I just opened up task manager for Chrome. Very similar to task manager for Windows, but this is showing you all the Chrome processes and what they're doing. You'll see that all of my tabs are up here. I have the tiny PNG tab. If I go and close tiny PNG, that tab disappears. Let me dock these things like this so you can see a little bit better. So tiny PNG is gone. You can see my mascots. You can see octane. This tab will look at a little bit later. But you'll also see, oh, you know what? I've probably been on this for too long. There you go. You'll also see I have this tab called pre-render. Mascots of minor league baseball, and you can see it's going to champions. So there is actually a hidden tab there. Now, we made a whole bunch of performance optimizations. Let me undo them. So I'm going to come in here. I'm going to delete the new files that we created. I'm going to revert the files that we changed. I'm going to reload everything in Visual Studio. I'll rebuild. And I will reload this page. All right. So I think we'll all agree now that we're back to the old crummy version of the site. But because of the pre-render, oh, man, it's like puberty all over again. But because of the pre-render, when I click on see all winners, look how instant that was. I have all these big images showing up, and it was instant. You'll also notice the pre-render tab disappeared. Chrome shows you what's happening with pre-rendering in another way. Let me go back to the home page. Because if you go to Chrome slash network internal slash pre-render, you can see here that it's telling me here's all the pages that it currently has pre-rendered. Here's all the hidden tabs that it has right now. You can see that the champions has been pre-rendered for 10, 15 seconds now. And if I come on here and I click on that link, you'll see, boom, that pre-render dropped down to here and says that it was used. And you can see that it's been canceled before. You'll also notice that I thought all of these other things that were pre-rendered when I did a search on Google. Because Google uses this trick to pre-render the first link on the search results. That's why the first link is always so much faster than everything else. Because they're actually rendering it before you click on it. So that is anticipating what the user is doing. So quick summary of what we can do in the network. We can do less. Fewer requests, smaller payloads. Showed you a bunch of ways that you can do that really quickly. We want to procrastinate and anticipate. Another way that people procrastinate is they use JavaScript to only download images once it's come into the viewport. I think a lazy load thing is going to solve that problem. You don't need to do it anymore. But if you have a ton of images, you can think about using scripting to do something like that. Okay, cool. So when we're talking about performance with the web app, we have to think about two different modes. We have to think about the user getting the app. And we have to think about the user using the app. So all of the network so far that we've talked about is part of the user getting the app. But the user can't even begin to download things until they've received the, what I like to think of as the main manifest of the page, the main HTML page, the thing that you're creating an ASP.net or a PHP or whatever your web framework of choice is. And so that's the server. Now there are tools to help on the server to figure out what's taking a long time to render the page and whatnot. But you're not going to use developer tools that are built into a browser for that. Instead, you'll use a CPU profiler. Now, I mentioned that I was sponsored by Redgate and they happen to make a performance profiler. I'm not a sales guy. So we're not going to dig into this too deeply. But I'm just going to show you this quick little video so you can basically see the way that these things work. What we care about on the server is how long things take on the CPU. So in Ants performance profiler, you can profile all different kinds of applications, but you'll see there's a large section for doing things around web development and ASP.net. Works with any web server, IIS express, IIS regular, even Cassini. And once you start profiling a site, you can browse around the site and you can see that we keep track for you. That's the red humps, how long or how busy the CPU is at any given time. And at some point, you can stop it, which I'll pause right here. You can select a period of time over the red graph and it will show you the call stack at that moment and which methods are taking the longest. Descending granularity all over again. You find the slowest method, you go in and you tweak that. Now, here's the problem with.NET and C-Sharp. We could spend the entire day talking about ways to improve its performance. I'm not going to bore you all with that. Instead, I'll give you some quick highlights of things that I've seen and learned by talking to the engineers on the profiler team. Four web devs. The number one expense that you have on the server period is leaving the process. Leaving the process is doing a database query, doing some web service call, things like that. Move everything in process as possible. That's why caching is important. Data caching. Because you're moving things that were once out of process into memory, something that you can access quick. So cache library, stay local. Iterate less. A lot of times, you'll find when you look at a CPU profiler like ours, that the method that took the longest to execute took that long because it was hit many times. It was run many times on a given HTTP request. That's usually because of looping. So let's say that we have some method that gets called 100 times. Sure, we can go in and try to optimize that method and we might shave off 2 milliseconds. But it would be better to find a way to drop the iteration from 100 to 80. So if we can filter the collections and only loop over the things that we absolutely need, that's the best way of going about it. The last thing that I see time and time again is people loading data all in one shot. You need some XML configuration file or you're reading some feed that comes in from one of your vendors or something like that. And that just kills your memory. So stream data in any time that you can. There's a couple other little miscellaneous things. The one that shocked me the most, I didn't know this until I worked at Redgate and I was talking to the profiler guys who really dig into the CLR and performance optimizations around that, is string concatenation. Here's the rule of thumb. If you're concatenating three strings, you're probably okay. But even then, you might want to use a string builder. Any more than that, string builder. Better performance every single time. I'm lazy. I can concatenate 15 things together because I don't want to, like, knew up a string builder. But that's what you want to do. There's lots and lots of more things that you can do in.NET to make things better, right? We have async now that's really powerful. You should look into that. There's ways to improve your queries that you're making from the server. These slides are all already available and you can follow that link to find lots and lots more tips and tricks. Cool. So, server rendered, set down the manifest, everything downloaded on network. We just covered everything that we needed to do really quickly to get the application to the user. Now, let's talk about how the user uses the application. And so, for that, we have compute. So, I just showed you a quick little video of a server-side profiler. I didn't drill into it too much because there's also client-side profilers. And so, once again, those are built into your F12 tools and they work... Come on, F12 tools. Ah, function lock. There we go. And they work in very much the same way as the server-side tools. This is built into IE and Chrome for sure. I'm pretty sure Firefox has a profiler as well. Now, my little mask, it's a minor league baseball application. It's not very JavaScript heavy. So, it's kind of a boring thing to profile. So, instead, what I'm going to use is this thing called Octane, which is a JavaScript benchmarking tool that the Chrome team uses to see how Chrome is performing from build to build. So, all I'm going to do is I come in here to profiles. I'm going to collect a JavaScript CPU profile and I'll hit start. Once that starts, I'm going to run Octane. So, Octane runs through a bunch of separate benchmarks. You'll see a couple of interesting ones in there. They run a Game Boy emulator, which I think is kind of fun. They render a PDF file in JavaScript, which is cool. And that's the way that Firefox does it natively nowadays. They actually compile TypeScript because that's a very large JavaScript application and they want to see how they can compile TypeScript in the browser. So, it runs through all of these different benchmarks. It gives individual scores for each of them. And eventually, I will have a score overall for how my machine and the browser is doing against all of these crazy pieces of JavaScript. So, one second, we should be good to go. Great. And I will stop the profiler now. And so, what I get is this down here. So, the first thing I'm going to look at is this breakdown. This is the breakdown that's very similar to what you might have seen when I paused the video. It's a breakdown of all the different methods and how long those methods took to execute. That's quite handy. Not really for me because I didn't write this code and I don't know what these methods mean. But if I wanted to focus in on one of them, I could select it and press this little eyeball and that's going to focus in on that part of the call stack and then I can drill down into there. And so, that's kind of cool. I think Internet Explorer actually has a better experience around this right now. I know. I've not said that in many, many years. Internet Explorer has a better experience. But one of the things that they do is they identify libraries like jQuery automatically and allow you to also add additional libraries. So, they filter out library code because I'll see a whole bunch of jQuery stuff in one of my websites and that doesn't really help because I'm not going to go and perform as optimized jQuery. I'm just going to let them handle that. But filtering all that noise out is great. One of the cool things that Chrome does, though, is it has this flame chart. A flame chart across the x-axis is showing time and across the y-axis is showing the height of the call stack. And so, what that allows you to do is from a really high level start to identify patterns in your code. So, what I can see here is if I zoom in on this point here, I see all these little tiny dips. Let me zoom in here. I see these little dips, right, where we kind of go down to nothing. And you can see them right here, these little pink marks there. I've been able to deduce, figuring that out, that that was a little gap in time between each one of the separate benchmarks ran. So, I can see that pattern and focus in on the second one if I wanted to. I also see that this pattern here, it's very sawtooth. You can kind of see that, like, these stripes, they look like barcodes down here at the bottom, but they're kind of doing the same thing over and over and over again. Well, why is that? Oh, because we're looping. And that benchmark, they're looping over something. So, you can get a really high picture, well, my brain is fried. We can get a really high-level picture of what's happening in the code, and then you can drill down, you can select a subset of it and fix that specific method. So, basically, the things that you want to do with JavaScript, like my high-level tips, are to manage your scope well. So, you may ever heard people tell you, make sure you're using the var keyword, because if you don't use the var keyword, the variable goes to the global scope. Well, anytime you access a variable in JavaScript, it tries to find that variable in the local scope first. We'll scan through that whole table, if you will, of local variables. And if it doesn't find it, it will move up to the global scope. So, you can save yourself a lot of extra scanning if you make sure you're using var appropriately and putting things in the closest scope. Closures and the width statement, never use the width statement. All it's doing is adding an additional scope. It's three layers of scanning now. It slows everything down. Enclosures kind of do the same thing. Those are really hard to avoid, but be careful what you're enclosing. It encloses little as possible to manage your scopes. Next, this is straight from Crockford's book, Avoid 4-In Loops. 4-In is horrible. It has really bad performance across all the browsers. The vendors know it. They're in ECMAScript 6, which is coming soon. They'll have a replacement for it. Don't do it. But if you do some research into how to improve JavaScript performance, the biggest news that you're going to hear is, avoid the DOM. It's like being shouted from up high. Avoid the DOM, which I think is stupid. Because the whole reason I'm writing JavaScript in the browser to begin with is because I need to manipulate the DOM. I'm reading some values that a user has given me. I'm changing something on the screen. Avoiding the DOM doesn't really help me. So, it's a nice thing to say. But the reality is what we need to do is we need to understand what to do with the DOM so we can use it in a performant manner. Now, to do that, we need to talk about frame rates. That's because when we touch the DOM, we create a ripple effect. A ripple effect that we don't even see programmatically and until recently haven't had the tools to measure. When I touch the DOM, it means that something needs to change on the screen most of the time. And that means that the browser's rendering engine, which works 100% declaratively, needs to repaint something on the screen. And that takes time. So, I'm not much of a video game player. I like my Super Mario brothers every now and then. But that's about it. But I do know that PC gamers are really big on their frame rate. That's an important thing, is frame rate. And when any time we're talking about rendering and how fast something can render, we're thinking about frame rate. And the frame rate that we want is 60 hertz. 60 hertz is what almost every monitor runs at these days. It's basically what the human eye can perceive. What that means is that my screen right now is refreshing itself. It's repainting every pixel 60 times a second. So, one second is 1,000 milliseconds. And we're doing a refresh every 60. So, take our one second. Let's divide it by 60. And that means that we have about 16 milliseconds in between every time a page or every time the screen refreshes. That's about how much time that we have to make a change. So, Chrome allows us to measure this. I'm going to come back over to our application. And I'm going to open up this timeline. I'm going to do something really simple. I'm just going to press record. And I'm going to scroll. Okay. That's enough data for me to sample. That's fine. So, I clicked on this events mode. And what it allows you to see is all of the yellow is when some event happened. Blue is network time. You can see that anything in purple is layout green is paint. This is kind of a hard way to see it. I'm going to switch over to the frames view. And you can see each one of those stacks is a frame being painted and how long it took. And you'll see I have these two benchmark lines here. I have the 30 frames per second and the 60 frames per second. I just told you that 60 frames per second is what the human eye can perceive. And you'll notice that on my website, I'm going over 60 frames per second many a times. And oftentimes, I'm shooting way, way below 30 frames a second. This creates a phenomenon known as jank. You've seen jank if you ever scroll to website and it's really sluggish to scroll. The browser is working too hard to paint. You'll especially notice it on a touch device. Jank shows up on a touch device when you try to scroll or you move the page and it doesn't stick right on your finger. It's like you move your finger to the top and then the page follows you, right? And your finger is always just a little bit ahead. That's jank and it's bad and it's because you're going at less than 60 frames a second. So let's dig into ways that we can fix this. So a quick review of what we just saw on that timeline. There's a bunch of different colored bars. They're all stacked up and they mean different things. So the blue is network activity. That's when we're waiting on downloading something. Yellow is JavaScript executing ours or library. Purple is recalculating styles and layout. That's the browser creating the geometry of the page, making all of the squares that you need to lay everything out. It creates the skeleton. And then green is the painting when it paints the flesh over the top of the skeleton once it's calculated the geometry. You might have also seen some gray or clear stuff. That's stuff that Chrome for whatever reason isn't able to measure or just some idle time. We're not going to worry about those for right now because we're going to focus on the big picture. So here's our timeline. Every 16 milliseconds, the rendering engine or the graphics card is going to repaint. And the problem is with JavaScript in events, we do everything on an event, somebody might click a button and they might click a button right at this moment here with two milliseconds left and you're trying to make a change and you expect it to be painted two milliseconds later. You're not giving yourself enough time. By the time you do your JavaScript and all the rendering, you're not going to get it done in two milliseconds. So what we can do is we can sheet a little bit and we can use a method called request animation frame. It's supported by all the browsers. Request animation frame takes in a callback function and it runs every time the rendering engine just finished painting a frame. Which means that during request animation frame, you basically get almost the full 16 milliseconds. That is how to give yourself the most amount of time. You don't want to be running at some event that happens randomly in the middle. You want to be trying to run at the request animation frame. And in that callback, you run your JavaScript which touches the DOM which will affect paint and have that ripple effect. And if you can get all of that done, including the ripple effect within 16 milliseconds, you will have a screaming, fast sight, easy to use, no jank, 60 frames per second. And so every 16 milliseconds, another request animation frame method gets called and you go on and on and on. But you can see here this third time, my JavaScript took too long to execute. And so when it finished, there wasn't enough time for the rendering engine to paint. And we went over that 16 millisecond boundary. Which means that the browser drops the entire frame. It's just going to wait until the next interval to pick up again and try to paint something. So that's what's happening on my website right now, which is why we're not hitting the 60 frames per second. So with request animation frame, right now, you'll see that as I scroll this page, I have this little badge up here that shows you the icon of the league that we're showing. So when I scroll over to Eastern, it changes to Eastern. And when the Florida State League gets to the top of the page, we switch over to the Florida State League. That's the only real JavaScript that I have on the page. It's happening on scroll handler. You can kind of imagine what that code looks like. It's quite simple. Here's how we could change the code to leverage request animation frame. So I'll capture the scroll position of the window. That's what's happening on that first line there. And then I'll check to see if I've already scheduled the change to happen of the image in that if scheduled animation frame. And I'll return if I have otherwise, I will set up my request animation frame to swap out the league badge. So what this does is a couple of things. If the user fires the scroll event more than one or two times in that 16 milliseconds, right now, I'm trying to do that work as much as I can on the browser. It doesn't matter. If I could get it to happen two or three times within those 16 millisecond boundaries, they would only see one update because we're going faster than the screen. And usually we can't. So what we can do is we just slow everything down and we only update when the page is getting refreshed. We're doing a whole lot less work because we're not just stacking up all this work to do every single time they scroll. So this would be a great change for me to make to this website and use request animation frame. Another pattern that we see that's really bad with JavaScript and the DOM is an anti-pattern known as layout thrashing. So this code seems innocuous enough. We may be written something like this. So I'm looping over a bunch of paragraphs. I'm taking each one of them. I find the width of some div and I move the paragraph over to the left so it's the same. So it's right next to the size of the div. No big deal. I just loop over them. But here's what happens. The browser is honest aid. It will never lie to you. The browser does not lie. So the first time we come through this loop, we grab the paragraph off the collection, we get the width of this div, and then we change the left style of that paragraph. When we run this line of code, the browser knows that there's been a change to the DOM and it marks the entire DOM as dirty. No big deal. It's dirty. Until we come back around the loop and we get back to reading this offset width. Like I told you, the browser will not lie to you. So you're asking for the width of a div now that theoretically could have been changed because you moved that paragraph in the iteration before. So the browser stops on this line and says, you know what? They changed something. Let me recalculate the entire geometry of the page. Let me do all of that work so I can figure out what the real width of this div is and that returns it to you. And you do the exact same problem over and over and over again. Where you change one value, recalculate the layout. Change the value, recalculate the layout, and you're doing it over and over. So the easy fix here is to do all of your reads before all of your writes. So in this case, I read that offset width once. I do a bunch of writes. So the DOM gets marked as dirty here. But on the next iteration, I'm not reading anything. So it will let me continue to write, write, write, write to the dirty DOM. A dirty DOM is a dirty DOM. And eventually, at some point, I will do a read or the screen will update and it will calculate it what it needs to. But I'm not forcing it to calculate the layout anymore. So hopefully, that all makes sense. So those are some things that we can do with JavaScript. There's actually an interesting library I would recommend toying around with it called Fast DOM, which uses RequestAnimationFrame to allow you to access the DOM in the way that you want to, like this anti-pattern here. But what it's really doing in the background is it's putting all of the reads and writes into RequestAnimationFrames and running them in the right order. So I feel like I would mess myself up doing that. But big sites use that library to avoid layout thrashing. It forces you to write in a way that you can't get into layout thrashing. Okay, cool. So that's the JavaScript. And those are things that we can do to make the JavaScript faster. But like I said, we're still going to have a ripple effect. And at some point, we are going to want to paint. So let's look into what we can do with that. So on this page, I'm going to go ahead and I'm going to enable this thing called Show Paint Rectangles. Now, what Show Paint Rectangles does is you can probably see on the screen that there's a green tint that's going around that logo every time I scroll. You'll also see there's a green tint going around the scroll bar. What Show Paint Rectangles is doing is it's literally putting in green wherever the browser just painted. So on every little scroll, I'm just continually pressing the down key on my keyboard, every little scroll you're seeing that I'm repainting that logo, which is weird because it's not changing. And yet it continues to get painted over and over and over again. That's because it is an absolute position. And the background behind it is subtly changing. It's a transparent PNG and the gradient texture behind that image is subtly changing as every time I go. And browsers render in tiles. So if I turn on this composited layer borders, you can see these blue lines. These are the tiles in which the GPU renders the page. And so any time one of those tiles gets marked as dirty, it needs to repaint that area. So what can we do to fix this? We don't really want to be painting that thing over and over again. So I am going to select this image. And you can see I have it here in the DOM Explorer. And there's a class on it called League Badge. So we're going to do a little trick here. I'm going to say dash webkit dash transform colon translate Z, which is a function. I'll pass in zero to it. Now, you might have noticed instantly that I now have an orange square around that league icon. So what I've just done is I've moved that image into a separate layer. Now, these aren't layers like you would think of layers when you think of a Z index. These are layers that the GPU uses to render and do compositing, which essentially means that this California League logo is almost, you can almost think of it, it's on a page of its own now, as far as the GPU is concerned. The GPU will render the entire background, everything but that orange square and the orange square separate. And then when it pops up on the screen, it just puts them one in front of the other. So that's pretty simple. And what that means is now, when I scroll, because it's in a separate layer, I changed to Carolina, so it went green. But you can see the entire time I'm on Carolina, I'm not getting green. I'm not repainting that thing because it's separate. And it's not changing because it's separate. When I get to Eastern pretty soon, and it's going to change, you should see it go green. And boom, it goes green, but now it's not. So I'm doing a lot more scrolling with a lot less painting, which is going to help my frame rate. So that's a pretty cool little trick. Now, you might look at me and say, well, how did you know to put WebKit translate Z on there, Nick? Well, the reality is it's a hack. This is me knowing about an implementation of the way that Chrome's rendering engine works. And I know that anything that has a translate Z on it will automatically get promoted to its own layer on the GPU. And the Chrome team actually recommends that you do this from time to time, but they kind of do it with an asterisk saying, we know that that's kind of dirty because you're leveraging an implementation detail. There are other things that promote layers, and the Firefox rendering engine works the same way. So video elements and canvas elements automatically get promoted, things that have opacity animations, composited plugins like flash and silver late, they all run in their own separate GPU layer. There's a few other things that are there. There's a spec that's currently in debate right now with the W3C called will change. So the reason people don't like the translate Z is because translate Z actually has a function, and we're not using it for that function, we're using it to screw around with the GPU. The will change spec would allow me to, instead of saying translate Z to say will change one, to which I'm telling the browser that this thing is going to constantly be changing, so I want you to pull it out and render it in its own layer. And the CSS community is a little bit of a kerfuffle about whether or not this is a good idea right now, but keep an eye out for the will change spec, and when the will change spec is in place, this will kind of work everywhere as soon as the browsers adopt it, but until then, WebKit translate transforms Z. Great. So we made that change. Let's see what else we can do. This painting thing is really cool. I like seeing the green. I like seeing what's being painted. So there's another mode that I can do. I'm going to enable continuous page repainting. When I do that, you notice my entire page goes green and stays green. That's because when I'm in this mode, I am forcing Chrome to repaint the page over and over and over and over again as fast as it possibly can. While it's doing that, you can see, wow. You can see that I'm getting page paint times up here in the upper right-hand corner. You can see this little graph. And my page paint times are sitting at somewhere around just under 40 milliseconds a refresh. 40 milliseconds every time I refresh my page, and I'm not even doing anything. No wonder why I'm not getting in with 60 within 16 milliseconds. I'm not getting that 60 frames per second. I'm taking too long. I think it's number to be somewhere south of 16. That's our magic number. So what we can do while we're in this mode, we can leave this little widget up here running and try to figure out what is actually taking up so much cycles. So I'm going to use this DOM Explorer again. I'm going to select this element here. And this works at any time in Chrome. If you press H on an element, it hides the element. So I got rid of that element, and we can actually see that it looks like things went down a little bit. I was taking a little less time to paint. I'm just going to continue to hide things on the page. You can see my numbers are continuing to drop as I hide things. Of course, the page is getting simpler and simpler. Now I'm right about the number I need it to be. I probably need to be a little bit less than that. So this is really cool. You can just kind of use the H key to hide things and basically bisect your DOM to figure out where all the painting time is coming from. So I mean, I can tell that these baseball cards are really causing some performance issues. So now let me drill into those and see what's causing the problem. So we'll look at Seawolf here. He's not that complicated. You can see there's the div with the background image. That's the actual picture. Here's the image that shows the Seawolf, the team logo. So what can we do here? I'm looking at the different styles. Well, I see there's a box shadow on the style. Let me turn that off and see what happens. So we're at 38 now. I turn that off. Holy moly. I almost cussed because that was amazing. I turned off the box shadow and I dropped down to 10 milliseconds to repaint the page. Well, cool. Let's see what else I can cut out. Let me turn off the border radius. I got another millisecond. Not that big of a deal. Let's turn on the box shadow again. Went up a little bit. Not that big of a deal. Still under my target number. Let me see if I turn them both back on again. Wow. So it seems like I can have one or the other and I dropped the performance big time. But these two properties in combination completely kill the rendering. So I'm going to pick one to turn off. Actually, I'll turn them both off. And so you can see that we've dropped down to eight and we've made this fix over here with the separate layer. I'm going to go to the timeline again. We'll clear or record. And you can see now we are getting almost all of our frames at or below 60 frames a second. We're getting a couple that go over and then we can dive into farther and figure out why that's happening. So we just dropped from somewhere north of 30 down, well, south of 30 to north of 60 with a couple of small changes. Now you might be saying to me, but Nick, my designer really wants rounded corners and drop shadows and you just cut that out and that changes the design of the site. That's fine. What I would recommend that you do in this case is preprocess your images. Go into Photoshop or whatever and add the rounded corners and drop shadows so it's still just an image that the browser is displaying. But you're not using CSS to add those effects and you're going to get a big performance boost and you'll be closer to jank free. Now I knew to go and look at the way that things were painted because I knew to uncheck the border box and the drop shadow because I know which properties affect which part of the rendering engine. So here's a handy little cheat sheet. If you see recalculate style, that usually has to do with your CSS selectors. Now there's a lot of articles out there on the internet that you can find about optimizing CSS selectors and the Chrome team used to have a CSS selector profiler that would allow you to see which selector was taking long and going optimize them. They feel like at this point that problem has been solved. They've even removed the profiler. You can still optimize, but it's a super micro optimization to worry about your selector at this point. Anything that manipulates the box model width, height, left, top, that's going to affect the layout. So if you're seeing a lot of purple layouts, go and focus on the box model properties. The rest of the properties are like color and background color and blah, that's all going to be paint time. And if you see green ones that say composite, that's usually things that are happening on the GPU. And things that happen on the GPU are good in general. It's very fast. It's very efficient at painting. And so there's four things that are considered to be jank free. That's scaling, rotating, moving, and fading the opacity of something. Those are all using those functions like the translate that I just showed, translate scale, rotate that are available. They're all hardware accelerated. So if your designer wants you to do some animation or some effect on an image, try to keep it to one of those four and you'll have a pretty good performance. So closing up, there's a bunch of resources available, lots of interesting things. I didn't cover the psychology of performance, which is a very interesting read for when you can't fix performance. How do you trick somebody into thinking that it's still fast? So I recommend that book. There's a whole bunch of other books up here. Anything by Steve Solders is amazing. Jankfree.org is really good if you're into the rendering stuff in HTML5 rocks. All of those resources are available from this QR code and link. If you have any questions, what time do we have? Are we at 12.40? Is it our 11.40? Yeah? 11.20. Oh, so we're five minutes over. So I'm going to be kind to the next speaker. I'll take questions out in the hall or you can tweet me at nickmd23. I really appreciate everybody showing up. Please leave feedback. Please visit the redgate booth if you appreciated this talk and say hi to those guys. Thank you very much.
 All right. Thank you for everybody for being able to wake up this morning and make it out.  I've been sick for the last couple of days, so I have this very sexy voice, which is why  I'll be using Notepad for the rest of the presentation. We'll just read the whole thing.  One guy. No, I'm not going to do that to you because I can't spell that well. My name is  Nick Molnar. I'm from New York City. I've been a web developer for a little over 15 years now.  Yeah, I'm honored to be here speaking to you at NDC Oslo. About three years ago, my buddy  Anthony and I, we created an open source project called Glimpse. Just so I can get a feel of the  room. How many people have heard of or used Glimpse? Show of hands, please. All right. Awesome.  I actually have a bunch of stickers, so if you guys are interested in stickers, swing by after  the talk and I can give you some Glimpse stickers. We did Glimpse about three years ago after about  a year and a half of being fairly successful with that in the open source community. We got  sponsored by a company named Redgate, who's also sponsoring this conference, so please swing by  their booth and say hi. What that has afforded me to do is spend the last year and a half focusing  solely on Glimpse. Now, Glimpse is a debugging and diagnostics platform for ASP.net, and it has a  lot of uses, but one of the main use cases that people like to use Glimpse for is around  performance. So, in reality, for the last year and a half, I spent a lot of time focusing on  web performance and the things that we can do to make web applications faster. So, if you're  anything like me now, you're super worried about this loader bar. It's making your skin crawl,  and I'm just hoping that this PowerPoint presentation loads in time for us to start the  presentation. Oh, good news. We loaded, so now we can be full-stack web developers. So, let's stop  and talk about why performance matters really quickly. To me, it's very simple, and I'm guessing  because you're in this room, you already agree that performance matters, but let's cover it really  quickly just in case you're not in that place. So, performance to me is usability. There's been  this whole UX movement. That's been a big thing. I think now that we need to start thinking about  performance in the same breath with UX and start treating performance as a feature. So, you see  this hashtag perf matters up there. That is not some crazy thing that I made up. That is actually  the hashtag that people who care about web performance use on Twitter and follow. So, if you  follow that, you'll get lots of great information, much of which I will give you in this talk.  There's tons of studies that say that performance matters. So, Google, I'm sorry, Amazon increased  their page load time by 100 milliseconds, and their sales decreased by 1%. You've heard case  studies like this. Google increased their page speed by half a second, and they lost 30% of  their traffic on a bounce rate. So, if you're in the e-commerce world, performance and sales  correlates together very well, maybe you're not. Maybe you're a content publisher or something  like that. Well, the performance of your site affects your ranking in the Google page index. So,  the faster your site is, the higher the ranking you are. So, not only does it affect sales, but  it affects traffic and eyeballs. And finally, we live in a world of devices now where I have  limited battery life on this thing, and if your website is slow or uses up a lot of my resources,  I'm not going to sit on it very long because I don't want to kill my battery, and I know of  certain websites that do kill my battery. So, for all of those reasons, I think that performance  matters. Here's another way of looking at it. This pyramid, maybe you've seen the my right-hand  side, your left-hand side before. It's Maslalov's hierarchy of needs. Maslalov was a psychologist  who many, many years ago before I was born said that human beings had these basic needs that  needed to be met first, and once those basic needs were met, they could move on to fulfill  more advanced needs, and that human beings flourish when they get to the top of this pyramid. So,  for example, I must have physical safety. I need a roof over my head, and I need to be warm before  I can start worrying about my self-esteem. And once that is met, I can move on. There's a guy  named Aaron Walter, who's the UX director for a company in the states called Mailchimp, who's very  popular and famous in the UX world, who has proposed a similar pyramid based off of Maslalov,  which is Aaron Walter's users of software needs. So, users of software need a couple of things.  First of all, they need the piece of software that they're using to be functional. It has to  solve a problem. We all know that because we're developers and we solve problems every day.  Next, it needs to be reliable. It needs to solve the same problem over and over and over again.  You may remember when Twitter had troubles a few years ago, and every other day you would access  the site, and you got the fail-wail. Twitter was not reliable, and during that period of time,  I rarely ever used Twitter, just because they weren't reliable. They fixed that now. Next,  software needs to be usable, and these are the kind of the order in which we should be thinking  about things. There's no point in having usable software if it crashes half the time. So, usable  software is easy to pick up and learn and easy to remember how to use when you come back to it  later. The next layer up is performance. That's the layer that I've added in. Performance is not  actually a word, thus the quotes, but I use it anyway because I'm American and we disagree with  a lot of things that Webster said. So, performance software is software that does all of the other  jobs, but quickly. So, let me give you an example. On my machine right now, I have two photo editing  pieces of software installed. I have Photoshop and I have Paint.net. 99% of the time when I need  to edit a photo, all I'm doing is cropping it. I never open Photoshop to crop a photo because  Paint.net is just so much faster at doing that job. So, performance matters. And then lastly,  Walter suggests once we've kind of nailed these things, we can start to make our application  pleasurable. We can add personality. We can put funny little images and gifts and things like that  that we see at the sites that we love like GitHub or Trello, et cetera, et cetera. So, this is why  performance matters. Let's, actually there's one more thing that we really need and that's Wi-Fi.  That's the basic. Just a quick show of hands. How many people were in my talk yesterday? Okay,  for you people, I'm sorry. I only have like three jokes. This was the new one. It's gone now. So,  I learned yesterday that self-deprecating humor is the best kind of humor around here. So, once  we have Wi-Fi, we're good to go. So, the problem is we need to figure out how we're going to solve  performance problems and yet we constantly have pressure from our bosses, our stakeholders,  our customers who all they want is more and more features, tighter and tighter deadlines,  and we need to accomplish all of these things. And so, I've found that the things at the top of  that pyramid are the first things that go when you're trying to hit those deadlines or those  outside pressures. So, I've come up with this attack plan and this is how we're going to kind of  try to focus on the performance problem. So, the first thing we need is measurable improvements.  Everything we're going to do today is really about me showing you a tool for how to measure  performance because if you can't measure it, you can't improve it. Once we measure it, we'll look  at ways of improving some common scenarios. Two and three, platform stability and environmental  neutrality, they kind of go hand in hand together. Basically, if I summarize that, it means do oranges  to oranges and apples to apples comparisons when you test your software before and after an  optimization. What does that mean? Run on the same hardware. Run on the same software. And when I  say run on the same software, I mean all the way through, make sure that caches are primed or  emptied the way they were between the two runs. Make sure your antivirus software isn't running on  one of the runs. Make sure you don't have any cron jobs going. You'll be amazed at how much you  could skew results with simple things that are happening on your machine all the time. Number  four is to be scenario focused. I was a consultant for a very long time before I moved full time  into the world of open source software and many times my customers would come to me and say,  the application is too slow. Make the application faster. I'm here to tell you, you do not make  applications faster. You make scenarios faster. You make the login sequence faster. You make the  purchasing path faster. You make the search faster. But you need to look at a scenario that you can  run a user scenario through and measure the performance of that and make that faster. You want  to have preset goals when you're doing that, especially as consultants. That's important  because we're constantly thinking about the money that we're burning. Trello was a really good  example a few months ago. They put out a blog post where they set this goal that they were going to  make the main card rendering page 50% faster. They were going to work on it until they did it. They  did it in five days. That's the luxury that Trello has. Me as a consultant, I usually don't have  that kind of luxury where I say, I'm going to work on it until it's done. But instead, you can  kind of flip it around and say, you know what we'll do? We're going to work on this one scenario for  the next sprint, the next iteration, and we're going to make it as fast as we can. It's a fixed  amount of time. You know how much budget you're burning up to make something faster. Then finally,  what I think is the most important one is you want to approach performance in the order of  descending granularity. So get the big wins first and then focus on the smaller wins as you go.  Everything that we talk about today is going to focus on that. On the web, most of the time,  remember, I'm going to say most of the time a lot today because you need to measure things,  most of the time the network is going to be where your biggest problems are. That's where we're  going to start today. The good news is also because that's the largest bit of problems, there's the  most documentation and best practices published about that kind of stuff. Okay. So network,  let's dive into this. So I have a very simple application here. It's called mascot.  Because I'm American, there's a bunch of things that you probably already know about me. One,  I'm fat. I like to eat. I like my baseball. I'm a huge Miami Marlins fan. They're half a game out  in the National League East if anybody cares. But I know you don't. So instead of making a website  about baseball, I made a website about baseball clowns, also called mascots. In America, there  is a minor league baseball system. There's 160 teams each with their own mascot. So this website  is dedicated to them. You can see all the individual sub leagues. I can go here to the  Carolina league. We can click on the link. You can see here's all the mascots from Carolina.  I also have this right here. You can click on the minor league baseball logo. You can see  all of the different mascots from all of the different leagues on one page. And then finally,  every year, there's a contest that the mascots have called mascot mania where they compete and  fun Olympic games like pie throwing. And I don't know what else they do. They throw a lot of pies.  And so, you know, there's winners of that. And so some of my favorite guys are in here.  Like this gentleman right here, his name is ball of pinio. He's a half jalapeno, half baseball bat.  You can't get any better than that, right? Those of you who were around yesterday also know that  I'm quite fond of Roscoe the P. Ray rooster because he beats people up. And I have no clue what  that has to do with baseball, but violence is good. Just joking. I'm playing the American  stereotype here. So what we want to do is we want to think about how this website is affecting  performance on the network. So the best tool to do that is F12. Now, F12 works in every single one  of the browsers. And for almost all of this next section about networking, the F12 tools in your  browser of choice will get the job done. I'm showing Chrome because that's the one that I prefer,  but you can use Firefox or IE. You're going to get almost basically the same results.  So what I want to do is I want to scroll over to the Texas league. I'm going to go to this network  section. Let me go ahead and clear it. I'm going to refresh the page. Now, what you're seeing down  here is all of the HTTP requests that were made to render this page to the screen. There's lots of  information here. You can see there's, if I can come over here to filters right here, and I can say  that I only want to look at images. And so you can see that I have 22 of the 27 requests that were  made on this page were for images. I can show you why that's the case. The way I've constructed this  page, I tried to make the mascots look like baseball cards, which means that the little team  logos you see right here in the bottom right hand corner are separate images that are layered on top  of the mascot image. So there's two layers there to make these baseball cards. So everything that  you see there is two image requests. So that's kind of cool. We can filter and do stuff like that.  We can dig into any individual request and we can see all the HTTP headers if that interests you.  What I find particularly of use is the size, content, and time latency. I don't know if that's  me or not. The size you'll see there on the first one is 4.0 kb. The next one is 8.1 kb. And you can  see a number in gray, 3.6 and 7.8. The number in gray is the actual amount of usable content that  Chrome was able to use. The number in black is the amount of content that was downloaded. So you  can see that for each one we have about.4 k overhead of content that we couldn't use but that we  downloaded. That is the overhead of an HTTP request that you're seeing over and over and over for all  27 of our requests. On the time, you can see 35 milliseconds with 34 underneath it. That is how  long it took to download but the latency is how long we took waiting on the network to actually  start the download. Now, I'm running all of this locally from my machine so the latency times  aren't, the download times aren't much more than the latency because I'm getting it fresh off a  disk. But you would see much worse performance in other places. There's some other cool things  that you can do. You can turn on this preserve log and if I turn on preserve log and I browse to  another page, the network tab keeps the first request and adds in a bunch of other stuff from  the next request you can see here for local host. I can also right click in here and I'm going to  copy all as Har. Har is a standardized format that's based on JSON that's used for performance  experts to pass around this kind of information in a standardized format. What's really nice is if  you see a performance problem on your website, you can copy the Har file, you can email it to a  buddy and they can use any tool on their end like HTTP watch or Fiddler to load that information  back up. So I'm just using an online tool. I just pasted the JSON that was copied and if I do a  preview, you can see here are my two requests. I can move back and forth between the two of them  and see how they were broken up into what was waiting on DNS, how much of it was images and  what not. And I can see all of the requests and their headers. So it's a good way to preserve  that information for later analysis. Not a lot of people know about Har's but I think they're  very useful. So you may be saying to me, well, that's great, Nick. You just showed me all this  way to do measurement but now how do I know how to improve things? Well, when you've been around  the block as much as I have, you kind of get some intuition for what to do. But the Chrome team has  done a couple of interesting things to help you so you don't have to understand all of this.  The first that I'll show you is this audits tab. If you come to audits and you hit run,  it will scan through basically all the information that was present in the Har file and it will give  you recommendations. It will tell you what you need to fix. And they present them in this red,  yellow, green fashion where you fix the red first. Once again, they are following descending  granularity. So you can see here that enabling GZ compression and leveraging browsing caching are  two of the bigger things that I should do. I have installed an extension to this auditing tab  that gives me accessibility information. So you can see here things like my images should have  an alt attribute. And this auditing tab does some really cool things too. You can see here that I  have a bunch of unused CSS rules. If I drill into this, you can see that 15% of my screen.css file  is not used. 79% of my normalized CSS file is not used. And my validation file is not used at all.  So these are great places for me to go and start making performance optimizations.  The Chrome team also ships a separate plugin called page speed. Why they didn't build this  into the audits tab? I don't know because it's almost the same as the audits tab. But it's  kind of the audits tab on steroids. So once again, plugin that I have installed, I hit start analyzing.  It will give you many of the same recommendations, but a couple of extra ones. It goes a little bit  farther to helping you solve those recommendations. Let me show you what I mean. So there's a  recommendation here to optimize images. If I click on it, it will show me all of the images that I  could optimize. And if I open up one of them, it will give me the compressed optimized image. So  that is the team logo for one of the teams. All I need to do is right click on this, save as,  overwrite the file that's on my disk, and boom, I've optimized that image. I didn't have to  go find any external tools or anything like that. Chrome just does it for you. So that's really nice.  So there's a bunch of recommendations here. Let's look at how we would actually fix these  things in one of our websites. So I'm going to open up the code for this minor league baseball  thing. And we're going to fix a couple of problems. The first thing is we're in web config, and we  want to compress data. We want to squish it down using gzip or deflate and send over as few bytes  on the wire as possible. It's important to do less. That's the first rule of performance  optimization. Do less means two things when you're on the network. Fewer HTTP requests and fewer  bytes over the wire. So all we have to do in.NET is change a couple of values. You can see here  that I'm looking at URL compression under system web server. And I'm going to do static compression.  I'm changing that to true. All of my static files, CSS, JavaScript is now going to be compressed.  And all the modern browsers, and when I say modern, I mean even back from like the last  eight years, know how to deal with this compression. And if they don't, ASP.NET knows not to compress.  You can also do dynamic compression. This is on the HTML that you're generating on the fly using  Razor or something like that. You can run into some problems with dynamic compression, so you need  to test it. But for this website, it's fine. It's great. In about two seconds, I've just added  compression to my entire website. The other thing that we saw as a big recommendation is to cache  our content. So by me uncommenting this one line and system web server, again, I'm saying that for  static content, I want to cache it on the client using HTTP headers. I'm going to use the expires  header. It's going to add a header that says that any assets that are downloaded are valid until 2020.  So nobody will try to redownload any of my images until 2020 unless they clear out their browser  cache. They get it one time, and every time they go back to the page, it'll be fast. So that's great.  Two quick updates. The next thing I'm going to show you is bundle config. This shipped recently,  I think,.NET 4 and ASP.NET. This is a feature that allows you to take your JavaScript files and  CSS where you might have multiple. You saw that I had at least three CSS files and merge them  together at runtime into one file, which means I'm going to go from three HTTP requests down to one,  which is great. And so what I've done here is I've created a, what I'd like to think of as a  virtual file at tilde slash content slash CSS. That's not actually a file that exists. I just  picked an arbitrary name. But what I'm telling ASP.NET to do is to take these three real files,  normalize validation and screen CSS, and combine them into this one fake file. So now that I've  done that, I can come into my layout. You'll see here is where I'm including those three real files.  I'm going to comment that out so they're not included anymore. And instead, I'm going to tell  ASP.NET to render. Once again, there's that fake file that I had, tilde content CSS. So now only  one CSS file will be there, but it's really all three combined. The next thing I'm going to do is  leverage a plugin that I have for Visual Studio called Web Essentials. If you're a web developer  on the.NET platform, Web Essentials is aptly named because it's essential. So one of the things  that I'm going to do is I'm going to come over here. We've been looking at the Texas League. And so  you can see that I have my content. I have a bunch of images. I have small mascot images. These are  the actual pictures of the mascots. And I'm going to go into the Texas League. And I'm going to right  click on this folder. And I will go up to Web Essentials. And I will say create an image sprite.  Okay, that's fine. We'll save a.sprite file, which is an XML file. You'll see this pop up.  This XML file has a bit of information in it. It's really just a manifest. You're seeing all of  the different PNGs that were in that folder automatically included. You can see that we're  going to do a vertical orientation. And now you can see down here in this message that the sprite  has been generated. So let me browse to this and explore. Here you can see my sprite that was just  created. And so you can see all of the mascots are now one image stacked one on top of the other.  So if we take a look at this image, you can see the size of it  is 340K. I think that we can do a bit better. So I'm going to come over and I'm going to use,  there's lots of tools for doing this. I like using online tools. I'm going to come to this thing  called Tiny PNG. I'm going to simply drag my sprite over here. I'm just going to upload it.  Tiny PNG does lossy compression. It's actually going to cut some of the bits out. I theoretically  am losing some quality here. So I've looked at the before and after pictures beforehand and I can  tell you that I can't tell the difference with my naked eye. So I'm good with it because as you can  see, I've gotten a 72% increase in size. I went from 348K down to 98K for all of my images because  that's that one sprite. So let me go ahead and I will right click on this, save the link as,  and we will just replace my sprite. Great. So now this image is 95.6K.  Okay. What other optimizations can we do here? Okay. Well, so now I created the sprite. The other  thing that happens when you create the sprite is WebEssentials creates a CSS file, a less file,  and a SAS file for whatever flavor of CSS you like to do. If I open up the CSS file, you can see,  oh, that is not the file I wanted to open. If I open up the CSS file, you can see that it's  made a bunch of classes where the class is the name of the mascot or the file. So you can see my  buddy, Bala Pina, right there, Deeson Hornsby are right behind him. So I'm going to include that in  my layout. I'm going to kind of cheat here instead of merging it in to save time. I'm just going to  drop the link in there. So now we have two style sheets, but it's still less than we had before.  So that's great. And I'm going to update the layout file that we used to render the page. So before,  you can see that we were doing an inline style to set the background image to the mascot, right,  all of this. Now that I have a real CSS file, I'm going to change it so that we do a class. And that  class is going to be the mascot's slug. So the slug for Bala Pina is Bala Pina. So that will  match the class name of the CSS file. Let's see what else we can do. On the page, let me go back  to this page. Oh, you know what? Let me... On the page, you can see that we have this little baseball.  That baseball is just a tiny image that shows up next to all my headings. I don't really need to  download that over and over again. So instead, Web Essentials again gives me this little tool tip  that I can go. So here's that ball. It gives me this little tool tip where I can say that I want to  embed the image as a base64 data URI. When I do that, I get all this gobbly book. But that gobbly  book is actually the image base64 encoded and put into the spreadsheet automatically for me. So now  when somebody downloads that style sheet, they will also be downloading the baseball, and they  won't have to do a separate request. So at the beginning, I don't know if you guys caught the  numbers, but we are doing somewhere in the area of 70 requests... Oh, sorry, 77 requests to render  the page. It was like 646K. Now that I've made all of these optimizations, I've saved everything.  Let me go ahead and refresh the page.  And you can see that we've dropped down dramatically from 27 requests down to 12 requests,  and from 646K to 5. Huge savings in about five minutes worth of work. So I think that that's  really powerful and something that you should definitely be looking at.  About 80% of the problems on the Internet are due to the network. You'll also notice that I still  have a lot of images. Those are all the little team images that show up on the top of the baseball  card. I could sprite those up and reduce by a factor again, but I think you guys get the point.  So let's go ahead and move on. So we can do less, right? Fewer HTTP requests and less bytes on the  wire, but there's other strategies that we can use to help out on the network. The first of which  is to procrastinate, right? If we know somebody doesn't need something right away on our page,  we can give it to them later. And so there's a couple of ways that we can do this. First of all,  all browsers support this script async attribute. If you've done any performance optimization,  you know the best practice is to put all of your script tags as low in the page as possible.  You want to have it before the end body tag. The reason for that is because the way that a browser  parser works is when it hits a script tag, it stops everything. It stops painting the page to the  screen, and it goes off and it downloads that script file. So you have to wait for the script to  download. Once it's downloaded, it executes the entire script file. So you have to wait for that  to execute. And after all that's done, it will continue to paint the page. So if you push them  down to the bottom, most of the page should be painted before we have to run into that whole  bottleneck. Sometimes we don't have that luxury. Sometimes we need to put like a Facebook like  page somewhere on our page for our customers. And so you put a script tag somewhere in the middle.  Well, Facebook and everybody else has agreed to follow this async attribute. And what ends up  happening here is there's actually two parsers in all the browsers. There's a pre-parcer. The  pre-parcer runs through the DOM and all it's doing is looking for things that need to be downloaded.  When it sees this script tag, it will say, hey, you know what, this is async. I'm going to mark  this to be downloaded at the end when everything else is downloaded. So it doesn't get downloaded  in order. So it procrastinates and says, we'll download this later because it's not 100% essential  if they get it a second or two later after the content. We're fine with that. The other thing  that you can do, and right now this only works in IE11, is you can add this lazy load equals one  attribute on images. It works the exact same way as as a sync. Basically the pre-parcer finds that  and marks that as a lower priority resource to be downloaded. Funnily enough, IE is the first  implement. It's a part of a specification that the W3C is working on right now called the resource  prioritization specification. And eventually every element that downloads an asset, you'll be able  to put lazy load on. But as of right now, it's only images and only in IE. So those are both very  powerful to procrastinate. The opposite of that is you can anticipate. And this is really interesting  because this works in most of the browsers. As you can see, except for the last one, we have,  I'll talk about that in a second. So you can put these meta tags, these link tags in your pages,  and as we move down the list, they get more and more powerful, but they will use up more and more  resources from your user. So the first one is this DNS prefetch and you point to a domain name.  And when the browser parser sees that, you'll probably put it in the head near the very top,  it resolves the DNS of that domain. So later on, when you request an asset from that domain,  you won't need to resolve it at that point. So a good example is if you're putting your images  on a CDN and all of those images are at cdn.mywebsite.com, you need to resolve that host name by putting  this at the top of your page, you'll save yourself about 200 milliseconds off the DNS  resolution. So that's cool. Small savings, but pretty cheap. You can do a couple of these,  it's not really going to affect your user. The next one down, slightly more powerful,  is prefetch. Prefetch, you point to an actual asset. So you can go off and download something  that you know your user is going to need later. So maybe there's an image that you're going to  be showing when they click on a button that you anticipate them to click. You can go and download  that so that way next time or when they click the button that's going to show the image,  it's already been downloaded. You don't have to wait for that download. Now here's the trick.  All that this prefetch does is make an HTTP request. So if I download some image,  and there's no caching on that image, when the user clicks the button to make the image display,  the browser's just going to request the image again. The browser doesn't know that it already  prefetched something. So if you're using prefetch, make sure you cache whatever asset you're using.  Otherwise, you're going to cost your user twice, which is not what you want to be doing.  The last one is really cool, but it's not supported in Firefox.  It's called pre-render. Pre-render, you can point at any page. And what essentially happens,  this is the way that I like to think of it, is the browser goes off and downloads that page,  and all of the assets associated with that page, and executes all the JavaScript running on that  page, and it does all of this in a hidden tab. And when you click on the link on your page that  would lead to the pre-rendered page, the browser takes the hidden tab and takes the tab the user's  on, and it swaps them. And it's instant. So let me show you that.  I'll close the tools here. On the home page, if I view source,  you can see that I have this pre-render in here, and I'm pointing to the champions page,  because I find that most people come to my website because they want to see who won  mascot mania, as you would. So, but because I have this pre-render, let me go ahead and press  shift, escape, and I just opened up task manager for Chrome. Very similar to task manager for  Windows, but this is showing you all the Chrome processes and what they're doing.  You'll see that all of my tabs are up here. I have the tiny PNG tab. If I go and close tiny PNG,  that tab disappears. Let me dock these things like this so you can see a little bit better.  So tiny PNG is gone. You can see my mascots. You can see octane. This tab will look at a little  bit later. But you'll also see, oh, you know what? I've probably been on this for too long. There you  go. You'll also see I have this tab called pre-render. Mascots of minor league baseball,  and you can see it's going to champions. So there is actually a hidden tab there. Now,  we made a whole bunch of performance optimizations. Let me undo them.  So I'm going to come in here. I'm going to delete the new files that we created.  I'm going to revert the files that we changed.  I'm going to reload everything in Visual Studio. I'll rebuild.  And I will reload this page. All right. So I think we'll all agree now that we're back to the old  crummy version of the site. But because of the pre-render, oh, man, it's like puberty all over  again. But because of the pre-render, when I click on see all winners, look how instant that was.  I have all these big images showing up, and it was instant. You'll also notice the pre-render tab  disappeared. Chrome shows you what's happening with pre-rendering in another way. Let me go back  to the home page. Because if you go to Chrome slash network internal slash pre-render, you can see  here that it's telling me here's all the pages that it currently has pre-rendered. Here's all the  hidden tabs that it has right now. You can see that the champions has been pre-rendered for 10,  15 seconds now. And if I come on here and I click on that link, you'll see, boom, that pre-render  dropped down to here and says that it was used. And you can see that it's been canceled before.  You'll also notice that I thought all of these other things that were pre-rendered when I did a  search on Google. Because Google uses this trick to pre-render the first link on the search results.  That's why the first link is always so much faster than everything else. Because they're  actually rendering it before you click on it. So that is anticipating what the user is doing.  So quick summary of what we can do in the network. We can do less. Fewer requests,  smaller payloads. Showed you a bunch of ways that you can do that really quickly. We want to  procrastinate and anticipate. Another way that people procrastinate is they use JavaScript to  only download images once it's come into the viewport. I think a lazy load thing is going to  solve that problem. You don't need to do it anymore. But if you have a ton of images,  you can think about using scripting to do something like that. Okay, cool. So when we're  talking about performance with the web app, we have to think about two different modes. We have  to think about the user getting the app. And we have to think about the user using the app.  So all of the network so far that we've talked about is part of the user getting the app. But  the user can't even begin to download things until they've received the, what I like to think of as  the main manifest of the page, the main HTML page, the thing that you're creating an ASP.net or a  PHP or whatever your web framework of choice is. And so that's the server. Now there are tools to  help on the server to figure out what's taking a long time to render the page and whatnot. But  you're not going to use developer tools that are built into a browser for that. Instead, you'll use  a CPU profiler. Now, I mentioned that I was sponsored by Redgate and they happen to make  a performance profiler. I'm not a sales guy. So we're not going to dig into this too deeply.  But I'm just going to show you this quick little video so you can basically see the way that these  things work. What we care about on the server is how long things take on the CPU. So in Ants  performance profiler, you can profile all different kinds of applications, but you'll see there's a  large section for doing things around web development and ASP.net. Works with any web server,  IIS express, IIS regular, even Cassini. And once you start profiling a site, you can browse around  the site and you can see that we keep track for you. That's the red humps, how long or how busy the  CPU is at any given time. And at some point, you can stop it, which I'll pause right here. You can  select a period of time over the red graph and it will show you the call stack at that moment  and which methods are taking the longest. Descending granularity all over again. You find the slowest  method, you go in and you tweak that. Now, here's the problem with.NET and C-Sharp.  We could spend the entire day talking about ways to improve its performance. I'm not going to bore  you all with that. Instead, I'll give you some quick highlights of things that I've seen and  learned by talking to the engineers on the profiler team. Four web devs. The number one expense that  you have on the server period is leaving the process. Leaving the process is doing a database  query, doing some web service call, things like that. Move everything in process as possible.  That's why caching is important. Data caching. Because you're moving things that were once out of  process into memory, something that you can access quick. So cache library, stay local. Iterate less.  A lot of times, you'll find when you look at a CPU profiler like ours, that the method that took  the longest to execute took that long because it was hit many times. It was run many times on a  given HTTP request. That's usually because of looping. So let's say that we have some method  that gets called 100 times. Sure, we can go in and try to optimize that method and we might  shave off 2 milliseconds. But it would be better to find a way to drop the iteration from 100 to 80.  So if we can filter the collections and only loop over the things that we absolutely need,  that's the best way of going about it. The last thing that I see time and time again  is people loading data all in one shot. You need some XML configuration file or you're  reading some feed that comes in from one of your vendors or something like that.  And that just kills your memory. So stream data in any time that you can. There's a couple other  little miscellaneous things. The one that shocked me the most, I didn't know this until I worked at  Redgate and I was talking to the profiler guys who really dig into the CLR and performance  optimizations around that, is string concatenation. Here's the rule of thumb. If you're concatenating  three strings, you're probably okay. But even then, you might want to use a string builder.  Any more than that, string builder. Better performance every single time.  I'm lazy. I can concatenate 15 things together because I don't want to, like,  knew up a string builder. But that's what you want to do. There's lots and lots of more things  that you can do in.NET to make things better, right? We have async now that's really powerful.  You should look into that. There's ways to improve your queries that you're making from the server.  These slides are all already available and you can follow that link to find lots and lots more  tips and tricks. Cool. So, server rendered, set down the manifest, everything downloaded on  network. We just covered everything that we needed to do really quickly to get the application to  the user. Now, let's talk about how the user uses the application. And so, for that, we have compute.  So, I just showed you a quick little video of a server-side profiler. I didn't drill into it  too much because there's also client-side profilers. And so, once again, those are built into your  F12 tools and they work... Come on, F12 tools. Ah, function lock. There we go. And they work in  very much the same way as the server-side tools. This is built into IE and Chrome for sure. I'm  pretty sure Firefox has a profiler as well. Now, my little mask, it's a minor league baseball  application. It's not very JavaScript heavy. So, it's kind of a boring thing to profile. So,  instead, what I'm going to use is this thing called Octane, which is a JavaScript benchmarking  tool that the Chrome team uses to see how Chrome is performing from build to build. So, all I'm  going to do is I come in here to profiles. I'm going to collect a JavaScript CPU profile and I'll  hit start. Once that starts, I'm going to run Octane. So, Octane runs through a bunch of separate  benchmarks. You'll see a couple of interesting ones in there. They run a Game Boy emulator,  which I think is kind of fun. They render a PDF file in JavaScript, which is cool. And that's the  way that Firefox does it natively nowadays. They actually compile TypeScript because that's a very  large JavaScript application and they want to see how they can compile TypeScript in the browser.  So, it runs through all of these different benchmarks. It gives individual scores for each  of them. And eventually, I will have a score overall for how my machine and the browser is  doing against all of these crazy pieces of JavaScript. So, one second, we should be good to  go. Great. And I will stop the profiler now. And so, what I get is this down here. So, the first  thing I'm going to look at is this breakdown. This is the breakdown that's very similar to what  you might have seen when I paused the video. It's a breakdown of all the different methods and how  long those methods took to execute. That's quite handy. Not really for me because I didn't write  this code and I don't know what these methods mean. But if I wanted to focus in on one of them,  I could select it and press this little eyeball and that's going to focus in on that part of the  call stack and then I can drill down into there. And so, that's kind of cool. I think Internet  Explorer actually has a better experience around this right now. I know. I've not said that in  many, many years. Internet Explorer has a better experience. But one of the things that they do  is they identify libraries like jQuery automatically and allow you to also add additional libraries.  So, they filter out library code because I'll see a whole bunch of jQuery stuff in one of my  websites and that doesn't really help because I'm not going to go and perform as optimized jQuery.  I'm just going to let them handle that. But filtering all that noise out is great.  One of the cool things that Chrome does, though, is it has this flame chart. A flame chart across  the x-axis is showing time and across the y-axis is showing the height of the call stack. And so,  what that allows you to do is from a really high level start to identify patterns in your code.  So, what I can see here is if I zoom in on this point here, I see all these little tiny dips.  Let me zoom in here. I see these little dips, right, where we kind of go down to nothing.  And you can see them right here, these little pink marks there. I've been able to deduce,  figuring that out, that that was a little gap in time between each one of the separate  benchmarks ran. So, I can see that pattern and focus in on the second one if I wanted to.  I also see that this pattern here, it's very sawtooth. You can kind of see that, like, these  stripes, they look like barcodes down here at the bottom, but they're kind of doing the same thing  over and over and over again. Well, why is that? Oh, because we're looping. And that benchmark,  they're looping over something. So, you can get a really high picture, well, my brain is fried.  We can get a really high-level picture of what's happening in the code, and then you can drill  down, you can select a subset of it and fix that specific method. So, basically, the things that  you want to do with JavaScript, like my high-level tips, are to manage your scope well. So, you may  ever heard people tell you, make sure you're using the var keyword, because if you don't use the var  keyword, the variable goes to the global scope. Well, anytime you access a variable in JavaScript,  it tries to find that variable in the local scope first. We'll scan through that whole table, if  you will, of local variables. And if it doesn't find it, it will move up to the global scope. So,  you can save yourself a lot of extra scanning if you make sure you're using var appropriately  and putting things in the closest scope. Closures and the width statement, never use the width  statement. All it's doing is adding an additional scope. It's three layers of scanning now. It  slows everything down. Enclosures kind of do the same thing. Those are really hard to avoid,  but be careful what you're enclosing. It encloses little as possible to manage your scopes.  Next, this is straight from Crockford's book, Avoid 4-In Loops. 4-In is horrible. It has really  bad performance across all the browsers. The vendors know it. They're in ECMAScript 6, which is  coming soon. They'll have a replacement for it. Don't do it. But if you do some research into how  to improve JavaScript performance, the biggest news that you're going to hear is, avoid the DOM.  It's like being shouted from up high. Avoid the DOM, which I think is stupid. Because the whole  reason I'm writing JavaScript in the browser to begin with is because I need to manipulate the DOM.  I'm reading some values that a user has given me. I'm changing something on the screen. Avoiding  the DOM doesn't really help me. So, it's a nice thing to say. But the reality is what we need to  do is we need to understand what to do with the DOM so we can use it in a performant manner.  Now, to do that, we need to talk about frame rates.  That's because when we touch the DOM, we create a ripple effect. A ripple effect that we don't even  see programmatically and until recently haven't had the tools to measure. When I touch the DOM,  it means that something needs to change on the screen most of the time. And that means that the  browser's rendering engine, which works 100% declaratively, needs to repaint something on the  screen. And that takes time. So, I'm not much of a video game player. I like my Super Mario  brothers every now and then. But that's about it. But I do know that PC gamers are really big on  their frame rate. That's an important thing, is frame rate. And when any time we're talking about  rendering and how fast something can render, we're thinking about frame rate. And the frame rate  that we want is 60 hertz. 60 hertz is what almost every monitor runs at these days. It's  basically what the human eye can perceive. What that means is that my screen right now is refreshing  itself. It's repainting every pixel 60 times a second. So, one second is 1,000 milliseconds.  And we're doing a refresh every 60. So, take our one second. Let's divide it by 60. And that means  that we have about 16 milliseconds in between every time a page or every time the screen  refreshes. That's about how much time that we have to make a change. So, Chrome allows us to  measure this. I'm going to come back over to our application. And I'm going to open up this timeline.  I'm going to do something really simple. I'm just going to press record. And I'm going to scroll.  Okay. That's enough data for me to sample. That's fine. So, I clicked on this events mode. And what  it allows you to see is all of the yellow is when some event happened. Blue is network time. You can  see that anything in purple is layout green is paint. This is kind of a hard way to see it. I'm  going to switch over to the frames view. And you can see each one of those stacks is a frame being  painted and how long it took. And you'll see I have these two benchmark lines here. I have the 30  frames per second and the 60 frames per second. I just told you that 60 frames per second is what  the human eye can perceive. And you'll notice that on my website, I'm going over 60 frames per second  many a times. And oftentimes, I'm shooting way, way below 30 frames a second. This creates a  phenomenon known as jank. You've seen jank if you ever scroll to website and it's really sluggish to  scroll. The browser is working too hard to paint. You'll especially notice it on a touch device.  Jank shows up on a touch device when you try to scroll or you move the page and it doesn't stick  right on your finger. It's like you move your finger to the top and then the page follows you,  right? And your finger is always just a little bit ahead. That's jank and it's bad and it's because  you're going at less than 60 frames a second. So let's dig into ways that we can fix this.  So a quick review of what we just saw on that timeline. There's a bunch of different colored  bars. They're all stacked up and they mean different things. So the blue is network activity. That's  when we're waiting on downloading something. Yellow is JavaScript executing ours or library.  Purple is recalculating styles and layout. That's the browser creating the geometry of the page,  making all of the squares that you need to lay everything out. It creates the skeleton.  And then green is the painting when it paints the flesh over the top of the skeleton  once it's calculated the geometry. You might have also seen some gray or clear stuff. That's stuff  that Chrome for whatever reason isn't able to measure or just some idle time. We're not going  to worry about those for right now because we're going to focus on the big picture. So here's our  timeline. Every 16 milliseconds, the rendering engine or the graphics card is going to repaint.  And the problem is with JavaScript in events, we do everything on an event, somebody might  click a button and they might click a button right at this moment here with two milliseconds left  and you're trying to make a change and you expect it to be painted two milliseconds later.  You're not giving yourself enough time. By the time you do your JavaScript and all the rendering,  you're not going to get it done in two milliseconds. So what we can do is we can sheet a little bit  and we can use a method called request animation frame. It's supported by all the browsers.  Request animation frame takes in a callback function and it runs every time the rendering  engine just finished painting a frame. Which means that during request animation frame,  you basically get almost the full 16 milliseconds. That is how to give yourself the most amount of  time. You don't want to be running at some event that happens randomly in the middle. You want  to be trying to run at the request animation frame. And in that callback, you run your JavaScript  which touches the DOM which will affect paint and have that ripple effect. And if you can get  all of that done, including the ripple effect within 16 milliseconds, you will have a screaming,  fast sight, easy to use, no jank, 60 frames per second. And so every 16 milliseconds,  another request animation frame method gets called and you go on and on and on. But you can see  here this third time, my JavaScript took too long to execute. And so when it finished, there wasn't  enough time for the rendering engine to paint. And we went over that 16 millisecond boundary.  Which means that the browser drops the entire frame. It's just going to wait until the next  interval to pick up again and try to paint something. So that's what's happening on my website  right now, which is why we're not hitting the 60 frames per second. So with request animation frame,  right now, you'll see that as I scroll this page, I have this little badge up here that shows you  the icon of the league that we're showing. So when I scroll over to Eastern, it changes to Eastern.  And when the Florida State League gets to the top of the page, we switch over to the Florida State  League. That's the only real JavaScript that I have on the page. It's happening on scroll  handler. You can kind of imagine what that code looks like. It's quite simple. Here's how we  could change the code to leverage request animation frame. So I'll capture the scroll position of  the window. That's what's happening on that first line there. And then I'll check to see if I've  already scheduled the change to happen of the image in that if scheduled animation frame. And  I'll return if I have otherwise, I will set up my request animation frame to swap out the league  badge. So what this does is a couple of things. If the user fires the scroll event more than one  or two times in that 16 milliseconds, right now, I'm trying to do that work as much as I can on  the browser. It doesn't matter. If I could get it to happen two or three times within those 16  millisecond boundaries, they would only see one update because we're going faster than the screen.  And usually we can't. So what we can do is we just slow everything down and we only update when the  page is getting refreshed. We're doing a whole lot less work because we're not just stacking up all  this work to do every single time they scroll. So this would be a great change for me to make to  this website and use request animation frame. Another pattern that we see that's really bad  with JavaScript and the DOM is an anti-pattern known as layout thrashing. So this code seems  innocuous enough. We may be written something like this. So I'm looping over a bunch of paragraphs.  I'm taking each one of them. I find the width of some div and I move the paragraph over to the  left so it's the same. So it's right next to the size of the div. No big deal. I just loop over them.  But here's what happens. The browser is honest aid. It will never lie to you. The browser does not lie.  So the first time we come through this loop, we grab the paragraph off the collection,  we get the width of this div, and then we change the left style of that paragraph.  When we run this line of code, the browser knows that there's been a change to the DOM  and it marks the entire DOM as dirty. No big deal. It's dirty. Until we come back around the loop  and we get back to reading this offset width. Like I told you, the browser will not lie to you.  So you're asking for the width of a div now that theoretically could have been changed  because you moved that paragraph in the iteration before. So the browser stops on this line and  says, you know what? They changed something. Let me recalculate the entire geometry of the page.  Let me do all of that work so I can figure out what the real width of this div is and that  returns it to you. And you do the exact same problem over and over and over again. Where you  change one value, recalculate the layout. Change the value, recalculate the layout,  and you're doing it over and over. So the easy fix here is to do all of your reads  before all of your writes. So in this case, I read that offset width once. I do a bunch of  writes. So the DOM gets marked as dirty here. But on the next iteration, I'm not reading anything.  So it will let me continue to write, write, write, write to the dirty DOM. A dirty DOM is a dirty DOM.  And eventually, at some point, I will do a read or the screen will update and it will calculate  it what it needs to. But I'm not forcing it to calculate the layout anymore. So hopefully,  that all makes sense. So those are some things that we can do with JavaScript. There's actually  an interesting library I would recommend toying around with it called Fast DOM, which uses  RequestAnimationFrame to allow you to access the DOM in the way that you want to, like this  anti-pattern here. But what it's really doing in the background is it's putting all of the  reads and writes into RequestAnimationFrames and running them in the right order. So I feel like  I would mess myself up doing that. But big sites use that library to avoid layout thrashing. It  forces you to write in a way that you can't get into layout thrashing. Okay, cool. So that's the  JavaScript. And those are things that we can do to make the JavaScript faster. But like I said,  we're still going to have a ripple effect. And at some point, we are going to want to paint.  So let's look into what we can do with that. So on this page, I'm going to go ahead and I'm going  to enable this thing called Show Paint Rectangles. Now, what Show Paint Rectangles does is you can  probably see on the screen that there's a green tint that's going around that logo every time  I scroll. You'll also see there's a green tint going around the scroll bar. What Show Paint  Rectangles is doing is it's literally putting in green wherever the browser just painted.  So on every little scroll, I'm just continually pressing the down key on my keyboard, every little  scroll you're seeing that I'm repainting that logo, which is weird because it's not changing.  And yet it continues to get painted over and over and over again. That's because it is an absolute  position. And the background behind it is subtly changing. It's a transparent PNG and the gradient  texture behind that image is subtly changing as every time I go. And browsers render in tiles.  So if I turn on this composited layer borders, you can see these blue lines. These are the  tiles in which the GPU renders the page. And so any time one of those tiles gets marked as dirty,  it needs to repaint that area. So what can we do to fix this? We don't really want to be painting  that thing over and over again. So I am going to select this image. And you can see I have it here  in the DOM Explorer. And there's a class on it called League Badge. So we're going to do a little  trick here. I'm going to say dash webkit dash transform colon translate Z, which is a function.  I'll pass in zero to it. Now, you might have noticed instantly that I now have an orange square  around that league icon. So what I've just done is I've moved that image into a separate layer.  Now, these aren't layers like you would think of layers when you think of a Z index.  These are layers that the GPU uses to render and do compositing, which essentially means that this  California League logo is almost, you can almost think of it, it's on a page of its own now,  as far as the GPU is concerned. The GPU will render the entire background, everything but that  orange square and the orange square separate. And then when it pops up on the screen, it just puts  them one in front of the other. So that's pretty simple. And what that means is now, when I scroll,  because it's in a separate layer, I changed to Carolina, so it went green. But you can see the  entire time I'm on Carolina, I'm not getting green. I'm not repainting that thing because it's  separate. And it's not changing because it's separate. When I get to Eastern pretty soon,  and it's going to change, you should see it go green. And boom, it goes green, but now it's not.  So I'm doing a lot more scrolling with a lot less painting, which is going to help my frame rate.  So that's a pretty cool little trick. Now, you might look at me and say, well, how did you know  to put WebKit translate Z on there, Nick? Well, the reality is it's a hack. This is me  knowing about an implementation of the way that Chrome's rendering engine works. And I know that  anything that has a translate Z on it will automatically get promoted to its own layer on the GPU.  And the Chrome team actually recommends that you do this from time to time, but they kind of do it  with an asterisk saying, we know that that's kind of dirty because you're leveraging an  implementation detail. There are other things that promote layers, and the Firefox rendering  engine works the same way. So video elements and canvas elements automatically get promoted,  things that have opacity animations, composited plugins like flash and silver late, they all  run in their own separate GPU layer. There's a few other things that are there. There's a spec  that's currently in debate right now with the W3C called will change. So the reason people don't  like the translate Z is because translate Z actually has a function, and we're not using it for that  function, we're using it to screw around with the GPU. The will change spec would allow me to,  instead of saying translate Z to say will change one, to which I'm telling the browser that this  thing is going to constantly be changing, so I want you to pull it out and render it in its own  layer. And the CSS community is a little bit of a kerfuffle about whether or not this is a good  idea right now, but keep an eye out for the will change spec, and when the will change spec is in  place, this will kind of work everywhere as soon as the browsers adopt it, but until then, WebKit  translate transforms Z. Great. So we made that change. Let's see what else we can do. This painting  thing is really cool. I like seeing the green. I like seeing what's being painted. So there's  another mode that I can do. I'm going to enable continuous page repainting. When I do that,  you notice my entire page goes green and stays green. That's because when I'm in this mode,  I am forcing Chrome to repaint the page over and over and over and over again as fast as it  possibly can. While it's doing that, you can see, wow. You can see that I'm getting page paint times  up here in the upper right-hand corner. You can see this little graph. And my page paint times are  sitting at somewhere around just under 40 milliseconds a refresh. 40 milliseconds every time I refresh  my page, and I'm not even doing anything. No wonder why I'm not getting in with 60 within 16  milliseconds. I'm not getting that 60 frames per second. I'm taking too long. I think it's  number to be somewhere south of 16. That's our magic number. So what we can do while we're in  this mode, we can leave this little widget up here running and try to figure out what is actually  taking up so much cycles. So I'm going to use this DOM Explorer again. I'm going to select this  element here. And this works at any time in Chrome. If you press H on an element, it hides the element.  So I got rid of that element, and we can actually see that it looks like things went down a little  bit. I was taking a little less time to paint. I'm just going to continue to hide things on the page.  You can see my numbers are continuing to drop as I hide things. Of course, the page is getting  simpler and simpler. Now I'm right about the number I need it to be. I probably need to be a  little bit less than that. So this is really cool. You can just kind of use the H key to hide things  and basically bisect your DOM to figure out where all the painting time is coming from. So I mean,  I can tell that these baseball cards are really causing some performance issues. So now let me  drill into those and see what's causing the problem. So we'll look at Seawolf here.  He's not that complicated. You can see there's the div with the background image. That's the  actual picture. Here's the image that shows the Seawolf, the team logo. So what can we do here?  I'm looking at the different styles. Well, I see there's a box shadow on the style. Let me turn  that off and see what happens. So we're at 38 now. I turn that off. Holy moly. I almost cussed  because that was amazing. I turned off the box shadow and I dropped down to 10 milliseconds  to repaint the page. Well, cool. Let's see what else I can cut out. Let me turn off the border radius.  I got another millisecond. Not that big of a deal. Let's turn on the box shadow again.  Went up a little bit. Not that big of a deal. Still under my target number. Let me see if I  turn them both back on again. Wow. So it seems like I can have one or the other and I dropped the  performance big time. But these two properties in combination completely kill the rendering. So  I'm going to pick one to turn off. Actually, I'll turn them both off. And so you can see that we've  dropped down to eight and we've made this fix over here with the separate layer. I'm going to go to  the timeline again. We'll clear or record. And you can see now we are getting almost all of our  frames at or below 60 frames a second. We're getting a couple that go over and then we can dive  into farther and figure out why that's happening. So we just dropped from somewhere north of 30  down, well, south of 30 to north of 60 with a couple of small changes. Now you might be saying  to me, but Nick, my designer really wants rounded corners and drop shadows and you just cut that  out and that changes the design of the site. That's fine. What I would recommend that you do in this  case is preprocess your images. Go into Photoshop or whatever and add the rounded corners and drop  shadows so it's still just an image that the browser is displaying. But you're not using CSS to  add those effects and you're going to get a big performance boost and you'll be closer to jank  free. Now I knew to go and look at the way that things were painted because I knew to uncheck  the border box and the drop shadow because I know which properties affect which part of the  rendering engine. So here's a handy little cheat sheet. If you see recalculate style,  that usually has to do with your CSS selectors. Now there's a lot of articles out there on the  internet that you can find about optimizing CSS selectors and the Chrome team used to have a  CSS selector profiler that would allow you to see which selector was taking long and going  optimize them. They feel like at this point that problem has been solved. They've even removed  the profiler. You can still optimize, but it's a super micro optimization to worry about your  selector at this point. Anything that manipulates the box model width, height, left, top, that's  going to affect the layout. So if you're seeing a lot of purple layouts, go and focus on the box  model properties. The rest of the properties are like color and background color and blah,  that's all going to be paint time. And if you see green ones that say composite, that's usually  things that are happening on the GPU. And things that happen on the GPU are good in general. It's  very fast. It's very efficient at painting. And so there's four things that are considered to be  jank free. That's scaling, rotating, moving, and fading the opacity of something. Those are all  using those functions like the translate that I just showed, translate scale, rotate that are  available. They're all hardware accelerated. So if your designer wants you to do some animation or  some effect on an image, try to keep it to one of those four and you'll have a pretty good performance.  So closing up, there's a bunch of resources available, lots of interesting things. I didn't  cover the psychology of performance, which is a very interesting read for when you can't fix  performance. How do you trick somebody into thinking that it's still fast? So I recommend that book.  There's a whole bunch of other books up here. Anything by Steve Solders is amazing.  Jankfree.org is really good if you're into the rendering stuff in HTML5 rocks.  All of those resources are available from this QR code and link. If you have any questions,  what time do we have? Are we at 12.40? Is it our 11.40? Yeah? 11.20. Oh, so we're five minutes  over. So I'm going to be kind to the next speaker. I'll take questions out in the hall or you can  tweet me at nickmd23. I really appreciate everybody showing up. Please leave feedback.  Please visit the redgate booth if you appreciated this talk and say hi to those guys. Thank you very much.
Nik_Molnar_-_Full_Stack_Web_Performance_0_3011.png Nik_Molnar_-_Full_Stack_Web_Performance_3012_15023.png Nik_Molnar_-_Full_Stack_Web_Performance_15024_15875.png Nik_Molnar_-_Full_Stack_Web_Performance_15876_16055.png Nik_Molnar_-_Full_Stack_Web_Performance_16056_16127.png Nik_Molnar_-_Full_Stack_Web_Performance_16128_16223.png Nik_Molnar_-_Full_Stack_Web_Performance_16224_16307.png Nik_Molnar_-_Full_Stack_Web_Performance_16308_16703.png Nik_Molnar_-_Full_Stack_Web_Performance_16704_16799.png Nik_Molnar_-_Full_Stack_Web_Performance_16800_17015.png Nik_Molnar_-_Full_Stack_Web_Performance_17016_17063.png Nik_Molnar_-_Full_Stack_Web_Performance_17064_17111.png Nik_Molnar_-_Full_Stack_Web_Performance_17112_17291.png Nik_Molnar_-_Full_Stack_Web_Performance_17292_17363.png Nik_Molnar_-_Full_Stack_Web_Performance_17364_18935.png Nik_Molnar_-_Full_Stack_Web_Performance_18936_19787.png Nik_Molnar_-_Full_Stack_Web_Performance_19788_23027.png Nik_Molnar_-_Full_Stack_Web_Performance_23028_32711.png Nik_Molnar_-_Full_Stack_Web_Performance_32712_32963.png Nik_Molnar_-_Full_Stack_Web_Performance_32964_33011.png Nik_Molnar_-_Full_Stack_Web_Performance_33012_33695.png Nik_Molnar_-_Full_Stack_Web_Performance_33696_33875.png Nik_Molnar_-_Full_Stack_Web_Performance_33876_34655.png Nik_Molnar_-_Full_Stack_Web_Performance_34656_34727.png Nik_Molnar_-_Full_Stack_Web_Performance_34728_38243.png Nik_Molnar_-_Full_Stack_Web_Performance_38244_38987.png Nik_Molnar_-_Full_Stack_Web_Performance_38988_39083.png Nik_Molnar_-_Full_Stack_Web_Performance_39084_46403.png Nik_Molnar_-_Full_Stack_Web_Performance_46404_46535.png Nik_Molnar_-_Full_Stack_Web_Performance_46536_46955.png Nik_Molnar_-_Full_Stack_Web_Performance_46956_47315.png Nik_Molnar_-_Full_Stack_Web_Performance_47316_47423.png Nik_Molnar_-_Full_Stack_Web_Performance_47424_49859.png Nik_Molnar_-_Full_Stack_Web_Performance_49860_50315.png Nik_Molnar_-_Full_Stack_Web_Performance_50316_50387.png Nik_Molnar_-_Full_Stack_Web_Performance_50388_50447.png Nik_Molnar_-_Full_Stack_Web_Performance_50448_53903.png Nik_Molnar_-_Full_Stack_Web_Performance_53904_57875.png Nik_Molnar_-_Full_Stack_Web_Performance_57876_58223.png Nik_Molnar_-_Full_Stack_Web_Performance_58224_58523.png Nik_Molnar_-_Full_Stack_Web_Performance_58524_58667.png Nik_Molnar_-_Full_Stack_Web_Performance_58668_59231.png Nik_Molnar_-_Full_Stack_Web_Performance_59232_62291.png Nik_Molnar_-_Full_Stack_Web_Performance_62292_62435.png Nik_Molnar_-_Full_Stack_Web_Performance_62436_63539.png Nik_Molnar_-_Full_Stack_Web_Performance_63540_63587.png Nik_Molnar_-_Full_Stack_Web_Performance_63588_68483.png Nik_Molnar_-_Full_Stack_Web_Performance_68484_68555.png Nik_Molnar_-_Full_Stack_Web_Performance_68556_68615.png Nik_Molnar_-_Full_Stack_Web_Performance_68616_69551.png Nik_Molnar_-_Full_Stack_Web_Performance_69552_70787.png Nik_Molnar_-_Full_Stack_Web_Performance_70788_70871.png Nik_Molnar_-_Full_Stack_Web_Performance_70872_75059.png Nik_Molnar_-_Full_Stack_Web_Performance_75060_75335.png Nik_Molnar_-_Full_Stack_Web_Performance_75336_75395.png Nik_Molnar_-_Full_Stack_Web_Performance_75396_75671.png Nik_Molnar_-_Full_Stack_Web_Performance_75672_81659.png Nik_Molnar_-_Full_Stack_Web_Performance_81660_81755.png Nik_Molnar_-_Full_Stack_Web_Performance_81756_82367.png Nik_Molnar_-_Full_Stack_Web_Performance_82368_84047.png Nik_Molnar_-_Full_Stack_Web_Performance_84048_84143.png Nik_Molnar_-_Full_Stack_Web_Performance_84144_86111.png Nik_Molnar_-_Full_Stack_Web_Performance_86112_86219.png Nik_Molnar_-_Full_Stack_Web_Performance_86220_86399.png Nik_Molnar_-_Full_Stack_Web_Performance_86400_88763.png Nik_Molnar_-_Full_Stack_Web_Performance_88764_89663.png Nik_Molnar_-_Full_Stack_Web_Performance_89664_89735.png Nik_Molnar_-_Full_Stack_Web_Performance_89736_94691.png Nik_Molnar_-_Full_Stack_Web_Performance_94692_94739.png Nik_Molnar_-_Full_Stack_Web_Performance_94740_97739.png Nik_Molnar_-_Full_Stack_Web_Performance_97740_98531.png Nik_Molnar_-_Full_Stack_Web_Performance_98532_98822.png