New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Docs on Performance Considerations #878
Comments
(1) How crazy would it be to move the conditional html5shiv logic into the parent doc instead of keeping it inside of modernizer? Or.. we should make it clear in the docs that if that's the only thing you need modernizr for, then inlining the conditional check into the doc is a much better way (performance wise) to integrate it. @paulirish when should you not have async on the script? Could we make async the default recommendation? I wouldn't discount runtime and network overhead.. I've registered tens of milliseconds for modernizr in the past (on my macbook pro), and the fact that it usually lives in the head places it right in the critical rendering path. |
Modernizr has a lot of tests, which might cause repaints (especially addClass and injectElementsWithStyle). If you put modernizr in the head, the tests are always working extremely fast. If you put Modernizr at the bottom or loading it async, it depends on the complexity of your HTML and CSS, how fast Modernizr is executed. (especially CSS including non matching selectors). Here is a simple example, which measures the execution time of Modernizr and outputs it in the console: Due to the fact, that I'm almost using a CMS (don't know how large the HTML will be) and my CSS is always over 50kb (without compression), I never put it bottom/load it async. |
@aFarkas this is very interesting. I'm not sure I fully grok the results or implications yet, but some test data:
Looking at the debug output: head: 21.000ms, async: 297.000ms, bottom: 471.000ms First of all, the variability here is massive.. so I wouldn't take those numbers at face value, but there is definitely something interesting going on here. Further, the real interesting bit is that the SpeedIndex / time to first paint, doesn't seem to vary all that much. But this deserves a much closer and in-depth analysis.. I'm not all that familiar with inner workings of addClass and injectElementsWithStyle. From what I can tell addClass adds names of supported features into root tag, and that's what you're referring to in terms of reflows / repaints? What's the logic behind injectElementsWithStyle? |
@igrigorik not necessarily |
About the the repaints/reflows. Like @ryanseddon already said, addClass and injectElementWithStyles doesn't always create reflows/repaints. addClass can create a repaint or reflow, if you are using some of those classes in your CSS. If you are embedding Modernizr into your head it will never create a reflow/repaint because the browser hasn't yet rendered any layout things (modern browsers only DOM). But even if you are not using those classes (my example do not use any of them) the CSS selector engine has to check wether the change on the root element has caused some style changes and changing the root element can cause style changes on all elements. Same goes to injectElementsWithStyles even if there is neither a reflow or a reapint. As soon as you include it at the bottom or async, the CSS engine has to look wether the injected element matches any selector in your CSS. This can be expensive. The intersting part about the tests above. If you change to another page with different HTML and CSS the async and bottom tests performance will change, but the head should be stable. |
@aFarkas @ryanseddon (pardon my naivete...) ... couldn't the cost of traversing a deep HTML tree be offset by running the test inside an injected iframe, or similar? That may not solve the entire problem, but potentially address at least a part of it? |
Running it async / bottom of the page could also cause extra requests (as well as a FOUC) when using Modernizr's classes if not used carefully: .thing {
/* Will match on first paint and request file because Modernizr hasn't run yet */
background-image: url(image1.png);
}
.feature .thing {
background-image: url(image2.png);
} Obviously changing the first selector to |
Another example of the above: .no-js .thing {
background-image: url(image1.png);
}
.js .thing {
background-image: url(image2.png);
} I don't know how to avoid 2 requests firing here when Modernizr loads async. Currently we switch Users making use of the change in #808 would be affected by this kind of problem too. |
@stucox interesting, thanks for the example.. this is indeed, a gotcha. I guess in a nutshell, if your code / markup is conditional of modernizr classes, then deferring the calculation + injection of those classes will be more costly, where more is highly variable on the site - the more it depends on these classes, and the more complex the page, the worse the end result. Hmm! This, of course, doesn't help with our goal of eliminating external JS to avoid the extra latency on mobile... |
Probably worth referencing @SlexAxton's article here — Deploying JavaScript Applications. The scout file technique relies on Modernizr being inlined in the initial page load (and hence critical rendering path); but you then try to minimise requests after that. Avoids these styling race conditions at least. A v3.0 build with only a few detects, with the |
@stucox thanks, that was a great read. In a nutshell: inline fonts and images into CSS, inline CSS into JS, and then use a loader to figure out which JS bundle to pull down. :-) I guess to come around full circle on this whole issue, things we discussed: (1) Loading Modernizr async could have some interesting negative side-effects: double downloads, expensive layout / reflows, and FOUC side effects amongst other gotchas. As such, perhaps some applications could defer its evaluation, but it really depends on which features they rely on, and the complexity of their pages.. A somewhat related question to consider is: are there sites that have it included that don't actually use it? I have reasons to believe that this is indeed the case.. as I've encountered dozens of sites in this category. (2) The good news is, the simplest minified+gzip'ed version of modernizr is 3.4KB, which means it could be inlined into the page and leaves us with ~11KB of budget for the rest of the content (to avoid extra RTT's). Of course, the more features you add in your modernizr build, the larger the size.. Hence: make sure you need the features you include in your modernizr build. tl;dr.. Check if you need it, if you need it, check that you need the included modules, and finally, inline it. Anything I'm forgetting? |
i stumbled upon these: This performance statistic made by jsperf_See link at the bottom_ shows the Offset vs documentOffset that in most of the newer browsers it have a better performance so i tested it out my self inside modernizr and i saw average performance improvement which i wanted to share (im not good with github and i wanted to make a "fork" of this but contact me on simonpetersen3AThotmailDOTcoDOTuk for the code i changed) My benchmark: Total duration offset: documentOffset: next place to improve performance is .appendChild but havn't really dogged into it yet tough |
Could we improve the repaint issue by using /cc @aFarkas |
In terms of performance, I am really confused as to why Modernizr test all cssomPrefixes every time? (If i am reading correctly). It's extremely easy to reliably detect the engine first, then only test the prefix for that engine and the non prefixed version. Testing them all is an unnecessary waste of resources. |
@hexalys Some browsers support multiple prefixes: older versions of Opera, for instance. That said, we could probably still make some efficiency savings, if only by determining the ‘most likely’ prefix then checking that first each time we do a |
@stucox I am aware of Presto support for webkit prefixes, though I don't get the usefulness of that. It was only making things worse IMO. Is there an actual compelling case for testing Webkit prefixes for Presto Opera? My sense of correctness as a developer to get Presto support is to add the -o- prefix in my css, or else forget about it. |
I had it in my head there were properties for which Presto had only implemented the I’ve raised #1223 to discuss this further. |
Here's a resource where it says opera will use |
(draft, updated Feb 2016) Performance Guidelines for Using ModernizrHistorically, the recommendation was for Modernizr to be placed in the head. There are two reasons:
So to be safe, the recommendation was to keep Modernizr as a request in the head. In this day and age, it's irresponsible to continue to recommend that. Let's walk through a few situations and what action you need to take: You are serving HTML5 elements in IE8
You are using styling hooks of Modernizr like
|
⬆️ My attempt so far, edits and suggestions welcome. |
@paulirish
I don't think this is right. Modernizr is a tiny and cacheable asset. The network performance hit will occure only once and will be "small". But the performance decrease for the runtime will be there with each website rendering. Therefore I still think the default suggestion should be "include Modernizr in the head of your document*. If you look into the test made with full Modernizr 2.6 (without third party detects) one year ago, you will see that including Modernizr in the head already wins on first impression. Not to mention the cached impression. This was the runtime performance of Modernizr in Chrome (using this site as a testing enviroment): I think the recommendations should be a simple no brainer:
Developers, who further want to omptimize the network performance should: About making Modernizr smaller:If someone includes all JS at bottom, most code of html5shiv is also only needed there. In this case only the style (for performance!) and the good old createElement part needs to be in the head. Perhaps I would maintain also a html5shiv-minimal.js that should be included in the head and if this is used one of the other shivs can be used async. What do you think about this? I made a testcase for this, which you find here: |
Just made some test with different websites. The performance decrease isn't that heavy like I tested on the site above. Here are the testresults: modernizr.com:Firefox: Chrome: IE9: en.wikipedia.org/wiki/Main_PageFirefox: Chrome: www.youtube.comFirefox: Chrome: www.amazon.com/:Firefox: Chrome: IE9: |
I'm not sure how true this is:
Of the CSS features it's on an element that never gets injected into the DOM. Of the ones we do inject it's at the bottom in a div which wouldn't trigger massive style recalcs and those usually have Inlining in the head if it must be there is a good recommendation. |
@ryanseddon While those numbers show that the performance decrease isn't in total that bad, I still have my problems with the recommendation. I think that an increase of network performance by about 100ms only on first impression doesn't outweight a decrease in runtime performance by about 20ms. The reason for this is a) runtime will be always slowed down with each page view, while network performance is only for the uncached page view improved, b) if there is an additional script request in the head, the browser might block rendering the page, but the browser isn't blocked, so it can parallelize a lot of tasks in the background. On the contrary while script is executed the browser is fully blocked and can't do anything and c) the decrease of the runtime performance isn't stable. We can not say it's always about 3x slower. The numbers do say it's between 1.5x - 12x slower depending on different circumstances (especially the amount and complexity of the used css selectors). |
This said, if someone really wants to squeeze out page performance, we endup with just one recommendation: |
It's unrelated. There is nothing about Modernizr.load that requires Modernizr to be in the head. So yeah they do apply. :) |
The page won't make the async requests until they're found. So I'm thinking that it's probably beneficial to put Modernizr.load requests before other JS resources that block. I don't have any data to prove this, just intuition. |
@paulirish html5shiv aside, any reason why we can't use script[async+defer] instead? In which case, putting it at the top of the head is not a problem... and actually helps the browser to discover it sooner. |
The Guide is so awesome |
Modernizr is now only used for the HTML5shiv, but included lots more that now isn't needed (e.g. yepnope) and was on an old version anyway. So, it makes sense to update the version of Modernizr and create a new minimal build. (The custom Modernizr build in the current version outputs a URL to reproduce the build, so it'll be easier to update now as well. There was some of our own Javascript used for SVG feature detection, and since this can be done by Modernizr, I've used that instead. Fixes #1774 Incidentally, there is a strong argument that we shouldn't put Modernizr in the <head>: Modernizr/Modernizr#878 (comment) ... and inline the HTML5 Shiv (including html5shiv-printshiv.js). I'll create an issue about considering that.
Modernizr is now only used for the HTML5shiv, but included lots more that now isn't needed (e.g. yepnope) and was on an old version anyway. So, it makes sense to update the version of Modernizr and create a new minimal build. (The custom Modernizr build in the current version outputs a URL to reproduce the build, so it'll be easier to update now as well. There was some of our own Javascript used for SVG feature detection, and since this can be done by Modernizr, I've used that instead. Fixes #1774 Incidentally, there is a strong argument that we shouldn't put Modernizr in the <head>: Modernizr/Modernizr#878 (comment) ... and inline the HTML5 Shiv (including html5shiv-printshiv.js). I'll create an issue about considering that.
Modernizr is now only used for the HTML5shiv, but included lots more that now isn't needed (e.g. yepnope) and was on an old version anyway. So, it makes sense to update the version of Modernizr and create a new minimal build. (The custom Modernizr build in the current version outputs a URL to reproduce the build, so it'll be easier to update now as well. There was some of our own Javascript used for SVG feature detection, and since this can be done by Modernizr, I've used that instead. Fixes #1774 Incidentally, there is a strong argument that we shouldn't put Modernizr in the <head>: Modernizr/Modernizr#878 (comment) ... and inline the HTML5 Shiv (including html5shiv-printshiv.js). I'll create an issue about considering that.
Modernizr is now only used for the HTML5shiv, but included lots more that now isn't needed (e.g. yepnope) and was on an old version anyway. So, it makes sense to update the version of Modernizr and create a new minimal build. (The custom Modernizr build in the current version outputs a URL to reproduce the build, so it'll be easier to update now as well. There was some of our own Javascript used for SVG feature detection, and since this can be done by Modernizr, I've used that instead. Fixes #1774 Incidentally, there is a strong argument that we shouldn't put Modernizr in the <head>: Modernizr/Modernizr#878 (comment) ... and inline the HTML5 Shiv (including html5shiv-printshiv.js). I'll create an issue about considering that.
Modernizr is now only used for the HTML5shiv, but included lots more that now isn't needed (e.g. yepnope) and was on an old version anyway. So, it makes sense to update the version of Modernizr and create a new minimal build. (The custom Modernizr build in the current version outputs a URL to reproduce the build, so it'll be easier to update now as well. There was some of our own Javascript used for SVG feature detection, and since this can be done by Modernizr, I've used that instead. Fixes #1774 Incidentally, there is a strong argument that we shouldn't put Modernizr in the <head>: Modernizr/Modernizr#878 (comment) ... and inline the HTML5 Shiv (including html5shiv-printshiv.js). I'll create an issue about considering that.
Modernizr is now only used for the HTML5shiv, but included lots more that now isn't needed (e.g. yepnope) and was on an old version anyway. So, it makes sense to update the version of Modernizr and create a new minimal build. (The custom Modernizr build in the current version outputs a URL to reproduce the build, so it'll be easier to update now as well. There was some of our own Javascript used for SVG feature detection, and since this can be done by Modernizr, I've used that instead. Fixes #1774 Incidentally, there is a strong argument that we shouldn't put Modernizr in the <head>: Modernizr/Modernizr#878 (comment) ... and inline the HTML5 Shiv (including html5shiv-printshiv.js). I'll create an issue about considering that.
Hi, Via Google Page Speed Test: Your page has 1 blocking script resources and 3 blocking CSS resources. This causes a delay in rendering your page. Inside head - <script src="assets/scripts/modernizr-3.5.0-respond-1.4.2.min.js"></script> For faster page loading, which do I use async or defer? Thanks, Shaun. |
… Modernizr to be loaded in the footer. Enqueueing Modernizr before `responsive-scripts` allows `modernizr` to be added as a dependency. This ensures Modernizr is always loaded first, incase any theme or framework JavaScript depends on Modernizr functionality. Loading Modernizr in the footer is generally ok, but there are situations that would experience issues (see Modernizr/Modernizr#878 (comment)). For that reason, we will leave Modernizr in the header by default. But if a theme needs to, for some reason, they can move Modernizr to the footer using `r_modernizr_in_footer`.
… Modernizr to be loaded in the footer. Enqueueing Modernizr before `responsive-scripts` allows `modernizr` to be added as a dependency. This ensures Modernizr is always loaded first, incase any theme or framework JavaScript depends on Modernizr functionality. Loading Modernizr in the footer is generally ok, but there are situations that would experience issues (see Modernizr/Modernizr#878 (comment)). For that reason, we will leave Modernizr in the header by default. But if a theme needs to, for some reason, they can move Modernizr to the footer using `r_modernizr_in_footer`.
Update: Read this comment: #878 (comment)
Modernizr needs to be placed in the
<head>
for two reasons:Let's just document this so people know what's up.
async
attribute and/or place it at the bottom if neither of these matter to you.Can someone take a crack at drafting this section for the docs?
The text was updated successfully, but these errors were encountered: