Monday, February 2, 2015

All about performance

My key request for the new system was good performance, no matter how achieved. I ended up with a single data table and a lot of cache around it. In Particle Database I have a search cache and coordinate cache in MySQL and each entity in JSON file. Entity contains all own attributes and relations, plus top 5 foreign relations. Each foreign relation type to each entity type has own JSON file cache.

MySQL

Indexing of MySQL tables is crucial, too little is bad and too much is bad as well. Best practice is to work tightly with DESCRIBE command and index columns used in JOIN and WHERE clauses.

There are several situations, where indexes can't be used. If you perform LIKE wildcard search, only col LIKE 'abc%' or col LIKE 'a%c%' use indexes, but col LIKE '%abc%' and col LIKE %abc' and col LIKE col2 don't - because of the way B-Tree index is designed. Or if you have to do a string operation before comparsion, e.g. UPPER(col) LIKE 'ABC', all rows has to be loaded. In such case consider adding a new column, with cached upper-case values.

PHP

I always checked what approach in PHP would be the fastest, especially for functionality in iteration (loop). I used simple script with a timer:

function test1()
{
 $t = microtime();
 while ($i < 1000) {
  // HERE TESTED CODE
  ++$i;
 }
 return microtime() - $t;
}

This way I tried the best approach for zero padding or type casting from string to number.

Server

Activate GZIP compression.

Encourage browser caching for static resources, using Expires header, Last-Modified date or ETags in the HTTP header.

Use different URL root for your resources, because browsers allows 4 to 8 simultaneous requests per domain, so using different domain will increase. And it will be cookie free (cookies are sent even for CSS or images). There are public CDN resources for widely used stuff, like jQuery or Bootstrap.

Client side

I also deeply enhanced client side for speed. First thing was to minimize external files - CSS and JS. Minimized code strips out all unnecessary stuff, like whitespaces or comments. Deep minification also rewrites variable and method names to contain as few characters as possible. Every byte counts.

Minimized file can be even smaller using GZip compression, widely supported by browsers. This way you can 58 kB file compress to 12 kB. But always keep the original file and never edit the minified one. The only downside is debug on production server, as you can`t get a proper line number, where problem occured, becuse everything may be on a single line.

I know JS libraries/frameworks, like jQuery or Dojo, are really handy and I like to use them, but only if I can't do it with pure ("Vanilla") JS. And it's valid for CSS boilerplate frameworks as well (Bootstrap, Foundation). Because it's universal, it contains a lot of code you never use, but browser parses it on every page load. If you want to use it, remove all unnecessary stuff, if you can (there are tools for that). jQuery is 85 kB of code just to make another code works. Using JS lib only for element addressing is pure blasphemy and I don't even want to comment using more than one JS library on single page.

Next step is reducing HTTP request count. I merged all JS into single file, the same with CSS. Then I created so calloed "CSS sprite" - all graphis (pictures), where possible, migrate into single PNG file ("spritesheet") and instead whole CSS background, specify just backgroud-position. You have to do it cleverly, because of mixing repeating (backgrounds) and non-repeating images (icons). I can't really recommend data URIs, because it can't be cached and therefore you will download it over and over again. The only time it was extremely hand was when I got the picture already as base64 from REST web service.

One of the most difficult steps was to minimize HTML code. Some browsers (with blue "E" in logo ;-) in some previous (but still used) versions can't handle innerHTML manipulations without another DIV, so every time I want to use element.innerHTML = someText, I have to specifiy extra inner DIV. There are certain situations, when the HTML code is really bloated and I can't do anything about it. If you write the code in HTML5, you can at least get rid of the ending slash on non-pair HTML tags, like INPUT or IMG.

It's also good to check for invalid stuff over time. You may not use the widget or page layout any more, but linked scripts and CSS may still be out there. Also check for unused JS functions/methods and unused CSS classes.

Webdesign

Responsive webdesign is neat, but you have to transfer all the code thru slow mobile connections (LTE or even 3G is still a dream in a lot of countries). Much better is to create lite mobile version. Don't immitate specific GUI (like iOS), it will look ridiculous on other platforms or after a platform redesign!

Always use proper image format - JPEG (JPG) for photos and PNG for everything else. Because JPEG is lossy format, you can tweak quality (from 0 being crappiest but smallest to 100 being best, but large) to achieve best ratio between quality and file size. You may need just to re-save JPEG photos, even it slightly impacts quality - but often greatly lowers file size. For both formats there are online and downloadable optimization tools, that help lower their file size.

Many effects, achieved by images in the past, are nowadays possible just by CSS effects. They are significantly smaller, of course, but increases CPU time required for their rendering. A lot of shadows, gradients and transitions may kill performance on (low end = mostly used) mobile devices.

Consider resizing large photos, especially for mobile websites. On the other hand, because of different dpi, you may need double the size of graphics elements based on image to look good or hi-res displays. The best option is to use SVG, but like CSS, it may also affect performance if not used wisely.

You may not load the content (photo, video...), if it's not yet visible. This technique is called "lazy loading". You can detect it's vertical position and compare it to the scrollbar position. When they meet, you can trigger download.

Try not to use webfonts on mobile version. The only sensible use there is for icon fonts, which are often better choice, than image icons - if you want different icon color on :active or even :hover, you just change CSS color property, not the whole image.

Social network buttons can be trouble too, especially when they are loaded directly from that site (with Like counters etc). If you want them, make it yourself and add them to your spritesheet / icon font.

Conclusion

Optimize your web for optimal performance is not easy task. It may feel it's better to start from scratch, but who says you have to adopt all of it? Try this and that now, but next time you build a new site, try to use as much as possible. Not only it will be faster for your readers or visitors, but you will decrease server load as a bonus. If you host your site in the cloud, it means you can reduce required hardware and lower the cost.

No comments:

Post a Comment