Monday, June 14, 2010

Optimizing Site Performance

Purpose & Goal

Right now I’m running some tests through webpagetest.org to establish baselines for site performance. This week I hope to generate some CSS sprites for our common images and set up gzip and xpiration headers on the server. Those are the last 3 big things pointed out by YSlow (actually I’m using Google Page Speed, but YSlow is cool too). Hopefully soon I'll be able to run a new set of tests to compare to the baselines I'm running now.

Sampling

I'm trying to get a representative sample of of pages to test. The home page gets the most traffic, followed by the Current Students page. Pride Online has the most popular departmental home page, but it's utilitarian and not representative of other pages. That moves us down to the home page for the library.

Those 3 pages realistically represent only about 1/6 of our total pages, but it’s the most heavily visited 1/6. I could add one more test case and increase my representativeness to the majority of our pages, but we’d see diminishing returns in terms of traffic (only about 16% of total site traffic). Also, the remaining 5/6 of pages are mostly text heavy and image lite. The benefits to those pages will come from optimizing the overall site template, which will be partially reflected in the 3 chosen test cases.

Thanks to Google Analytics for the ability to access that info easily.

The Site Performance widget in Google Webmaster Tools says we gained nearly half a second in our rendering time around June 6th. That half second was enough to move us up from around the 73rd percentile to the 82nd percentile. I have no idea why we saw this jump in overall site performance but I'm glad to have it. Maybe we can even hold onto it.

The Results Are (Mostly) In

The 3rd and final test is still running as I type this. I’ll link out to the results as they become available.

Step 1: CSS Sprites

I'm using SpriteMe and my own best judgement to create some CSS sprites for commonly used background images. I'm assuming SpriteMe already has something like Smush.it built in.

Results

Ok, maybe I need to smush my sprites after all. I've added about 60kb to each page load while freeing up about 15 http requests. Smush.it can’t find anything to optimize, but the image optimizer in YSlow can shave about 5k total from 2 of the sprites. SpriteMe makes some less than optimal choices, even occasionally introducing bugs. But like all software it has to make certain assumptions and those don’t always pan out. I need to improve upon the default sprites while I’m at it. With these newly optimized sprites, let’s look at the trade offs.

jQuery UI Theme Icons for Play Controls

The first sprite I made was actually already made for me thanks to the jQuery UI Theme Roller. I used the blue and red versions of the UI sprite to replace the play control icons under the featured stories on the home page. The bad news is that almost doubles raw file size. In exchange, I pre-load a lot of UI elements that we're not currently using anywhere else. But I plan to make more use of the jQuery UI in the future. So I think this is a worthwhile change.

Baseline With Sprites Change
HTTP Requests 8 2 -6
Total Bytes 5,310 9,724 +4,414

Stripes Sprite

Both the header and the footer have background stripes that go all the way across the page. These were tiny, 1 pixel wide repeated images. But they were easy enough to combine.

Baseline With Sprites Change
HTTP Requests 2 1 -1
Total Bytes 905 122 -783

Image & Badge Drop Shadows

Some day I’ll be able to pull this off with pure CSS but in the meantime I’m stuck with background images. The one for the standard large image gets used on most pages. The one for smaller images at the same aspect ratio and the badges gets used fairly often too. The other 2 don’t see as much use. Some pages don’t use any of these, but the majority of our high traffic pages do. The image produced by SpriteMe left extra white space to the right of the 16px icons screwing up the placement. I had to edit the sprite in Photoshop. After that Smush.it actually did a better job compressing the resulting image than YSlow.

Baseline With Sprites Change
HTTP Requests 4 1 -3
Total Bytes 4,018 5,092 +1,074

Icons

The image produced by SpriteMe left extra white space to the right of the 16px icons screwing up the placement. I had to edit the sprite in Photoshop. After that Smush.it actually did a better job compressing the resulting image than YSlow.

Baseline With Sprites Change
HTTP Requests 18 1 -17
Total Bytes 14,589 13,677 -912

Logos

I combined our logo with the RODP logo that’s featured on every page. But that only nets us 1 less HTTP request and as you’ll see below costs us significant bytes due to the differences between .jpg and .png image formats.

Baseline With Sprites Change
HTTP Requests 2 1 -1
Total Bytes 12,270 39,876 +27,606

In a Nutshell

