[Suggestion] Client-side Performance Optimization

"Mark"

Member
Just checking out the header, and I see a problem with the CSS and JavaScript includes: they use request variables. Many proxy servers won't cache anything with a request variable, so this should be fixed. The best solution is to put the versioning in the path, and change the path or filename on update. Also, what's that version tag doing on jQuery? It doesn't need it.

Another tweak to consider is moving linked style sheets, then linked scripts, to the top of head. You really want the linked content to be within the first 900 bytes or so. I'll explain why.

TCP uses a slow-start algorithm before ramping up transmission speed as ACK packets are returned. That means the second packet won't be sent until the first one is acknowledged. On broadband, that's about a 100 ms delay. On dialup, it's about a 300 ms delay. After the first acknowledgement is received, the server can optimistically send more than one packet based on the estimated transmission speed. But the first round trip is only ever one packet.

Now, if there's nothing in that first packet for a browser to do, it must wait 100 to 300 ms for the next segment of data to arrive to do anything. But if there's a link to something else it can start downloading (style sheets, for instance) it can start those download requests immediately. This shaves off an entire round trip time before those secondary requests start arriving! It's a fantastically easy way to make your pages 0.1 to 0.3 seconds faster!

TCP packets are usually limited to 1500 bytes max. Things like PPP or PPPoE add overhead, reducing the max size. Still, 1400 bytes is safe to assume. So why do you want thing inside the first 900 bytes or so? HTTP overhead. Lets look at a typical request for the forums here, broken apart at 1400 bytes: http://xenforo.com/community/forums/suggestions-tweaks-small-features.18/

Code:
HTTP/1.1 200 OK
Date: Sun, 12 Sep 2010 13:42:27 GMT
Server: Apache/2.2.3 (Red Hat)
X-Powered-By: PHP/5.3.2
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-control: private, max-age=0
Set-Cookie: xf_session=0ab87339101c9fcba0f69312acbd239c; path=/; httponly
Last-Modified: Sun, 12 Sep 2010 13:42:27 GMT
Content-Length: 56131
Connection: close
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<html id="XenForo" class="Public LoggedOut">
<head>
	<meta charset="utf-8" />
	<base href="http://xenforo.com/community/" />

	<title>Suggestions: Tweaks &amp; Small Features | XenForo Community</title>
	<meta property="og:site_name" content="XenForo Community" />

	<link rel="canonical" href="http://xenforo.com/community/forums/suggestions-tweaks-small-features.18/" />

	<link rel="alternate" type="application/rss+xml" title="RSS feed for Suggestions: Tweaks &amp; Small Features" href="forums/suggestions-tweaks-small-features.18/index.rss" />

	<meta name="description" content="If you'd like to suggest a tweak, adjustment, or small feature change, post it here. If you are suggesting a standalone feature, it may be better in this forum" />

	<link rel="stylesheet" type="text/css" href="css.php?css=xenforo,form,public&amp;style=1&amp;d=1284256636" />
	<link rel="stylesheet" type="text/css" href="css.php?css=discussion_list,login_bar&amp;style=1&amp;d=1284256636" />


	<script type="text/java

Whoops. What about those JavaScript files?

HTTP 1.1 only allows for 2 parallel requests per host name (FF2, IE6/7), but modern browsers will do 4 parallel requests now (Safari 3/4, Chrome 3, Opera). Some will do 6 (IE 8, FF3). And if the files are on other hosts, like the jQuery include, they can be downloaded, too.

Thankfully we did get the two CSS includes, but just barely! The CSS files were almost split off into the next packet. If the description or anything else were 18 bytes longer, the second CSS tag wouldn't have been complete.

But we still have room for at least one more download in all modern browsers...

The next packet will include the following (and part of the body, which is irrelevant for this discussion):

