We’ve rolled out some changes on on our beta server (http://www.fastmail.fm/beta/) that should improve the performance of the new interface on all browsers.
We’ve tested these changes on Internet Explorer 6+, Firefox 3+, Opera 9+, Chrome 1+ and Safari 3+, and believe they should work on all those browsers. If you experience any problems, please report them in our forum at http://forum.fastmail.fm (include details of your browser, OS, any extensions/add-ons installed, any anti-virus software, etc). Also we’d appreciate feedback from people comparing subjectively if they notice a difference in the speed between the regular and beta interfaces.
For the interested, here’s a quick overview of what the main changes on beta are.
First a bit about browsers. There’s actually no explicit way to tell a browser to “draw the page”. Basically it’s up to the browser to decide when to do that, and browsers make the decision based on lots of things that aren’t exactly specified, such as how much html has been loaded, is there any js still running or about to run, etc. For instance, Opera say:
Browsers may choose to wait until the end of a script thread before reflowing to show the changes. Opera will wait until enough changes have been made, enough time has elapsed, or the end of the thread is reached. This means that if the changes happen quickly enough in the same thread, they may only produce one reflow. However, this cannot be relied on, especially considering the various different speeds of devices that Opera runs on.
That’s why some pages you’ll see render progressively bit by bit as more html loads or more js runs, and others will appear to just render in one go. Additionally, every browser does this differently, and will do it differently depending on the speed of the computer it’s running on, so just loading the same page in different browsers or on different machines will appear at different times, and with different numbers of “in between” steps.
Now a quick overview of things we already do that help improve performance:
- We set the correct cache headers on all css and js and image files, with a 1 week expiry time in the future. This means that after the browser loads a css or js file the first time, it’s easily cached for the rest of your browsing session, and most likely for the rest of the week
- We “minimise” all css and js files. This is a process that removes unneeded whitespace and comments from css and js files
- We gzip all css, js and html pages
Combined, these things significantly reduce the amount of data that needs to be transferred (eg raw js library = 98k, minimised = 64k, minimised + compressed = 20k), and the overall number of requests that need to be sent to FastMail servers. After the css, js and images are cached, basically each click on a link or button requires only one request to the server.
However, there’s still some improvements that can be made. On the current production pages, the main html blocks look something like this:
- link to load css
- link to load js
- html body
Because of the way browsers work, the browser must load and parse the js files before it can even start parsing the html body content (yes there’s the “defer” attribute, but not fully supported by all browsers). Fortunately those js files are then cached (we set a 1 week cache time on all js and css files, so effectively after the first load, they’re cached for a long time), but it still means on every page view, the entire js has to be parsed (>100k worth!) before the browser can do anything else.
After the js & html is parsed, an event is fired in our js framework called “ondomready”. That event then runs js code that “fixes up” the page in various ways, such as attaching keyboard shortcuts, hiding parts of the page (eg sidebar) if they’re supposed to be hidden, etc.
Now this was the way things were originally, and the problem was that the ondomready code would do things like resize or hide the sidebar if necessary, etc. This could cause bad page “jumping” as the initial page rendered with regular sidebar width, and then “jumped” as the js collapsed the sidebar off to the left or made it thinner/wider, etc. Again, because we can’t control when the browser redraws, the jumping may have been better or worse depending on browser, network speed, computer speed, etc. Basically, not easily controllable.
So our initial fix that we did for this while we were testing the new interface was to do some of the work server side. So basically the server looks at the cookie that has “is the sidebar hidden”, and if so, it serves up the html page in such a way that the sidebar is initially hidden, so the js doesn’t have to do it, so there’s no “jumping” because even if the browser renders the page before the “ondomready” event, the sidebar is collapsed based on the actual html page data.
This all works ok, but it’s messy having to split logic between the server and the js, and it doesn’t solve the large js parsing issue.
So the new approach currently on beta is like this:
- link to load css
- link to load minimal js
- html body
- script that dynamically loads remaining js
The minimal js file is a much smaller js file, and the only thing it does is use js to modify the actual css style data (this is before any of the html body is actually parsed!) so everything will display correctly even on the very first rendering by the browser, even if it’s before the ondomready event!
The rest of the js is then loaded at the bottom of the page, and performs it’s usual actions of adding keyboard shortcuts, hiding other stuff on the page, etc.
Ideally, if we could control things explicitly, what we’d like the browser to do is this:
- load + parse css
- load + parse + execute minimal js
- load + parse html body
- render and display page
- load + parse + execute remaining js
The net result of all would be that:
- For us it’s nice that the html generated for the page is always the same now, it doesn’t have to be modified on the server depending on the users cookies. All that is handled client side now
- The client side js now handles even more things to avoid “jumps”. The following things are set by the js in the page header:
- the sidebar collapsed/expanded status
- the sidebar width
- the expanded/collapsed setting of sidebar sections (mailbox/message read screens only)
- saved searches collapsed/expanded status
- the visibility of the “redirect to” textbox in the action bar
We can also add more in the future relatively easily as well.
- The browser has to parse and execute a lot less js before the browser we can start parsing and rendering the html. Since we can’t control exactly when the browser actually first renders the page, this may or may not speed up the first “display” of the page, but hopefully it will in most cases cause the page to display faster.
There’s one other completely unrelated thing as well, we’ve changed from FCKEditor 2.6.4 -> CKEditor 3.0. CKEditor 3.0 isn’t quite released yet (we’re using an SVN version), but hopefully will be in the next few weeks or so. Basically it’s a complete refresh of FCKEditor.
Long term, we’re looking into creating a complete AJAX solution for the main mailbox/message read/compose pages, which would completely remove the need for reloading the page at all, but for now, this should be a noticeable performance improvement across a wide range of browser without any fundamental internal architecture changes and remaining backward compatiable with existing style sheets and tweaks.
Some other people have asked what effect the CSS has on the page rendering. The google page speed plugin mentioned that we were using a few very inefficient CSS selectors, so we’ve fixed those up on beta as well. We’ve done some testing, and from what we can tell, the difference between our current CSS and a base case of no-CSS at all is actually fairly minimal, the main performance bottleneck is the total number of mailbox rows to display, and is fundamentally about how fast the browser can parse and layout a table with a large number of rows. We recommend that people stick to the default of 20 or 25 display rows, rather than setting every folder to 100 rows, because that will noticeably slow down the page loading and display.