In a best case, a single page that references all these images (which is not terribly realistic), we’ve traded 28 fewer HTTP requests for 31,399 more bytes. That’s about 22 additional HTTP packets. Based on our test cases we’re realistically saving 12 – 15 HTTP requests per page. I know we’ll see a big pay off when we start effectively caching and gzipping this stuff, but I have to admit I’m disappointed with this first round of results.

Step 1.1: Learning from Mistakes

Ok, now I understand why the RODP logo was initially flagged as not to be included in a sprite due to being a jpg. I need to roll back the Logos sprite. I also need to alter the way we’re doing the random headers. We’ve been using something based on Dan Benjamin’s method from A List Apart but in terms of site performance it’s one of our slowest resources.

Results

We’re starting to see some minor pay off on the repeat viewing. Start render time on the current students page is now under 1 second. That’s pretty awesome, but we’ve still got work to do.

Step 2: GZip

I followed this guide as well as some official documentation from Microsoft. This online gzip test says we’re still not using compression but my web developer tool bar begs to differ.

Results

I just alphabetized the properties in each CSS declaration, primarily as a way to clean up my CSS and make it easier to manage. But Google’s Page Speed documentation points out the possibility of alphabetized CSS seeing more efficient gzip compression. Checking the alphabetized, minified, and gzipped version against the just minified and gzipped version with the Web Developer Toolbar (Information —> View Document Size) shows 8k either way. It doesn’t break it down into units any smaller than that. So maybe it saved me a few bytes, but not a kilobyte. So if we saw any gzip efficiency change from alphabetizing the CSS it’s less than 12.5%. If we assume rounding, then it’s less than 6.25%.

Step 3: Browser Cache

This one will be labor intensive. Before I put in the hours to set expiration dates and last modified dates for our various static resources I want to make sure they are properly optimized. We’ve covered CSS and JavaScript, but all the images that didn’t find their way into a sprite will need to be tested. I started this entry on Monday morning. It is now 1:30 on Wednesday. I this step will probably keep me busy the rest of today and a good chunk of tomorrow.

Ok, the clean up and optimization process has saved me about 10,000,000 bytes and 400 files. Gzip has been running for a full day and server’s resources don’t seem to be at all taxed. So far so good. Time to dive into Leverage Browser Caching.

I have a problem in that IIS reports the wrong last-modified date for certain files. Images seem fine. PHP files and other plain text files are not. Maybe I’m missing something in Dreamweaver. Our server also sends E-Tag hashes, which are newer than and in theory superior to last-modified headers. But they are also redundant together. E-Tags are apparently frowned upon when using multiple servers, such as a Content Delivery Network. All our content comes from just the one server so I don’t think it’s an issue with us. At worst we’s sending a single redundant HTTP response header.

So half the browser caching equation was essentially taken care of for us by IIS’s default settings. The other end of that equation is content expiration. I did not like that IIS just gave me a single check box to turn content expiration on. I wanted to be able to set it up by file extension. No such luck. I turned it on and did some testing with the Live HTTP Headers extension for Firefox. I was able to confirm that our php pages were not caching. Which is good. The last thing I want is someone to spend the next 6 months with an unchanging copy of our home page stuck in their browser cache. CSS and JavaScript files were caching, along with images. I already use version numbering on CSS, JavaScript, and image sprites. For the most part other images will have specific names anyway, so that’s good. Then I tested PDF files and did not like what I found.

I downloaded the PDF of our campus map and saw that the response headers told the browser to cache it for the next 6 months as well. We may need to update the map in the next 6 months. Maybe an office location changes to a new building or something. Worse still, many of our PDFs are newsletters. Monthly newsletters. Some offices have an archive listing years’ worth of issues while others just link to the most recent issue. In the latter case, I just save the new issue over the old issue on the server. With browsers caching these files, that wouldn’t work anymore. I’d have to give each issue a distinct name and adjust the HTML links to match, greatly complicating that process.

I rolled back those changes and tried setting up expiration on a directory by directory basis. So far I’ve only done /images/, /css/, and /js/. That covers the vast majority of our static files. Occasionally a directory off of root will have its own images directory. For now I’m leaving those alone. The handful of child directories inside /images/ also apparently did not did inherit the expiration settings of their parent folder.

The downside to all this experimenting is I have to bounce the IIS service for changes to take effect. That only takes about 8 seconds by my count, but I’t to think of someone submitting their final form for online orientation during that tiny window.

Page Speed still says we fail the browser cache check, but that’s due to the FEMA widget currently on our site. All resources being served from volstate.edu pass. So let’s run another batch of tests.

