Seems like one of the best ways to reduce page load time for the non-uber-connected (dial-up folks etc) is to flat out reduce the amount of data, no rocket science there.
A great overall speed-up guide:
http://nadeausoftware.com/articles/2007/03/essential_steps_speed_drupal_...
Which links to a hi-return-on-investment section about selecting a smaller theme:
http://nadeausoftware.com/articles/2007/03/speed_drupal_web_site_using_s...
They also say don't waste your time with css optimizers that just minify or reduce token size (i.e. #CCC instead of #CCCCCC) cause they are not hi-return-on-investment. Makes sense.
But how about only sending the css selectors actually used for any given page load?
Found a web site that will go through your flat html and css, and get rid of (or at least tell you about) unused selectors. Handy little script, would be pretty easy to reproduce from scratch:
http://services.immike.net/css-checker/
Combined with the fact that usually a very small percentage of the selectors actually sent over the network are actually used for any given page load, wouldn't it be handy if drupal could strip the unused stuff on each page load?
Hopefully someone has already looked into this, to determine if it's a belly-flop of an idea, or if it has specific problems, or if the added processing time negates the reduced transmission time, etc???
If not, some thoughts: seems like it would need to be a two-pass system: 1st pass = drupal generates the page like normal, but instead of sending it over the line, it saves it to a file; 2nd pass = the unused-css-remover-script scans the html to see what selectors are used, then builds css files that only have the used selectors, then sends them instead.
Is there any facility in drupal for "intercepting" the output that would normally get sent to the client, to allow for a two-pass system like this??
Have any other investigations on these lines already taken place?
Thanks!
Comments
Comment #1
caver456 commentedmight have issues with dynamically generated content referencing selectors that got stripped out and never got sent?? how to work around that...
Comment #2
dman commentedI'd say it's not about the added processing time (which would be a speed bump) but about the inefficiency introduced by such an attempt.
It's possible that for any complex theme, maybe as little as half of all the available css is used on any single page render, but that css is then cached, and re-used on the next page load.
Attempting to run a process that custom-generates a stripped-down css that is uniquely tuned for each individual page necessarily results in sending a new css file for every page request.
So any optimization achieved in the first pageload is instantly overtaken in the second pageload, and badly inefficient over the course of any browsing session.
Actually, Drupals css cache does sorta do this anyway, by rolling a few different aggregations of css depending on which stylesheets are requested during the page build. It's possible to structure your theme to take advantage of that - by breaking your css into smaller stylesheets and only including them, via code, when needed.
Comment #3
caver456 commentedgreat clarification, thanks. Seems like making / using a small theme is the way to go.
Comment #4
caver456 commentedso, after thinking some more on it, maybe this would still be a good trade-off for a lot of users:
First impressions count. Most of the time, if you're targeting a new audience, they are looking at the home page, so you want max speed there. Child pages, maybe not as critical that they are fast. Repeat users might not mind quite so much on any of the pages. So, optimize the speed of the front page, and take the resulting small hit on other pages.
For the home page you could do the 2-pass css pruner to build a css that only has used selectors; load that with the front page, and then for all other pages let it use the full css which will take the load time hit that you mention of not being able to use the browser's cached css, but that won't matter so much on non-home-pages.
Then all other pages besides home page use the large version, just the home page uses the small version. That way your first load (assuming it's the home page) is fast, loads the small css; your second load (any other page) takes a bit longer to fetch the large css; then all subsequent page visits to any page on the site are fast assuming the browser has a normal css cache.
Even with bluemarine, the total css size of a home page load with just the basic system css (some various modules too but nothing big) comes to 31.8k. Right now for a similar flat non-drupal page that has a lot more content and looks a lot sharper, total css size is 8.8k - about a quarter as big. And I'd guess that only about 75% of the selectors in that non-drupal css are actually used.
Maybe a javascript pruner could be made too, with the same 'fast now, but no caching for later' tradeoff in mind. Plenty of overhead there.
Also there was another node somewhere on here, somebody did a test and only something like a tenth of all the loaded selectors were ever referenced. The css checker site is dead at the moment... should try a benchmark on drupal.org
Like you point out, structuring the theme to take advantage of different css aggregator versions might be pretty slick - but noting that of the 31.8k total, the theme css only accounts for 5.something k - that kind of indicates that optimizing the css will only save a small fraction of the total css.
Other thoughts?? I might try to plow ahead with this, but I don't have a lot of drupal api jedi knowledge, maybe it could even be done at the apache level?
Comment #5
caver456 commentedfound a good checker for firefox: http://www.sitepoint.com/dustmeselectors/
it can crawl a whole domain, or one page at a time.
So, on the basic druapl6.6 build with bluemarine, just the homepage: 108 used, 518 unused. So it uses about a sixth of the selectors, that is with Nice Menus enabled (32 used and 41 unused selectors from the 2 css files in that module)
On www.drupal.org:
181 used, 479 unused. Better, but it's still only using a quarter of all the selectors. Using a fudge factor for a sec to say all selectors are the same number of bytes, and using the fact that drupal.org loads a total 43,453 bytes of css, then getting rid of the 518 unused selectors would drop the total css size to 7497 bytes. That saves 35956 bytes, or, gzipped, probably about 12k bytes-ish; on a 56k dial-up modem, 9-bit bytes = 6222 bytes/sec so you're saving 2 full seconds on the front page load. Trivial difference for us uber-connected folks of course. Assuming that a lot of drupal sites are more complex than the drupal front page, you could scale that up a good bit.
Looking at yourshpere.com, the site profiled on drupal's home page today, there are 108 used and 1200 unused selectors. Less than 10% used.
Comment #6
caver456 commentedmaybe even better - still use the pruned css for the front page, but then make a set of files that is everything-else and load that for all other pages. Load them as two aggregated css files. That way if you ever go back to the front page again you don't have to reload, it's still valid from the cache; and on other pages, you don't have to reload all of the css - just the portion that wasn't used on the front page. You could probably use that tool to crawl your whole site and then the 'everything else' css file could be reduced a lot. Problems might come up when you have dynamically generated content that uses selectors that can't be detected ahead of time.
Still, reality check, anyone else think this idea has merit? Why or why not?
Comment #7
dman commentedThis does basically happen already -
IF you have broken your CSS down into sub-fules in the first place, and IF you have conditionally included them correctly using drupal_add_css() then only the invoked css gets aggreated when serving that page. It's all very clever.
But to make it work, you need to (at least) have your common.css and your front.css and your inner.css different soewhat.
Me, I go all the way and have a small css for each functional module sometimes. So only search pages get the search styling and only shopping cart pages get that styling, etc.
It's all then aggregated - and if the combo of files is the same - it gets sent back cached each time.
As with any caching vs pruning, there is a point where this trick will lose its efficiency.
Comment #8
caver456 commentedwill have to check later when in front of the server... but, sounds like this means you should be able to get pages served up with zero unused selectors?
Comment #9
dman commentedWell, in theory, for very simple, predictable, and non-dynamic sites, anyway.
But aiming at that as a target, or counting redundant selectors as a metric is likely to be counter-productive.
Comment #12
dpearcefl commentedAt this point, no new features will be added to D6 (or D7). If you think Drupal 8 needs this feature, please assign this issue to D8.
Comment #13
mdupontThis is clearly won't fix. D7 and later already improve on CSS aggregation and file size by making it smarter (lazy-load). Parsing CSS and generated HTML to prune unused selectors is simply a bad solution for a piece of software as complex as Drupal. It's better to leverage browser cache for CSS files and send compressed data.