In followup of the CSS aggregation / caching issue and the follow-up duplicate.
That patch already has a solution for the problem that conditionally including CSS files makes the cache less efficient due to many more cache misses: the ability to exclude CSS files from the cache. We don't need a set of hooks for this at all.
We just need to properly evaluate which CSS files are candidates for caching and aggregation. In order to get things going, I suggest the following rules:
- CSS files that style content should be cached. We can assume that an admin will not enable modules / content types that are not used, and the main purpose of a site is to browse its content.
- Theme stylesheets should be cached. They are included on every page, after all. The case of multiple themes per site will end up causing duplicate caches, but as theme stylesheets tend to contain the majority of all CSS on a site, this will not cause much extra overhead.
- Stylesheets used only on a few pages should NOT be cached. The extra overhead for a small, extra stylesheet is nothing compared to having to download a completely separate cached file.
In order to avoid cache inefficiency, cachable CSS files should not be included conditionally if the condition is rarely true. Conditionally including based on e.g. user permission or preferences is of course valid, as they would still cause broad coverage.
Not cachable (used for only a few pages each):
./misc/farbtastic/farbtastic.css
./misc/maintenance.css
./modules/block/block.css
./modules/color/color.css
./modules/help/help.css
./modules/locale/locale.css
./modules/profile/profile.css
./modules/search/search.css
./modules/tracker/tracker.css
./modules/watchdog/watchdog.css
Cachable:
./modules/system/admin.css (currently included for all /admin pages, so it has broad coverage)
./misc/print.css
./modules/aggregator/aggregator.css
./modules/book/book.css
./modules/comment/comment.css
./modules/forum/forum.css
./modules/node/node.css
./modules/poll/poll.css
./modules/system/defaults.css
./modules/system/system.css
./modules/user/user.css
./themes/*
I think all of these stylesheets are either common enough or small enough to warrant inclusion across the site, if the owning module is turned on. If you disagree on a particular stylesheet, please explain why (but be sure to look at the CSS it contains, first).
I've attached a first patch, which makes all non-cachable stylesheets be conditionally included only where they are used (i.e. all the css files in the first list). In the CSS aggregation patch, these drupal_add_css calls should be changed to non-cachable. That should increase the cache efficiency.
This patch can be committed as is, without the CSS aggregation patch, as it already improves the number of CSS files that are loaded on the first page (or any other page).
Comments
Comment #1
m3avrck CreditAttribution: m3avrck commentedI really like this patch a lot -- it definitely makes conditional load of CSS files make sense.
And it works *perfectly* with the CSS aggregation patch for smaller aggregate files and less of them.
Seems like a win all around.
I'd say this is RTBC but I'm a bit tired so I'll leave that for someone else in case I overlooked something small :-)
Comment #2
Steven CreditAttribution: Steven commentedOops, the call for forum.css was mistakenly removed. Fixed. Thanks chx.
Comment #3
Steven CreditAttribution: Steven commentedNote to self: don't cook patches after travelling for 13 hours.
Comment #4
Morbus IffSteven - I wouldn't include the comment "Add CSS" - the name of the function call has these two words in it already, and it's a pretty self-explanable one-liner. Just wasted space and makes for ugly whitespace valleys. I also don't see watchdog in this patch? (For onlookers, color.css isn't included in the patch because it's already conditionally sound.)
Comment #5
Morbus IffComment #6
dvessel CreditAttribution: dvessel commentedI love you guys. ^^ I'll test it as soon as the preprocessor & this needs review.
Comment #7
Steven CreditAttribution: Steven commentedFixed watchdog.css and made the comment more meaningful.
Comment #8
Morbus IffThe comments still bug me, but certainly not a showstopper. This first patch is ready to go in.
Comment #9
Dries CreditAttribution: Dries commentedPatch looks good. I agree with Morbus on the comment. Maybe: "Conditionally include the CSS to reduce overhead:" would be better?
Comment #10
Dries CreditAttribution: Dries commentedSome performance benchmarks would be useful here. :)
Comment #11
Owen Barton CreditAttribution: Owen Barton commentedOK, So I did some benchmarking of #100563 and #100516. I used the same method as in my original bug report.
I am posting these results in both places, since they are both looking at fixing the same issue.
Here are the results, see below for explanation:
- As you can see, conditionally loading CSS is approximately 15% faster than the current situation, whilst a preprocessing cache is almost 40% faster.
In real time conditionally loading CSS is saves about 1 second from the time taken to initially load the page for the user. This is certainly worth committing in my opinion.
Adding a preprocessing cache saves about 3 seconds from the time taken to initially load the page for the user. This is, in my opinion an enormous benefit (compared to the effort spent whittling 50ms off a database query!) and is well worth the added complexity.
Comment #12
BioALIEN CreditAttribution: BioALIEN commentedGrugnog2, is it possible to conduct a test with both patches combined using the same criteria? Thanks in advance :)
These results are totally impressive! Im speechless.
Changed priority to critical to put it in line with the CSS preprocessor patch.
Comment #13
Owen Barton CreditAttribution: Owen Barton commentedOK, here are the combined results of both patches.
As I expected, there isn't any net benefit to using both approaches, suggesting that the effect of network latency with a large number of small files is (as we have been trying to point out many many times) a much more important factor than just the net file transfer size. Newbies who don't turn on CSS caching will still benefit from the conditional loading patch, however, so it is still worth committing IMHO - unless we decide to turn CSS caching on by default.
Note: The numbers report that they are very slightly slower, but I think this difference is probably a red herring and not statistically significant. I could run more tests if people thought this could be a real issue (or could think of a reason why there might be a real issue here).
Comment #14
Steven CreditAttribution: Steven commentedNote that the CSS files affected by this patch should not be cached by the CSS preprocessor when testing, though if you are using the front page to test, none of them will be included anyway.
The net result will be more HTTP requests for the front page, however the number cache misses globally will go down (because we use the slightly smaller cache more often).
Comment #15
m3avrck CreditAttribution: m3avrck commentedAlso of note, both of these patches when combined, will create *smaller* aggregated CSS files which will use up less bandwidth.
Comment #16
Owen Barton CreditAttribution: Owen Barton commented@Steven - thanks! I meant to explain this with my post, but ran out of time. Since I was hitting the front page we are simply seeing the result of a slightly smaller cache file (i.e. just cache hits). For other pages there is the delicate balance between hits and misses as you describe. As Robert suggests, we may eventually want to allow a contrib to provide an interface to configure cached vs. non-cached CSS (and even custom group them), since the balance will obviously vary between sites.
Another important note:
If we actually applied both patches, we would of course need to adjust the calls to drupal_add_css() that the 'less frequent' CSS files are not included in the cache ($cache = FALSE). Right now the caching would generate a new complete cache for each combination, resulting in unnecessary multiple downloads of the same CSS code (see http://drupal.org/node/98819#comment-161026 for a full explanation).
Comment #17
Steven CreditAttribution: Steven commentedI left the comment as is, because conditional CSS loading is really the most sensible behaviour. In the places where we don't conditionally include CSS, this is mentioned to apply for every request.
There is indeed a balance between caching and conditional loading, but all the CSS files that I moved are each only used on one or two pages, so there really is no argument there.
Commited to HEAD.
Comment #18
(not verified) CreditAttribution: commentedComment #19
moshe weitzman CreditAttribution: moshe weitzman commentedthe initial post here is the only description i know of clearly describes how to use the $preprocess parameter on drupal_add_css(). would be nice if we added very descriptive doxygen comment to that function.
Comment #20
m3avrck CreditAttribution: m3avrck commentedHere's a proposed patch to enhance the comments.
Comment #21
RobRoy CreditAttribution: RobRoy commentedThe Doxygen will need to be wrapped me thinks.
Comment #22
Dries CreditAttribution: Dries commentedI wrapped the lines and committed the patch ... BUT ... I think it needs more and better documentation.
1. The current phpDoc code assumes that a developer what the benefits of preprocessing are. I think that's problematic so I suggest that we add a short introduction so people can actually understand what this is about.
2. The documentation mentions what to preprocess but it doesn't provide a clue as what NOT to preprocess. All in all, it's not very clear.
Keeping the status as 'code needs work'.
Comment #23
m3avrck CreditAttribution: m3avrck commentedUpdated patch to clarify things.
Comment #24
m3avrck CreditAttribution: m3avrck commentedRevised edition, even clearer.
Comment #25
Steven CreditAttribution: Steven commentedAdded the line back in about themes and node styles, in a revised form. Should be very clear now :).
Committed to HEAD.
Comment #26
(not verified) CreditAttribution: commented