 What you're seeing here is, you know, we're taking two snapshots, right? So, we have a snapshot one and a snapshot two. And then in DevTools, you can actually say, show me the difference between these two snapshots. So, instead of navigating the entire whatever, you know, 50 megabyte or 100 megabyte stack that you have, you can actually just say, here are my snapshots, show me the difference. And what you're seeing here is, it's highlighting the difference for me, which is to say, hey, we created the difference between these two snapshots was that we created an extra 3,000 item objects. 2,000 of them were deleted. But there is 1,000 floating around, right? Which may or may not be a problem, right? This is where your application knowledge needs to come in because you may have allocated objects that you need for layer phase. But in this specific case, this is actually an example of a memory leak, where if you look at the code, we're literally allocated 3,000 objects, but we only deleted 2,000. And this is the tool that will allow you to spot this and say, okay, well, there's a delta of 1,000 objects. So, maybe that's something you should investigate, right? And here's the allocated size and all the rest. You can use the same strategy for actually identifying DOM leaks. So DOM leaks is an example of where you're creating element objects, right? So maybe you're creating a list of, I don't know, items, and then you forget to inject some of them into the DOM or you kind of left them floating around. Those references will stick around and they'll occupy memory. So leaks happened in browsers, right? They happened in your application code. If you look at even applications like Gmail, something we fight with all the time, like Gmail is a very complicated application. We've had users a year back that had tabs that were over 4 gigabytes in size, right? Because we found some memory leaks. Surprise. We use the heap snapshot tools and other tools to track them down and you can see on our internal dashboards, we track memory usage, you can see the release going out and just like the graph going like this, right? Because we were not cleaning up some of the messages. Like you would open a message, you would hit back and we would not delete the DOM tree, it would just kind of float around and there was still a reference to it and it was not being garbage collected. So, you know, things like that happen. They happen at scale and they can definitely affect your users. So, John, who was the speaker on that previous video that I showed, actually gave a fantastic presentation at I.O. on how does the memory model work in a browser? Like what are memory leaks in JavaScript to begin with? And they actually talk about the Gmail use case and how they track down memory leaks and other things. So definitely recommend checking out this presentation. Okay. So, we've constructed the DOM, constructed the CSS DOM, we have the render tree. Then there was the next step, which is the layout. Like what is this layout thing all about? And how can we optimize it for it? So, a very simple way to think about layout is we need to compute the width and height of each element. Like we literally need to lay out all the DOM on the page, right? So you have your float left, float right, absolute positioning and all the rest. Where does it actually all fit on the page? This is the phase where we determine the specific width and height. And the reason this actually gets quite complicated is because there's also parent-child relationships, right? You can have an element that is 50% wide. And inside of that element you say, well, this one's going to be 30% wide. And it's with respect to the parent element. So we need to propagate all of these calculations throughout the tree and then put it on the page, right? That is effectively the layout phase. So what I'm doing here is, you know, I'm declaring this stuff to be 50% of the actual layout viewport, which is nice and simple. And then I'm declaring the other ones to be 75%. But hey, I'm also not telling you here that, you know, on my div I have some padding, I have some margins and other things. This is all of the calculations that need to happen in the browser before we can paint something. So one fun, I guess, takeaway here is what happens if you resize, let's say we have our body element, right, which is said to be 100% width. And then I decide to resize it to be 50%. It means I need to recalculate all of the width and height and position elements for every single child element, right? So layout can be in fact very, very expensive because we need to propagate all of the changes throughout the DOM. So in recent, so I don't think this is actually in stable Chrome now, but in Canary, if you run Canary and you should when you're developing this stuff, we have a lot of new and nice features enabled. So if you look in your timeline, you can actually hover over the layout. So this is a specific, we're showing you an instance, like here we're performing a layout. And this specific layout for this page took 2.5 milliseconds, okay? And we had to adjust 34 nodes. Like whatever this code did, it affected 34 nodes, right? It probably touched the parent, which then kind of had to reflect back on 34 children. And you can see the total size of the tree is about 2,800 nodes in this case. So, you know, is this useful data? How do you use it? It is useful in the sense that it can help you diagnose things. Like you look at the timeline and you see like, oh, wow, layout phase took 200 milliseconds. What happened there, right? You can actually hover over this element and we will tell you how many nodes were affected and also which JavaScript code, and that's probably the most important part, or which code has triggered this update. So you can go back and say, oh, well, yeah, I updated the outermost element and that had to reflow the entire page. So what are some of the things that can trigger a layout? Adding nodes, right? So if you think about it, adding new things into the page will have to kind of shift around all the elements on the page, right? If I change the width or height, if I change the position of any given element, that may affect how the rest of the page is being laid out. So that's layout. That's the phase that happens here. And layout can be actually very, very expensive. So here is an example. And let's go. Okay. Wifi came back. Good. So very simple animation, as we'll see. I'm going to open the timeline. I'm going to start recording. I'm going to start the animation. And you can see the jank, right? So this is definitely not 60 frames per second. So we're just basically moving these balls back and forward. That's all we're doing. And the way it's implemented is, let me stop the recording here, the way it's implemented is it's literally just firing a JavaScript function, which says I'm going to grab each ball, I'm going to change your position like left offset by 10 pixels, right? And I'm just going to continue running this in a loop. So if you look at the actual recording that we did here, you can see that the layout was taking, you know, so there's many different layouts, some frames. So let me zoom in on one of these guys. There we go. So each one of these frames that we're trying to calculate here is taking over 200 milliseconds, right? So basically we're getting five frames per second. We're not getting 60 frames per second. We're getting five frames per second. And most of the time is actually being spent in this animation frame fired. So we have a specific file called source.js. And if I expand that, you can see that what's happening here is I'm just triggering like a bazillion recalculate style followed by a layout phase, right? So why does this happen? Let's go back. So this idea of a synchronous layout is very important. So recall that at the beginning I said, you know, the sequence of how all this code is being executed does not necessarily happen the way I have it on the left, which is your code followed by GCE layout and paint. In fact, that is the ideal outcome. And I say it's ideal because what it means is that you ran all of your logic and then basically we clean up a little bit afterwards. In fact, for best performance, we want to do or we want to be lazy, right? We want to defer all of our painting and rendering and layout work right until the last moment, right when we render the frame, right? That's the ideal outcome. But there are certain things that you can do to force an update. So as an example here, that's that's what a synchronous layout is. Here's, here's a snippet. Can you guys spot the problem? So it's kind of a silly snippet, but you know, for, for some nodes, right, some collection of nodes, I'm iterating over these nodes and I'm saying, okay, the position of the element, right? So I'm updating the CSS left property. The position of the element is going to be its current position plus one pixel. You know, doesn't seem too bad, except that what happens, so let's actually work through this. The first time we run this, the first iteration through this loop, we have the CSS object model, right? We query the offset left from, from the CSS. We get a value back. We update it and we set it, right? That marks the CSS as dirty, right? We haven't recalculated anything yet because ideally the browser wants to defer all of the work, right? Next time through the loop, we come around and you ask us for the offset left of the next element. At this point we say like, wait, wait, wait, hold on a second. The previous update that you've done could have changed the position of this next element, which means that we need to go back, recalculate all of the styles, basically reflow all of the things and then make sure that, you know, we give you the correct value because otherwise we would just give you garbage data back. So the first iteration of this is fine. The second iteration of this basically forces us to do the layout and this is what you're seeing here, right? You've done a CSS update or style update and then on the next iteration you're forcing us to do the layout. And that's exactly what you saw in this example. If I go back, this is why you see recalculate style layout. Basically what this tells me is that the code is queering the CSS and then it's just iterating all of these elements and it's forcing us to do all this work. Let's go back. So, you know, can you avoid this? Well, sometimes you can't, but oftentimes you could, right? Like there are ways, like this is a silly example, but let's say you're trying to shift all of the elements on the page by 10 pixels to the right, right? There are ways you can implement that without having to query each and every element in this way, right? You can basically increment all of the elements at once without querying the offset length and then we would perform layout once at the end. And I would avoid the problem. And by the way, sorry, I forgot to show you this here. Whenever you see this warning sign, let's see if I can make that a little bit bigger. This warning sign in DevTools is basically telling you that there's a forced synchronous layout. That's why we have the warning sign. We're basically saying like, look, your code is doing something not very smart right here. Maybe that's intentional, but you probably want to take a look at this. Okay. So, finally, we've done the layout so we know where on the page everything is going to be laid out. We can now actually paint some pixels. So how does the paint process actually work? First of all, we know the layout. We know the styles. There may be different layers. So, for example, we can promote certain elements into a separate layer like the video tag, right? We can render a video as a separate entity and that may be done directly on the GPU. So, the final step is basically the compositing step which takes all of this information together and pulls it into the final pixels. And the interesting part here is not all pixels are created equal as it turns out. So, first of all, of course we want to minimize the amount of area that is being repainted, right? So, let's say we're building our animation or we have some interactive component of our page. If every single time that user interacts with the page gets to repaint the entire page, that would be very expensive, right? So, we in a browser are trying to figure out a way like how do we optimize the area that we actually need to update and we can reuse the pixels from the previous frame. And you can also track that and I'll show you that in a second. So, the area matters, right? How much we're updating matters. But the second one is that not all pixels are the same because some pixels are just frankly more costly to render like you have a drop shadow and another element and another effect and an outline and something else, right? That is much harder for us to compute than a white pixel, right? So, depending on the number of effects that you may have applied on your specific elements, they may take a different amount of time. So, it's a combination of both how much you're rendering, like paint area and what are the effects that you're applying. And here's kind of the one-on-one version, right? Like, how does this stuff actually work in a browser? We have a screen, right? So, imagine this is kind of your phone in the landscape orientation, right? Internally, we actually split the screen into what we call tiles, right? So, these are the blue lines, right? So, we cache each tile, we render each tile independently, such that if you scroll, we can reuse tiles, right? You can see how they would overlap between different screens. Then, certain elements can have their own layers. So, I mentioned the video tag, but you can also promote certain elements, like a CSS animation is its own layer, right? And the reason we want to have layers is, it means we can composite the actual layers very quickly. Like, imagine you have a background. We shouldn't have to update the background if all you're doing is the CSS animation, for example, is rotating, right? We can actually keep the texture from the background and then supply the new location of the animated element. So, DevTools actually gives you visibility into all of this information. And the first time you enable all these flags, you get a bunch of red boxes and, you know, gold borders and all this kind of stuff, and it looks all kind of weird. This is what it all means, right? We've actually covered everything we need to know about this. So, this is me looking at Google+. I enabled a bunch of flags in Chrome DevTools. I'll show you guys in a second. And what it's showing you here is, first of all, we have the tiles, right? So, this is how Chrome is splitting the actual visual output, a little bitmap, if you will. The orange or the gold borders are showing you the individual layers. So, what the layer means is that, you know, if the page, for example, if I scroll the page, we don't have to repaint that layer, because like the headers all do the same. It just happens to be floating at the top, so we can just apply it later. And this is important because knowing which of your elements are layers, or maybe you should be promoting these layers, is a good performance optimization. And the last one is the red border, which is like when you hover over an element, it actually shows you what is the impacted area, right? So, let's take a look. Actually, let's go back here. So, in DevTools, actually, let me go back to here. Open a page. In DevTools, in your settings, you can go in and enable. So, show paint rectangles, show composited layer borders, and continuous page rendering. So, let me just disable some of these, actually. Okay. So, here we go. That's smaller. So, as you can see, as I'm scrolling over these different elements, it's showing me the area that's actually being updated. So, this is the area that Chrome has to repaint in order to, so, for example, in this case, it's changing the color of the text. And because the color of the text has changed, that's the area we need to update, which means that this entire tile will be updated. And this is nice, right? So, this is the case that you want. You are specifically like hovering over the text, and that's what we need to update. Then I hover over some other elements, like here, and for some reason, we're repainting most of the screen. So, this already tells you that, hey, there's probably something funny going on where we have some other element hanging at the top left corner of this page, which is why we're getting this giant bounding box. So, frankly, this paint right here is very, very expensive. And you can see that, in fact, when I'm scrolling here, I'm getting less than 60 frames per second. So, it's very expensive for us to paint this information. But let's look at a little bit more interesting example. So, I'm going to grab this guy. So, very simple page. I'm going to show you without depth. So, all it does is, as I'm scrolling, it kind of gives you this like animation effect, right? Silly, but illustrates the point. So, let's go into timeline, and I'm just going to start recording. So, I'm going to switch into frames mode, and here we are. I'm going to start scrolling, and as you can see, so far so good, right? So, this line right here is 60 frames per second, and when I scroll, most of the bars finish well below 60 frames per second, or within our budget, right? So, most of the frames are, so 12 milliseconds, sometimes we skip a frame, but more or less we're within the right order of magnitude. Now, let's clear this. Start recording again. I'm going to start scrolling, okay? Now, let's add some on-scroll handlers, JavaScript. Oof, okay? So, you can see that all of a sudden the performance of this thing has changed dramatically. I'm going to add some costly effects, and once again, you know, the performance drops through the roof. So, let's stop this and look at this actual code. So, we can zoom in on some frames, and you can see that all of a sudden, you know, when I enabled my on-scroll handlers, each one of my frames is basically reliably taking more than 100 milliseconds. And what's happening here is whenever a scroll event fires, I am firing the JavaScript function, which then does our example that we saw before, which is layout, recalculate style, recalculate style. And now you guys know what it's probably doing. It's probably updating some style, and then immediately querying the style back, which forces us to do the full layout again, right? So, there's a for loop somewhere in here which makes this extremely expensive. And of course, this is kind of a silly example to illustrate the point. But nonetheless, you'll find that a lot of sites have this type of code. And then finally, we have our costly effects. So, we can go into here. And you can see that all of a sudden we have this extra cost, which is the paint, right? And the paint itself is taking actually 12 milliseconds. It may be a little bit hard to see because it's small. But, yeah, so 12 milliseconds just to do the paint. And in Canary, we actually have a new feature, which is really interesting. So, let me open this up. We have this new mode called enable continuous paged root painting. So, what this does is it forces the browser. So, remember that we don't want to be painting this screen every single time. Like, if there's nothing to update, then forget it, right? Why should we do it? Well, continuous paged root painting actually forces us to do a paint on every single frame. And why would you want to do that? Well, it allows you to identify very quickly what are the expensive pixels on the page, if you will. So, here's an example. Notice that I have the page paint time here, right? And it says, well, right now it takes me about 15 milliseconds to paint this page. Which, as you know already, this is 15 milliseconds just to paint the page. This is without layout, without your JavaScript code, without anything else. But if I disable the costly effects, so some drop shadows and all the rest, look at that. The page paint time dropped to three seconds or three milliseconds, right? So, you can enable this in your own application and just kind of poke at different elements, hide them, reveal them, disable certain CSS styles and kind of quickly figure out what are the potentially expensive operations on your given page, right? Maybe you don't need that drop shadow after all. I don't have anything against drop shadow, by the way. Drop shadow is great. And we talked about continuous paged root painting. So, to play with continuous paged root painting, you're going to have to grab Chrome Canary. It's available in there. And then just enable it in your settings. And then I guess one last tip is, as part of the continuous paged root painting, we actually added a new shortcut, which is you can, if you're in the elements panel, you can click on any element and just hit H, which will hide it. And by hiding it, we're not forced to paint it. And that will quickly allow you to kind of toggle elements back and forth and figure out which are the expensive elements. So, a nifty little trick. And speaking of Chrome tips, a few things that you may find useful. So, debugging and rendering performance is hard, right? You need to play with the tools, you need to understand all the different interactions between painting, rendering, fetching different assets and all the rest. What you can do though is, let's say you've found a certain page or certain interaction on your application that is just not performing all that well, you can actually right click on the timeline. You can record the timeline, of course, but then you can right click and export it, which is great, right? Because you can then grab that thing, attach it to a bug report or send it to your co-worker and be like, hey, fix this. Or just save it for yourself later, right? So, I have lots of timeline traces lying around that I need to look into later to figure out what the problem was. And similarly, you can then go back into DevTools and load it in, right? And you can explore it. Of course, you don't have the context of the original page when you do that, but you still have the full fidelity of like, here are all the events, here's the JavaScript that fired and all the rest. So, very handy. Further, it can be sometimes hard to figure out what's going on, like there's a lot of events flying around. So, you can actually instrument your code to make this a little bit simpler. So, for example, in my JavaScript code, you can actually say console.timestamp adding result. And what it'll do is it'll add this annotation in your timeline and also put kind of a marker here to say. So, you can actually see like, here's the sequence, here's the timeline, here are the specific steps, right? Like I clicked on the button, I press submit, I did something else, and it allows you to segment this data much, much better. So, very handy, too. And last but not least, remember what I said about 16 milliseconds is not an absolute time. It differs based on the device that you're running. So, the great thing about DevTools is it actually, it provides all the same functionality on mobile as well, right? So, if you're using Canary, we actually made it much simpler. I'm sure many of you guys have used the remote debugging, but for the life of me, I can't remember the ADB command to set up the bloody poor forwarding. Every single time I'm like, I'm searching for it. You don't have to do that anymore. You just connect your phone via USB cable and you go to Chrome Inspect and you'll see your phone show up there. And you can then click on the specific page and you get all of the DevTools data but running on your phone. So, this means you can see the rendering and the CPU time costs on your phone, which is very important. And also, if your phone is connected to your 3G or 4G network, you also get the network performance of the actual experience on mobile. So, mobile first is of course a rallying cry for a lot of people and I think it's an important one. And if anything, we should probably start with developing on mobile devices and then anything we do to optimize mobile will of course translate into direct benefits on desktop. Okay, so finally, we've talked about the painting process and all the rest. What about the GPU? I heard that it makes everything go much, much faster. That is almost true, partially true. So, the thing to understand about the GPU is it's not exactly, it won't save all things either. Just like 4G won't make all of our network problems go away, GPU doesn't actually magically make everything faster. The way the GPU acceleration works is we've painted our texture, right? So far, we've looked at the specific tiles. We then take that tile and we upload it to the GPU. We transfer the specific tiles and then once those tiles are there, we can send simple commands to the GPU. For example, we can say, hey, remember that layer that I told you about, like that bar at the top? Yeah, just move it 10 pixels down. And that's actually very efficient because the GPU is very good at this kind of thing, right? And it means that we don't need to transfer the entire layer, like we don't need to repaint the whole thing. The GPU is very good at taking a whole bunch of different layers and just compositing them together, which is kind of the picture that you see here. You have a background, we have some leaves, and we have some information. If each one of these is a different layer, right, we can move the leaves independent of the background and the compositing can happen on the GPU, which is very fast. But the GPU is very, very limited in the kinds of things that can actually do with each texture. So it can't change the color of the texture, for example. In order to do that, we actually need to repaint the texture on the CPU and re-upload it to the GPU, right? So there are certain things you can do to have all the work be done on the GPU, and then there's many things you can do to hurt the performance. So texture uploads are not free. In fact, oftentimes, if you guys are designing for mobile and iOS and others, there's the transform translateZ hack, which is like, move everything into its own layer. Because I heard that makes everything go faster. What you're doing there is you're saying each and every element that you translate Z, it's upgraded into its own texture, which gets uploaded to the GPU. But then that doesn't actually help if all you're doing is changing text or changing color or doing other things, right? You're just costing more memory bandwidth between the GPU and the CPU. So GPU is not a free launch. The only thing that we have that is kind of like a free launch is the CSS3 animations. And the performance of CSS3 animations is still variable between the different implementations. Even the performance of CSS3 animations on different versions of Chrome is very different. It's improving rapidly, but there's some quirks. So here's an example. What are we doing here? We're actually declaring an animation, which is a hover effect. And so spin hover is me defining a new animation. It's not some magic CSS keyword. And what I'm saying here is spin run this animation for two seconds. The animation takes two seconds, and it runs on an infinite loop, right? And then here are the key frames. At 0%, you're at 0 degrees, and at 100%, once you're done, after two seconds, you're going to be rotated 360 degrees. And the CSS3 animations implementation will basically take that and say, okay, well, I need to interpolate between these two points, and that means I need to rotate the element 360 degrees. So this is kind of a very simple animation. And this is something that can be done by the GPU very, very efficiently, right? Because it's the one texture and it's just being rotated. So here is an example. So here's a CSS3 animation, right? Kind of a, once again, a silly one, but it illustrates the point pretty well, because if I open... Actually, let me load it in Canary. Okay, if I open my DevTools. Where's my settings? So right now you can see that the whole page is red, and that basically tells me that the entire page is being repainted on every single frame. That is not what we want. If I go back down and I disable continuous page repainting, you can see that there's no red circles. So there's red circles when I'm hovering, when I'm changing this stuff. But the animation is running smoothly at 60 frames per second, and there is no areas being repainted. Because all of this work is being done directly on the GPU, right? We're just sending commands to the GPU saying rotate this 10 degrees or rotate this 20 degrees. And this is handled entirely on the GPU. So these kind of gold borders show you the individual layers, right? So each one of these elements is an individual layer that lives on the GPU. So this is very efficient. This is the future of where we want to be with animations, because it allows us to offload all of the work. And could you implement this in JavaScript? Yes, you could, but that means that we would have to repaint each step of the animation in here. So CSS animations are definitely something to investigate. So with that, we've actually gone through the full cycle, right? We talked about the network. We talked about the HTML. We constructed the DOM and the CSS DOM. We talked about how JavaScript can actually hurt us. It's what enables the modern web, but at the same time, it's probably one of the biggest performance bottlenecks because it can block both on the DOM and on the CSS. We talked about the render tree and why that exists, layout. So why is layout so important in some of the quirks or performance pitfalls? And finally, painting. And then, you know, so this right here is a critical rendering path. The critical rendering path is basically what is the amount of time that it takes us to get to the first render of the page. Not the first byte of the page, because the first byte is meaningless, right? I could be waiting for five seconds for the page to show up. That's not good. We want to minimize the critical rendering path, which is to get to the first paint as quickly as possible. And then we want to continue building the page. And once we start continuing building the page, we need to do that at 60 frames per second, right? So these are very different disciplines, if you will, and different tools. Okay, so some summary tips. Reducing DNS lookups. So this means, you know, redirects, basically. If you're talking mobile, redirects are very, very expensive. Making fewer HP requests. So this is your concatenating files, minifying files and other things. You know, these are the kinds of things that we have to do in HP 1.1, hopefully with HP 2.0. We can undo some of that logic and move that complexity back to where it belongs, which is at the server layer. Using a CDN, right, is a very important optimization. So latency is our bottleneck, as you saw. Bandwidth matters, but latency is the bottleneck for most apps. And that's why CDNs are important. Reducing the amount of data that we transfer. So these are kind of the basic things that you would think that every application on the web is following today. It turns out that is not true. I can tell you that simply by enabling GZIP on all of the web, we could make it about 20% faster. Which is to say there's a significant portion of the web, which is simply just not. All it takes is one command line flag in your config to say compress these resources apache, please, or nginx. Lots of sites don't do it. So check your configuration. Optimizing images. So images are 60% of the bytes of an average page today, which is to say if you're using PNGs for photos, that's a terrible decision. Because PNGs are not designed for photos. If you use a JPEG or a new format like WebP, we can give you much better compression and reduce the size of your pages. And then of course caching. This is another big one. It turns out that lots of sites are simply not adding caching headers, right? And that's a problem. I'll show you. It's my favorite, favorite bad example. If it loads. Okay. So RedRobin.com. I'm getting a little hungry. I'm approaching lunch. So let's run and analyze. For for DevTools. And basically what it does is it'll reload the page and it'll monitor everything that's all the traffic, all of the assets, analyze them and basically give you recommendations for how you can make your site faster. If this actually loads. So we're going at about. 20 kilobytes a second. And I just happen to know that this page is about 10 megabytes in size because. These beautiful burger images. You can't see yet. They're they're PNGs right each one of these files is about 1.5 megabytes I think and they have this carousel, which is beautiful when you actually see it. The carousel is about six megabytes, but not only that. Oh, there we go. They actually don't. So they don't compress the images. They don't set the caching headers, which is to say every single time I reload the page and he's a fetch the entire 10 megabytes of the page. They don't compress the HTML or CSS. It's basically they don't do any of the optimizations. So they serve the entire 10 megabyte file on every single. Page load. And. I do want us to finish because I want to show you the the rules. Come on Wi-Fi. Well, let's continue and we'll actually just come back to it. Of course now that I'm downloading 10 megabytes of burger images. My presentation will probably won't work either. Let's take that out of that. Okay. So adding expires headers and adding things like it's actually basically a fingerprint on the asset, which says I can send you a query saying, hey, has this asset changed? Here's a fingerprint that I've last seen associated with this asset. If the file hasn't changed, we can just say, nope, it hasn't changed and you can extend that lifetime of that asset in your cache. So an important optimization there. Optimizing the critical rendering path. So incredibly, incredibly important for mobile. It takes multiple seconds right now on average to download or render a mobile page on mobile phone. Here are some tips that we've discussed to address that. So we need to stream the HTML. We need to place style sheets at the top. We need to inline, likely, some important styles with the critical styles. Defer any and all JavaScript such that it doesn't block. And maybe look into leveraging something like speedy where you can do push and other things. And then finally, once we've rendered the actual page, we can talk about performance, which is to say, once the page is loaded, we need to deliver 60 FPS. And for 60 FPS, you know your budget. It's 16 milliseconds. Really, it's about 10 milliseconds. And then you can just think about your budget and go through all the steps. Like what am I doing in my code? What is my performance? The JavaScript execution? Am I running long functions? Am I running too many of these functions? Should I be using the request animation callback? Should I be concerned about layout or painting? So certain styles may be just very expensive on my page. We provide in DevTools tools to diagnose all of this stuff. There's a lot of information in there. If you actually check out our DevTools documentation site, we give you nice tutorials for each component of using Timeline and all the rest. And then finally, test on mobile devices. I can't stress this enough. It's one thing to design something on your laptop, and it's another to actually test it on a real machine. So let's see. Hey, look at that. Okay, so we finished. So what PHP does is we actually, so we analyzed, oh, oh well, not a beautiful burger. So we analyze the page and we go through different rules and say, how can we optimize this specific page? So the score, we give it a score 42 out of 100, which tells me that there's lots of optimization that we can do. And on the left, that's the important part. We have specific things you can do. So optimize images, right? We looked through all the rules and said, look, if there's only one thing you're going to do, optimize your images. And by the way, if you optimize your images, you can have a 50% reduction in the file size of your page. So in fact, in this case, it's showing one megabyte, but I know that's not true because it didn't download all the images. If it downloaded all the images, it would have told you that it could have saved five megabytes if you optimize your images. And in this case, how does it actually know? Well, the extension actually downloads the asset and it also tries to recompress it with different formats. So I'm not going to try and open the original image because that's on the network, but you can actually click on see optimized. And this is the optimized version. We actually give it to you right in the extension, right? You can just right click on this and save it and be done with the optimization, right? So that's optimized images. Second one is enable keep alive. So it turns out that this site is actually, it's using the HP 1.0 spec or the model on their server. I think they're running Apache, which is once again a simple flag in your config. And what keep alive or lack of keep alive means is that every single request requires a new TCP connection. So imagine opening this on a 3G connection, right? Every single image would require a new TCP connection, which means going through the TCP handshake, new connection, that slow start phase and all the rest. So very expensive. After that, expiration, so there's no caching on any of these assets. So every single time you load the page, you have to refatch all of the CSS, which of course is going to hurt your rendering performance really, really badly, right? Because your first render may be slow, but your next navigation may be fast because you can grab the CSS out of the cache, not on this case. Enable compression, combine images, minify JavaScript, like this is basically a case study for all the things you, they forgot to do, I guess. And unfortunately, that's, I mean, it's a funny, bad example, but also frequently I find many big sites, popular sites that are not applying these optimizations. Things like not compressing, not enabling gzip or not compressing their images properly. So, you know, as a kind of closing thought, I'll leave you with this, which is performance as a discipline. It's a very complicated discipline, right? It is, you know, 10 years ago we were building documents. It was a very simple thing that we delivered a bunch of HTML markup. Today we're building applications. And because we're building applications, there's network constraints, right? With a native app, you download the app once, you kind of pay that cost once, and then you load it, and then you just load it every single time locally. With a network app, a web page, you're paying the install cost every single time, right? Because we're requesting it live, and this gives us a lot of advantages in terms of being able to update the application and deliver kind of better experience in terms of real-time notifications and all these other things. But if you're not careful about optimizing for network, it's going to hurt you. And then after that, right, we're not building pages, we're building apps, which means 60 frames per second. We have pages that are leaking memory, we have pages that are taking a lot of time to execute JavaScript, we have pages that are triggering layouts, style. We have this whole GPU rendering pipeline for CSS animations, we have 3D games in the browser, like it's a crazy world out there, right? So this stuff is evolving really, really fast. It's not a checkbox that you put at the end. It's like, yes, I made my site performance. It is a discipline, you have to design with it. And if nothing else, I'll give you the one last tip, which is make sure that you tie your performance goals to your product goals, right? Measure it against your bottom-line revenue or other impact, you know, engagement with the users. Because oftentimes you can show direct correlation between performance and how users engage with your app. And once you have that data, it's very simple then to prioritize performance just like any other feature on your product backlog, as opposed to like, oh, we need a performance sprint because our app got slow, right? Like that's how we thought a couple of years ago that you can't do that anymore. And with that, I think we made it to the end. So you can find the slides online. So actually that link, so fluent-perf shop will take you to a feedback form. I cheated. It's a feedback form, which you don't have to complete if you don't want, if you just press submit. But I would appreciate your feedback. And once you hit that, it'll give you the link for the actual slide download. So I'd appreciate your feedback if you could. And with that, I think we have a little bit of time for questions. So how far back does document.write reset the rendering phase? It's not that it resets the phase. It basically blocks us from constructing and from continuing to construct the DOM, right? Because basically when we see a script tag, we have no idea what's going to come next. Because for all we care, let's say the HTML document had a bunch of tags coming afterwards. Your JavaScript file could nuke all of that and write its own DOM. So it's not that it resets it, it just blocks us from proceeding and doing any other rendering. Which is why you want to avoid synchronous scripts at the beginning, right? Yeah, that's a fantastic question. So the question is how do we automate the performance testing and regression analysis? There's a number of different tools. Probably one of the best ones is I talked about exporting the timeline trace, right? You can actually do the same thing for a network trace, right? So with Chrome, you can actually, so phantom.js, right? Headless WebKit browser. You can actually get it to export that hard file or the HSP archive format, which you can then feed into tools like PageSpeed, Weislo and others and they'll run a set of rules and give you a score at the end. So there are plugins, for example, for Hudson where you have your check-in go in, you kick off a build which grabs phantom, runs a test against your site, grabs the hard file, runs PageSpeed on it, pushes it back into Hudson. This is a pretty complicated loop that I'm describing, but there are plugins that exist today that will give you the sort of automatic regression analysis. I think there's more. There's more tools that we need to build and more that we could do as developers to help automate this. That's for network. And for things like rendering performance, there's a new project that we have on Chrome called telemetry. You know, currently, it definitely requires some low-level fiddling with the bits. You can just, if you search for Chrome telemetry, you'll find it. It basically uses the Chrome debugging protocol to drive Chrome and then you can export the timeline data into whatever format you want and analyze it. Under the hood, both the hard file and the Chrome timeline trace is just a JSON payload. And once you have the JSON payload, you can run any analysis you want on it. And it has all of the metadata, like which function was being executed when the GC ran, what the timing was, etc. So the data is there. We just need better tools. Is it true that Chrome for iOS uses UI WebView? And if so, how does that affect the Chrome developer tools on iPad and iPhone? So yes, it is true that Chrome on iOS uses UI WebView because we have to. That is the Apple policy. But all of the other code, like the networking stack, all of the Chrome around Chrome, all the functionality or sign-in bookmarks and all the rest is the native Chrome code. So this is why, for example, we can deliver speedy on iOS, but we use the UI WebView for rendering. In terms of the DevTools integration, I think that's work in progress right now. It's definitely something that we're looking into. Anybody else? Right, so the tool that I showed for page repaint times, yeah. So that's part of DevTools. What you want to do is you want to go into the settings. So if you just open your DevTools, you click on settings. Okay. And you can enable show paint, rectangle, show compositor, layer borders. So those are the tools that I was showing. But you probably want to use Chrome Canary because it gives you more features that you can play with. And I think it's lunchtime, so they want us there. But I'm going to be around for the next while. So if you guys have any more questions, please feel free to reach out. And they took out off the screen, but you can ping me an email.