1. Measure everything
The first thing to do is gather a baseline of your application’s performance. Sometimes you’ll make a change to the site, thinking it will improve performance, but it will actually reduce performance. Although not quite a black art, ASP.NET performance tuning gives you unexpected results. Measuring performance should be a holistic exercise measuring server, JavaScript, and loading performance. Put away your stopwatch: there are some great tools for measuring performance such as Prefix.Prefix will allow you to highlight slow queries, large JavaScript files, and more. The measurements should give you an idea of which of these optimizations might help you the most. Make yourself a list, and order it from largest impact to smallest. You’ll often find that the items at the bottom of the list aren’t important enough to worry about until far down the road.
2. Pick the low-hanging fruit first
Once you have your list, then pick the item with the largest impact first. If you can show a large impact on your users right away, then it will give you some great political capital to continue optimizing and you’ll feel fantastic. Items which are global (JavaScript loading, CSS loading, and their ilk) will likely have a larger impact than changes to a single page.The rest of this blog post is arranged in a rough order of things that have a large impact to those with a small impact. Obviously, this will vary slightly from site to site so be sure to take it with a grain of salt.
3. Enable compression
The HTTP protocol is not a particularly efficient protocol and, by default, there is no compression of the content. Some web resources are already compressed, especially images, but HTML, CSS and JavaScript are typically transferred as text. Even the most archaic of browsers support compression of HTTP content using the gzip algorithm. The savings from using gzip compression on an HTML file are around two thirds; that is to say that a 100kb uncompressed file will end up being 33kb over the wire. This is a stunning savings!For the more adventurous there is an updated algorithm called Brotli, which is showing great promise and is quite well supported on modern browsers.
4. Reduce HTTP requests
Every time the browser needs to open a connection to the server there a tax that must be paid. This tax is in the form of TCP/IP connection overhead. This problem is especially noticeable in scenarios with high latency where it takes a long time to establish these new connections. Add to this the fact that browsers limit the number of requests they will make to a single server at once, and it becomes apparent that reducing the number of HTTP requests is a great optimization.Aside: Latency vs. bandwidth
When optimizing web page loading, it is important to understand the difference between latency and bandwidth. Let’s imagine that you have twenty donkeys you need to move from Banff to the Grand Canyon (two popular donkey hotspots). To get the donkeys moved as quickly as possible, you need to optimize two things: how many donkeys you move at once, and how long it takes to move a donkey.Bandwidth is how many donkeys you can move at once – in high bandwidth scenarios, you can move a lot of donkeys at once in your cattle hauler. In low bandwidth scenarios, you can only fit one donkey in the passenger seat of your 2001 Honda Civic, and that one donkey insists on listening to Destiny’s Child the whole way.
Latency is how quickly you drive between Banff and the Grand Canyon. High latency means there are lots of delays along the way, slowing down the transit time. The low-latency donkey move means that you drive straight through the night, and don’t stop at all at any of the donkeys’ favourite tourist sites. Ideally, you want to move as many donkeys at a one time and avoid any stops along the way.
Tooling
Depending on the type of the resource being requested from the server, there are a few different approaches to reducing the number of requests. For JavaScript, concatenating the scripts together into a single file using a tool like webpack, gulp or grunt can bundle together all the JavaScript into a single file. Equally, we can combine CSS files into a single file using tasks in the same build tools we use for JavaScript.For images, the problem is slightly more difficult. If the site uses a number of small images, then a technique called CSS Spriting can be used. In this, where we combine all the images into a single one and then use CSS offsets to shift the image around and show just the single sprite we want. There are some tools to make this process easier, but it tends to be quite manual. An alternative is to use an icon font.
This brings us to HTTP 2.
5. HTTP/2 over SSL
The new version of HTTP, HTTP/2, introduces a number of very useful optimizations. First, the compression we spoke of in #3 has been extended to also cover the protocol headers. More interestingly, the connection with the server can transfer more than one file at a go using a mechanism known as “pipelining”. This means that the reduction of HTTP requests by combining files is largely unnecessary.Most every browser has support for some version of HTTP/2 but ironically, the limitation tends to be more on the server side. For instance, at the time of writing, Azure Web Apps do not have support for HTTP/2.
The server can now make intelligent decisions about the page content and push resources down before they are even requested. So if the index page contains a JavaScript file that won’t be discovered until the browser has parsed the entire page, the server can now be instructed to start the transfer of the file before the browser has realized it needs it.
SSL is part of this tip because all browsers that support HTTP2 require that it be served over HTTPS.