Sencha Touch apps, and hybrid HTML5 apps in general, are not known for their blazing speed. We strive, and often invest considerable time and energy, to make our Sencha Touch apps perform as well as possible. Ideally to parity with native. While this is not always possible for reasons inherent in the platform, there are a few “weird” tricks you can use to make sure it’s not your code making things slow down.
Have These Tools Gone Too Far?
Before we get to the goldmine of performance enhancing code, let’s go over some general tools in our arsenal we can use to diagnose performance issues and determine which approaches might be faster.
One tool that is very useful for any JS performance analysis is jsPerf. This website allows you to create isolated test cases to evaluate the performance of various strategies you might have that ultimately do same thing, but may perform wildly different. For example, this test compares the performance of the native Array method indexOf versus manually finding the index with a for loop. Unsurprisingly, the native method is faster than the unoptimized naïve implementation. But we didn’t know that before we started, so we’ve learned something. The site will also give you a performance breakdown from all the other people on various browsers that have run the test.
Chrome’s Developer Tools is also indispensable for performance testing. Not only can we do simple timing benchmarks using console.time, we can also manually create a new JS profile in the Profiles tab, or use another console method to do it in code: console.profile. We can also evaluate memory usage in the same Profiles tab, giving us valuable metrics into both CPU and memory performance. And the icing on the whole cake is that we can do all of the above magic on a real device. At least, devices running Android 4.4 and up using Chrome (or a version of Cordova that uses Chrome WebViews). See Google’s Remote Debugging guide for details.
Tips To Trim Your JS Run Time
When tuning your own JS, it is very often advisable to start targeting your slowest performance areas first. Using Chrome’s JS CPU profiler, you can see where your app is spending most of its time, and also which methods are “heaviest”, by using the dropdown menu next to the little eyeball. I typically like to use the Tree view when doing a first pass on optimization, but the Heavy view also has times when it is most useful.
JSON
One of the things I originally noticed taking a substantial percentage of time was Ext.encode and Ext.decode. These two methods are essentially the Sencha equivalent to JSON.stringify and JSON.parse, with some extra special Sencha magic to make it compatible with some older Sencha syntax. The good news is that you can disable the Sencha path and just use the native methods (where available). It’s not documented (as far as I can tell), but it’s relatively easy to figure out how to disable it. I typically add the following code in my index.html file before any Sencha includes.
if (typeof Ext === 'undefined') { Ext = {}; } Ext.USE_NATIVE_JSON = true;
This allows us to rely on the browser to make our JSON encoding and decoding as fast as possible. This tip is difficult to performance test, but looking at the before and after in your profiler should show a marked improvement. Depending on how much JSON your app processes, this can often make a noticeable impact.
LOOPS!
Oh the loops. Let’s take a look at a simple case comparing Sencha’s and JS callback loops to plain JS for loops. On my machine, Sencha’s and JS callback loops are about 50x slower than simple JS for loops, looping over the same array and doing the same operations on the values in the array. While the callback loops are often more readable, you have to determine if that performance hit is worth the gain in readability. In some scenarios it might make sense, especially where the loop body is the bottleneck.
Continuing with the above loop observation, if we recall that Sencha’s preferred method of iterating over a store of models is to use the store’s each method, we can also gain some performance by swapping it with a traditional JS for loop. I’ve created some comparison code in a jsPerf test case that overall does the same thing. However, our traditional JS for loop technique is faster. Depending on your loop body, you may or may not see a benefit in doing things this way.
Similar follow-on techniques that end up being significantly faster using JS techniques are filtering stores and sorting stores. In particular, it can be significantly faster to create a single filter function than use Sencha’s declarative syntax, but again at the cost of readability.
Lists and Things
When a Sencha List is backed by a Store, the list attaches some listeners to the store that make some of the magic list things possible. For example, when you add items to the store, the framework automatically adds items to the list UI. This is often very helpful, and indeed decreases some boilerplate we would otherwise have to write to update the list ourselves. However, if we’re adding many records to the store, one at at time in a loop, we’ll run into a performance issue where Sencha is adding items to the list UI as we’re adding items to the store. This can cause a major slowdown, depending on how many items you’re adding to the list. The solution is to build up an array of records to be added to the store, and use setData once, passing the array, to update the store and cause a single update to the UI. The code below should hep to illustrate:
var store = this.getList().getStore(), records = store.getRange(), newRecords = this.getNewRecords(); // some method to get new items to add. store.setData(records.concat(newBobs));
Similarly, it can be up to twice as fast to build your DOM strings manually (using string concatenation) than to use Sencha’s XTemplate class to build the structure. Consider this jsPerf test case. If we’re doing this in a loop over many items (say, for a list), the time savings can be significant.
One Weird Trick
The above tips are some specific things I’ve run across to help make Sencha perform a little better. However, the real “one weird trick” is learning to use tools like jsPerf and the Chrome Developer Tools, and in general having a performance tuned mindset when programming. These tools allow you to analyze your very specialized performance issues, and apply exactly the tweaks required to get your performance numbers in line. Likewise, as you uncover these performance issues, you’ll begin to consider the performance implications of the code you write, meaning you’ll have to do less profiling and post hoc optimization in the future. I hope that utilizing the above tips will help, but very likely you’ll uncover your own set of weird tricks, and hopefully Sencha devs won’t hate you for mangling their framework.
Thanks for the tips! are you on Twitter?