Results

Conclusion

This has been less a blog entry and more 4 days of notes to myself. Tomorrow I will try to recap the high points and present my data analysis.

Thursday, May 27, 2010

Why I Became a Web Geek

Amber asked for web dev origin stories. Here’s mine.

In the late 1940s, in the parts of the world that mattered, the end of WWII and the atomic bomb had brought the philosophical shift we now label post-modernism into the cultural mainstream. But in rural Tennessee, just down the road from the house I grew up in, my grandmother was being loaded onto a wagon to make the 5-mile trip “in to town.” My dad was a breech birth. Home birth had taken a turn for the worse and they needed to get a doctor involved. 40 years later my dad bought about 42 acres of farm land he occasionally plows with a team of mules just for the fun of it. This is just a hobby. He earned his living by dismantling nuclear weapons in the same facility that enriched the uranium for Fat Man and Little Boy.

This is the culture that produced me. About 5 years after I was born, satellite dishes cropped up like kudzu and started piping in the culture of the greater global village. It was amazing to watch the communication/information explosion transform this area.

For example, I found the CBC fascinating. I didn’t understand all the politics involved, but I knew they had a station that broadcast in French because that was the primary language in Quebec and that they had a station out of the North West Territories that featured a lot of content relevant to the First Peoples population there. I didn’t really see that same kind of diversity in American media. We had a lot of forced tolerance, but as we tried to blur lines based on gender and race we drew a greater distinction along class lines. The prevailing message was small towns and small people didn’t matter.

That could be do in part to the overall wealth worship fetish America had in the 80s. Compare Bill Cosby’s Dr. Cliff Huxtable to Fat Albert and the Cosby Kids from the decade before. I could (and still do) identify with the Cosby Kids despite geographic and racial boundaries. The Huxtable kids lived in a different world and that had very little to do with race. The Cosby Kids were a rare example of the not-rich and not-famous among pop culture characters; and closer to my day to day reality than Larry, Darryl and Darryl.

Getting rural areas and poor areas connected to the mass media grid gave these marginalized populations a window to the world. Getting connected to the internet would give us a voice. I wanted to be part of that.

Also, growing up on a “farm” affected my work ethic. I don’t miss manual labor under the blazing Tennessee sun. It was 80 degrees at 8 am today, and it’s not even truly summer yet. I feel most fulfilled if I can point to something at the end of the day and tell myself “I made that. That didn’t exist this morning and now it does.” Luckily I discovered that digital artifacts scratch that psychological itch just as effectively as a freshly planted row of crops. And with significantly less sun burn. If only typing code burned calories as effectively.

On top of that, I’ve been extremely lucky to be in the right place at the right time both educationally and professionally. Everything fell into place and I feel like I’ve found the perfect fit to do what I am.

My dad’s birth story sounds like a scene from a John Ford movie and I earn a living building communication systems out of ASCII characters. That’s a huge jump in just 2 generations. I have 2 kids of my own. I can’t imagine what their adult lives will be like.

Wednesday, May 19, 2010

Why I Stay Busy

On Friday @kdc retweeted something from @eduguru that sent me to @educheckup where I watched several videos, including the one on Miami-Dade where he mentions one of the few things they did right was pausing the story rotation when you click on a particular number to view a particular story.

Hmm… Do we do that?

No. Crap!

Ok. We can fix this. Our front page story rotation is based on the jQuery Cycle plugin from @malsup. Let’s start there.

New version is out as of last month. Might as well update. Should probably update my version of jQuery while I’m at it. We were still running 1.3. Update both scripts, re-minify our local file (plug in collection and custom code), and make sure nothing breaks.

Background color change on pager links to indicate currently active slide no longer works. Fire up FireBug. Eventually figure out activeSlide class is being applied to the li tags rather than the a tags inside the list items. For our implementation, that means the buttons were being flagged as active rather than the actual pager links. Created a custom function for updateActivePagerLink and all was well again.

Pshew! We’re in the clear. Checking the docs for Cycle. Source code reveals some new stuff that hasn't made its way onto the web documentation yet. Luckily Mike does a good job of commenting his code. The functionality I need isn’t built in, but a couple of options get close, such as pause and pauseOnPagerHover. I didn’t have either of those enabled, and pause has been built-in forever. Learning about one usability gotcha lead me to discover another.

