Hacker News new | ask | show | jobs
by molf 4121 days ago
This article makes too many assumptions for my taste. Some other considerations that spring to mind:

- A sprite tends to have fewer bytes than separate images, because of image format overhead and better compression when combining similar things.

- The same may be true for zipped CSS/JS.

- What about CSS/JS/image parsing overhead?

- Even though HTTP2's request overhead is substantially less than HTTP1, fewer HTTP requests shouldn't make your site slower.

So the best practices will be outdated. Harmful? Not so sure.

5 comments

The other thing missing is how this interacts with CDNs. Concatenating my whole site's CSS into one file isn't just to decrease round trips, it's because there's only 1 URL to cache (both CDN and client browser) for every page.
That's actually harmful for cache performance. Ideally you want many small, granular caches that only expire when the specific content changes to get the highest cache hit rates: if you concatenate everything together, making changes to a single file requires redownloading the entire bundle.

(It's still faster under HTTP/1.1 to concatenate because of the overhead of making multiple HTTP requests and the parallelization limit, but one of the perf gains of HTTP2 is that since extra requests are cheap caches can be targeted and granular. Changes to a single script won't necessitate redownloading a giant concatenated bundle — you only need to download the single script that got updated.)

This is 100% right. One additional point about the importance of granularity to effective cache management: if you bundle rarely accessed resources together with frequently accessed resources in the same cache entry, those rarely accessed resources are taking up space that could be used for other data. You may actually be causing cache misses and additional network traffic!
That's only true if you're updating your assets faster than your cache TTL. I suppose if you're a continuous deployment shop that deploys to prod 25 times a day, that's a concern, but not if you deploy once a month.
Cache granularity absolutely matters regardless of your cache TTL, because as I mentioned in my other reply in this thread, by bundling rarely accessed and frequently accessed resources together you are pushing other frequently accessed resources out of the cache.
It's true that images may compress more efficiently in some cases when combined into a sprite sheet, but in my experience the scenario described by the article is very likely: your sprite sheets will end up containing images that the current page doesn't need. That has significant costs in terms of bandwidth usage, memory usage on the client side, and CPU / energy usage on the client side (for decoding unneeded image data).

Further, if any image on the sprite sheet is currently visible, the entire sprite sheet must remain in memory, when otherwise the browser could free the memory of all the non- visible images. And it may sometimes be necessary to use a much more expensive drawing path when drawing sprites to ensure that pixels from one image don't bleed into another image.

These negative effects will be felt most severely on resource-constrained mobile devices, where it matters most.

One should always measure when making decisions about performance, but in an HTTP2 world my recommendation would be to avoid sprites in most cases.

Sprite sheets contain images browser might need. Essentially it's a preloading technique, so you won't be slowed by waiting for something to load, even if it takes half a sec.

Also, most if not all browsers use GPU to render web pages. And spriting actually comes from gamedev/GPU world [1], where many textures are baked/combined into a big one because it's efficient from performance/memory layout POV.

A final thought, what about server IO becoming a bottleneck when it needs to read hundreds of small files from disk for each request?

[1] http://www.blackpawn.com/texts/lightmaps/

HTTP2 supports server push, which should serve your needs for preloading just fine. (And there are other approaches as well; that's just one example.)

Browsers generally use the GPU for compositing web pages, but the CPU still generally does most of the rendering, and that frequently includes at least some of the image rendering. That's not actually relevant, though. The problem is bleeding; see here [1] for an example of someone encountering it in a gamedev context.

So how do you solve bleeding? If you read the answer to that Stack Overflow question, you'll see that it involves correctly setting up the data in the texture to avoid the issue. The problem is that browsers cannot assume that you've done that. The workaround depends on which graphics backend (OpenGL, D2D, etc.) is in use, but it can sometimes involve making a temporary copy of the region of the texture you're going to draw, which is obviously expensive.

As for server IO being slowed down by reading hundreds of small files from disk, I'd expect a properly configured server to be serving those files from memory, either explicitly or implicitly through the OS's filesystem cache.

[1] http://stackoverflow.com/questions/7894802/opengl-texture-at...

Agreed, as someone who has done a significant amount of work to sprite our images and concat our js/css files this title, and most the content, scared me. The only thing I haven't pushed through is different domains for static assets (though I have them cached for a year with a query string as a cache-buster for when we deploy new js/css). None of this, in a HTTP2 context, seems like it would be extremely, or at all for that matter, harmful. Furthermore with our aggressive caching the request only needs to be made a single time (assuming the user hasn't cleared their cache).

I welcome HTTP2 but I'm not too sure on the timeline for rollout and if it will end up being an "IE6"-type thorn in my side. Even if we are split 50-50 between HTTP1 and HTTP2 it sounds like the best approach is keep doing what you are doing... Either way I don't think sprites/concating/minifying is going anywhere anytime soon.

I think if there's a significant enough uptake on HTTP/2, we may get to a point where we start feeling that it's not worth optimizing for HTTP/1.1 anymore. Obviously HTTP/1.1 will still just work, but the HTTP/2 approach _can_ potentially work much more elegantly, when all the tooling is in place, and I definitely believe it will be preferable.

I agree that the existing approaches are very much valid though, but I also look forward to not having to concatenate javascript files anymore. We've developed a lot of tooling to work around issues that stem from that, and it doesn't address the problem of needing a varying set of javascript files on a per-page basis easily.

I've said this before and I'm still not certain, but given that all browsers on desktops now auto update, and that browsers on phones and tablets have a short shelf life because those devices are far more disposable, I can't see how we're going to end up in an IE6 scenario.
A few reasons why concatenating CSS and JS might be harmful under HTTP2. Firstly, it means that the code must be downloaded serially. Given that the per-request overhead is minimal for HTTP2, it's more efficient to download the files separately in parallel. Second, in most cases you don't actually depend on all the CSS/JS for the user to start interacting with the page so keeping things separate can mean that the perceived response time is shorter. Finally, separate resources can result in less cache invalidation when assets are changed which could make a big difference for repeat visitors.
better compression when combining similar things.

Or, as I like to put it: "Birds of a feather compress better together."

Shouldn't a single request be multiplex-able, too? Isn't BitTorrent just one example of such a single-logical-request, multiple-connection protocol?