Code:
script">
	<!--
	var _gaq = [['_setAccount', 'UA-10855523-4'], ['_trackPageview']];
	!function(d, t)
	{
		var g = d.createElement(t),
			s = d.getElementsByTagName(t)[0];
		g.async = true;
		g.src = ('https:' == d.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
		s.parentNode.insertBefore(g, s);
	}
	(document, 'script');
	//-->
	</script>
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js?_v=2dd2572b"></script>
<script type="text/javascript" src="js/jquery/jquery.xenforo.rollup.js?_v=2dd2572b"></script>
<script type="text/javascript" src="js/xenforo/xenforo.js?_v=2dd2572b"></script>
<script type="text/javascript" src="js/xenforo/discussion_list.js?_v=2dd2572b"></script>



</head>

Here we get the rest of the includes. The browser is currently downloading just three files (and probably still waiting for the CSS file requests to start coming in). Modern browsers will thus start more requests. IE8/FF3 will start the next 3. The others will start the first 2. None will start the last download, since it would exceed the per-host limit (remember we're still downloading the index page and two CSS files).

Because the last JavaScript won't start downloading until something else has completed, it would have made more sense to have started earlier. Worse, because we're downloading JavaScripts, the next downloads won't start until after the scripts have finished executing. So we could be waiting a while, especially if that DNS lookup to ajax.googleapis.com stalls (I would recommend self hosting jQuery to avoid that issue).

A further delay is that big fat jQuery script being first. Now this probably has to be first for dependency reasons. None-the-less, it is blocking any further downloads of images referenced in the HTML or CSS. Wouldn't it have been nice to start it 100 to 300 ms earlier, and have it fully parsed and executed 100 to 300 ms earlier, too? :)


I would strongly consider hosting the CSS files on a different domain (one that doesn't set cookies), along with all the images they reference (make this an option in the forum config). E.g. static.xenforo.net

I would also suggest having the ability to split the avatars over two or three domains (modulo the user id for consistent distribution and browser caching). E.g. av{1,2,3}.xenforo.net

I would further consider combining some of the JavaScript files, to get the code downloaded sooner and parsed sooner. If the two CSS files are on a separate domain, three JavaScript files will start downloading immediately. I would leave them on the same domain, because you don't want a DNS delay, especially for JavaScripts, as they block everything else. Props for putting them after the CSS links.

I would also consider moving the images referenced in your CSS to the top of the CSS files. If re-ordering the CSS doesn't make sense, just apply them to dummy id's or classes at the top of the file. You want all the images (and fonts, and imported style sheets) to be referenced in the first 900 bytes.

And yes, I am a website performance nut, from the server metal to the user eyeball. ;D
 
Upvote 10
Please remember that this is an alpha version. I'm quite sure that most, if not all, of these issues will be cleaned up prior to release. ;)
 
Please remember that this is an alpha version. I'm quite sure that most, if not all, of these issues will be cleaned up prior to release. ;)

Of course. I'm merely pointing it out now, because it should eventually be done. That being said, it's a 30 second fix that I spent an hour writing about. I'll probably post it on my (still not created) performance blog later.


There are actually further ways to optimize this, but they may involve more work. One is forcing a PHP buffer flush as soon as the CSS and JS includes are output. As these shouldn't (likely) depend on which page is being loaded, they can be output straight away. This speeds up the aforementioned download process. The client then has something to chew on almost immediately, and begin downloads very quickly. The server can then continue executing the rest of the page, determining the page title, the description tag, the canonical tag, etc., while the client has already started downloading the CSS (and the images therein) and the JS. So many software projects make the client wait for anything until after all the output data has been generated. That's a big no-no when it comes to web performance.

Check out one project I'm working on that implements the techniques I've talked about: http://whotheman.com/ . It's a fully interactive video site, with 200,000 pieces of content, running on a single box. It can pump out ~28 pages per second. But more importantly, look at how fast it appears to load. Even though it takes over 2 seconds to complete (the ads take over half the time), it's designed to be lightning quick for the user.
 
Check out one project I'm working on that implements the techniques I've talked about: http://whotheman.com/ . It's a fully interactive video site, with 200,000 pieces of content, running on a single box. It can pump out ~28 pages per second. But more importantly, look at how fast it appears to load. Even though it takes over 2 seconds to complete (the ads take over half the time), it's designed to be lightening quick for the user.
Loads very fast. What kind of traffic does it get?
 
Loads very fast. What kind of traffic does it get?

Right now, not much. It didn't take off as intended, and we're switching business models. The code base may soon be getting millions of hits per day, and it's ready to handle that without slowdowns.
 
Top Bottom