When I presented a Tech Talk Live session on Aikau a few weeks ago I mentioned that I would be looking at performance issues. We'd already had some excellent analysis done by Axel Faust and I had done some initial prototyping but I'd not had the opportunity to properly focus on it. The discussion also came up in the IRC channel so I figured that it was about time that I looked into it properly.
I set up a performance test page that consisted of a list with a single view containing a table of 100 rows, each with 50 cells containing a single Property (so over 10,000 widget instances) where each Property was assigned 50 custom properties. This is not typical for an Aikau page but was the scenario that Axel Faust had previously encountered and was designed to help flush out where performance issues were originating.
This test page took 55 seconds to load in debug mode (PLEASE NOTE: This is not representative of normal page rendering - a typical existing Aikau page in Share on the same system takes under 2 seconds to render in debug mode).
I spent a few days working through the troublesome areas and figuring out ways to drastically improve performance and have reached the point where once all the changes are integrated the performance test page load was reduced to under 15 seconds (a 366% improvement!) and under 10 seconds in production mode.
In a more realistic test (with 25 rows and 5 columns) the rendering went from around 2 seconds to under 1 (but still a 100% improvement):
The biggest challenge was is to be able to fold all these improvements back into the main code base in such a way that:
- All widgets work exactly as they did before
- Function signatures in modules don't change
- Execution code path doesn't change
It is the last 2 issues that are the most technically challenging but are important because we don't want to break any 3rd party extensions to existing widgets - and ensuring backwards compatibility of Aikau releases is our top priority!
The main changes that we've made can be summarized as follows:
- Building widget elements outside of the main document body (to avoid unnecessary browser rendering cycles)
- Building each widget DOM natively (rather than using the capabilities provided by dijit/_TemplatedMixin
- Bypassing unnecessary property handling in dijit/_WidgetBase
- Switching from using dojo/Deferred to native Promises
There is a new BaseWidget module that new widgets should use (and existing widgets will be converted to use) and a new ChildProcessing mix-in module that provides the function "createChildren" to replace "processWidgets".
We made some initial improvements in the 1.0.100 release but the main improvements will be in the 1.0.101 release. At the moment that changes are confined to the list rendering and I will be working through the remaining renderer widgets to update them to build their DOM natively. The next stage will be to convert the remaining widgets to take advantage of the new code.
There's still a way to go before all the whole Aikau codebase is updated to take advantage of these improvements but I thought it would be useful to let people know that it was something we were focusing on. It also further shows how we're able to make these sorts of improvements in Aikau without impacting any existing pages.