At this point the easy, built in stuff is fixed. I turn my attention back to the initial issue. Following the conventions set up by 2 existing options within the plugin (pauseOnPagerHover and onPagerEvent) I create a new option called pauseOnPagerEvent and insert 2 new lines of code: an if statement to check the state of the new option and code lifted from pauseOnPagerHover to actually trigger the pause. Amazingly, this works on the first try.

I’m still not happy with that solution because I think a more elegant way to do it would involve onPagerEvent. I just spent another hour trying to figure out how to write my own function for that callback and failed yet again.

Sunday, May 02, 2010

Nashlantis

13 inches of rain so far with another 4 or 5 on the way.

The first photo here is me mowing our lawn a couple of summers ago. I'm a tiny little dot way back there, but you can see me if you squint. Take note of the big maple tree in the left of that image, the square of concrete to the right, and the row of 2 chestnut trees further back from that.

The 2nd photo is taken from right next to the big maple tree. The 3rd photo was taken from the concrete pad. Norma was ankle deep taking that photo, even with 5 inches of concrete between her and the ground. I'm standing about 3 paces beyond the edge of the concrete. You can see how deep it is out there.

The water has actually receded since we took those photos, about a foot by depth, several yards by horizontal progress. The concrete is no longer covered, for example. But there's another storm coming and the big one that dumped all that on top of us is technically still in the watershed the feeds into the lake near here. I'm hoping the worst is past us but I'm not going to count on that.

Thursday, April 22, 2010

Trusting Second Guessing My Gut

Since December I've been blogging on the new multi-user WordPress server at work, but it's currently down. Once we get it back I need to figure out how to automate cross posting. This blog space belongs to me, Derek Pennycuff. The blog on the WordPress server technically belongs to the webmaster position at Vol State. If/When I leave Vol State this will still be mine. The other will not. Echos of the knowledge management course.

The Web as She Is Used

Anyway, right now I'm struggling with some stuff that my gut tells me is right but I have no evidence to support or refute it. I'll try to lay it out here and cite some specific examples.

Email Links

The default behavior of a mailto link is to launch your default email client. Here at work that means Outlook. On a default install of Windows it usually means Outlook Express. Our audience does not make heavy use of email. Most of them probably don't have a default email client. What email they use is probably web based. Some browsers let us set our web based email client of choice as the default, but how many users actually bother to do that? Personally, I tend to use Firefox's "copy email address" feature from the contextual menu when I right click a mailto link. But I've spent enough time working tech support to know that most people don't know much about advanced features such as right click menus.

So I prefer to use the actual email address as the link text for mailto links. In addition to all of the above, it's a nice visual cue to differentiate a mailto link from a standard hyperlink.

Telephone Numbers

Making that change introduced a new problem. The easiest way for me to make this change is to add a column at the end of the table and move the email addresses there. But that puts the telephone numbers in the middle of the table. A mailto link is designed to be used online. A telephone number is non-interactive.

(Although I confirmed over the weekend that a Nexus One phone does recognize a well formed phone number on a web page and makes it behave a bit like a mailto link. Among our users that's an edge case and I have no confirmation of how other mobile devices treat phone numbers. [Several people have pointed out this is de facto standard behavior on smart phones.])

My gut tells me most people will either dial the number from the screen or write it down. Either will involve a bit of eye darting on and off the screen. Have you ever had to look away from one specific table cell and then locate it again quickly when you look back? Working in Excel can cause this just as much as tabular data on a website. It's not easy. I think putting the phone number at the end of the table helps with this a bit. This brings us to a 3rd iteration of a staff listing page.

Aesthetics vs. Usability

The list above shows a progression of what my gut tells me is increasing usability. However, my eye disagrees. Aesthetically I see each version as a step down from the previous. And without any real usability data I'm at a loss as to how to resolve this cognitive dissonance. I'm dealing with opinion either way. And since so much of the usability issue hinges on action taken off-line, picking up a phone and making a call, I can't think of a way to A/B test it.

I need to standardize these pages. Inconsistency kills both the usability and the aesthetics. All I've got to go by is my own conflicting opinions, so if you've got any ideas please share them.

Update

A strong preference for option #2 is already apparent among the people who have chimed in on this issue. Apparently I'm on to something with the email address as link text for mailto links but I'm probably over thinking the telephone number thing. I'm going to continue to mull this over mentally so feel free to continue to comment or email me if you're reading this now and have anything to share. But in the interest of productivity I'm going to make option #2 the standard format and get to work. :)