Network Latency

To minimize application startup time, you need to bear in mind that browsers impose a limit on the number of concurrent network connections to any one domain.

This means that if many files are requested from one domain, once this quota is used, subsequent downloads will be queued, and they will be processed only when a connection slot becomes free. Newer browsers have higher limits, but this makes optimization all the more important in older, slower browsers.

The solution is to use the Sencha SDK tools to build a single, concatenated JavaScript file containing all the required JavaScript used by the application.

The SDK “create” command analyzes the application by loading the page, loading all files referenced by all “requires” and “uses” properties of class definitions. It then creates a single JavaScript file containing all required class definitions in the correct order.

Another method for minimizing network latency is enabling GZIP compression in the web server which serves the Ext JS page and its associated JavaScript and CSS.

CSS Processing

CSS selectors are matched right to left, following the DOM’s parentNode pointer.

This means that a selector like

.HeaderContainer .nav span

will be processed, and an attempt made to match it on every span in the document. The parentNode axis will be followed to try to find ancestor nodes with the two specified classes.

It’s much more efficient to use a single, identifying class name on elements to be styled.

JavaScript Execution

There are several points to bear in mind when optimizing performance of JavaScript code:

Avoid older or badly written JavaScript engines.

Optimize code which is repeated frequently.

Optimize code which is executed at render or layout time.

Better still, try not to execute any extra code at initial render or layout time.

Move invariant expressions outside of loops

Use for (…) rather than Ext.Array.each

If a function performs its task conditionally and is frequently called, check the condition outside the call, and only call it if necessary. (See calls to fireEvent in Ext JS codebase)

Setup and teardown of a call frame (apparatus needed to make a function call) is slow on bad JavaScript engines

Code optimization example

Imagine a statistical application which offers to perform operations on numerical grid columns. We can add items to the grid’s header column menu which perform the required operation upon whichever column they are invoked.

The handler must interrogate its context and operate on the currently active header:

The example at GitHub illustrates two ways of performing this operation. It will run in any of the SDK’s examples directories.

A first attempt at writing a function which totals the currently active column might look like this:

There are a couple of things wrong with this approach. Firstly, the use of Ext.each invokes the passed function for every record in the array. As we have seen, function setup can affect performance. Also, the result of the menuItem.up('dataIndex') expression is invariant, and should only be executed once, outside the loop.

This may seem a trivial difference, but the performance difference is significant.

In the table below, the calculation function was iterated 10000 times to provide a measurable time:

Browser

Bad

Good

Chrome

1700ms

10ms

IE9

18000ms

500ms

IE6

Gave up

532ms

As you can see, even without iteration, IE9 would have taken 1.8 milliseconds to perform a single operation using the suboptimal implementation.

Use Page Analyzer to measure performance

Page Analyzer is an example under the SDK’s example/page-analyzer directory. It runs a page from the same domain in a captive iframe, and instruments the captive Ext JS instance, so it can analyze layout performance and the performance of any method of any object.

If you’re using Chrome, start it from the command line using the –enable-benchmarking switch to enable microsecond timing accuracy.

To analyze the performance critical areas in the above example, switch to the “Performance” tab, and then in the lower left TabPanel, the “Accumulators” tab. Paste the following into the textarea:

Ensure that the 10000 times iteration is commented out in the two total calculation functions, so you get accurate performance state.

Then load the grid performance example in the Page Analyzer, request the total using “Get total in bad way”, and click the Page Analyzer’s “Update Stats” button at the top right.

Then click “Reset” to zero the accumulators, increment the “Build” counter, and request the total using “Get total in good way” and click the Page Analyzer’s “Update Stats” button.

In the “Grid” subtab of the “Performance” tab, you can then see the results of the two runs like this:

You can see that the hoisting of the ComponentQuery invariant expression out of the loop resulted in far fewer calls to the ComponentQuery methods.

Coalesce multiple layouts

Ext JS 4 performs an automatic layout run upon content change or size change. This means that an update of a Button’s text can cause a layout of the owning Toolbar (because the Button’s height may change); and then the toolbar’s owning Panel must be layed out (because the Toolbar’s height may change)

Because of this fact, it’s important to coalesce multiple layouts caused by several content or size changes into one run. The way we do this is as follows:

{
Ext.suspendLayouts();// batch of updates
Ext.resumeLayouts(true);}

The true parameter passed means that as well as re-enabling layouts, it flushes any queued up layout requirements to run at that point.

Reduce DOM burden

It’s important to reduce container/component nesting to as shallow a level as possible to avoid redundant layout runs and DOM reflows. These can be expensive.

Also, the principle to follow is to use the simplest Container or Layout which will do the required job.

A common example of over-nested components occurs when someone wants to put a Grid into a TabPanel. A naive implementor might use the following construct:

What that does is adds an ordinary Panel to the TabPanel as one of its child items. That Panel then contains a Grid. So that’s one extra layer of nesting which is doing nothing.

In fact, it’s breaking the operation of the TabPanel because that wrapping Panel has not been configured with a layout, so it does not perform any sizing on its child Grid, which means it will not adapt to the TabPabel’s size, and scroll within the TabPanel’s height.

The correct way to do this is as follows:

{
xtype:"tabpanel",
items:[{
title:"Results",
xtype:"grid",
...
}]}

Why is this principle important?

It’s important to try to keep the component tree (and therefore the DOM tree) as light as possible because, in Ext JS 4, more components are Containers that have child items, and they run their own layout managers. For example, a Panel’s Header is now a first class Container, and it may be configured with extra items that will appear in addition to the title text and any tools.

This is an overhead, but it offers much flexibility in terms of UI design to have a Header as an addressable Container within the hierarchy.

Also, in Ext JS 4, Components use Component Layout Managers to manage the size and position of their internal DOM structure instead of shoehorning all that processing into an onResize method as in Ext JS 3.x.

Visualize the component tree

When designing a UI, and thinking about the above principles, try to envision the UI as a tree structure. For example, a Viewport could be envisioned like this:

In order to render a component tree, it is walked twice.

In the first pass, beforeRender is called upon each component, and then getRenderTree yields a DomHelper config object which is converted to HTML markup and then added into the render buffer.

After the first pass, the HTML representing the whole tree is inserted into the document in one operation. This reduces the DOM processing involved in creating the application’s structure.

Then the tree is walked again. onRender is called which links up each Component with its associated DOM node. Then afterRender is called to finish the rendering process.

Avoid shrinkwrapping (auto sizing based on content) where possible.

Although Ext JS 4 offers the possibility of sizing Containers to fit around content (referred to in Ext JS as “shrinkwrapping”), this does impose the burden of running partial layouts, followed by a browser reflow to allow measurement of results, followed by a subsequent layout using the measured height or width.

Avoiding the need for a flush of calculated sizes to the DOM to allow measurement, will improve performance.

If these constraints are hit, then the entire layout has to be recalculated. If, for example, a flexed child of a box layout receives a calculated width less than its configured minWidth, it will switch from being flexed to being fixed at its minWidth, and the entire box layout will have to be recalculated.

A similar effect occurs when a box layout’s stretchMax configuration is used. All child Components switch to having a fixed perpendicular dimension (eg, height for an HBox layout), and the layout is recalculated.

Avoid postprocessing DOM of Component in afterRender

To avoid DOM reflows and repaints, try not to postprocess the DOM structure of a Component after it is rendered. Instead, use provided hooks to modify the configuration of the Component before its HTML is generated.

If it is important to change the DOM structure, then getRenderTree would be the best method to override.

Grid performance

If the dataset is very large, and the UI precludes use of a PagingToolbar, use buffered rendering commonly known as an “infinite grid”.

To implement this, all that is necessary is to configure your store with:

buffered:true,
pageSize:50,// Whatever works best given your network/DB latency
autoLoad:true

And continue to load and manipulate it as usual.

How it works

The grid now calculates how large the rendered table should be using the configuration of the PagingScroller which is the object that monitors scroll position. These configurations are as follows when scrolling downwards:

trailingBufferZone The number of records to keep rendered above the visible area.

leadingBufferZone The number of records to keep rendered below the visible area.

numFromEdge How close the edge of the table should come to the visible area before the table is refreshed further down.

The rendered table needs to contain enough rows to fill the height of the view plus the trailing buffer size plus leading buffer size plus (numFromEdge * 2) to create some scrollable overflow.

As the resulting table scrolls, it is monitored, and when the end of the table comes within numFromEdge rows of coming into view, the table is re-rendered using a block of data further down in the dataset. It is then positioned, so the visual position of the rows does not change.

In the best case scenario, the rows required for that re-rendering are already available in the page cache, and this operation is instantaneous and visually undetectable.

To configure these values, configure your grid with a verticalScroller:

This will mean that there will be 40 rows overflowing the visible area of the grid to provide smooth scrolling, and that the re-rendering will kick in as soon as the edge of the table is within 5 rows of being visible.

Keeping the pipeline full

Keeping the page cache primed to be ready with data for future scrolling is the job of the Store. The Store also has a trailingBufferZone and a leadingBufferZone.

Whenever rows are requested for a table re-render, after returning the requested rows, the Store then ensures that the range encompassed by those two zones around that requested data is in the cache by requesting them from the server if they are not already in the cache.

Those two zones have quite a large default value, but the developer can tune them to keep fewer or more pages in the pipeline.

Cache Misses

When “teleporting” way down into the dataset to a part for which there are definitely no cached pages, there will be a load mask and a delay because data will need to be requested from the server. However this case has been optimized too.

The page which contains the range required to create the visible area is requested first, and the table will be re-rendered as soon as it arrives. The surrounding pages covering the trailingBufferZone and leadingBufferZone are requested after the data that is really needed immediately by the UI.

Pruning the cache

By default, the cache has a calculated maximum size, beyond which, it will discard the Least Recently Used pages. This size is the number of pages spanned by the scroller’s leadingBufferZone plus visible size plus trailingBufferZone plus the Store’s configured purgePageCount. Increasing the purgePageCount means that once a page has been accessed, you are much more likely to be able to return to it quickly without triggering a server request later.

A purgePageCount value of zero means that the cache may grow without being pruned, and it may eventually grow to contain the whole dataset. This might actually be a very useful option when the dataset is not ridiculously large. Remember that humans cannot comprehend too much data, so multiple thousand row grids are not actually that useful – that probably means that they just got their filter conditions wrong and will need to re-query.

Pull the whole dataset client side!

One option if the dataset is not astronomical is to cache the entire dataset in the page map.

You can experiment with this option in the “Infinite Grid Tuner” which is in your SDK examples directory under examples/grid/infinite-scroll-grid-tuner.html.

If you set the “Store leadingBufferZone” to 50,000 and the purgePageCount to zero, this will have the desired effect.

The leadingBufferZone determines how far ahead the Store tries to keep the pipeline full. 50,000 means keep it very full!

A purgePageCount of zero means that the page map may grow without limit.

So when you then kick off the “Reload”, you can see the first, visually needed page being requested, and then rendered.

Then, you can see the Store diligently trying to fulfill that huge leadingBufferZone. Pretty soon, the whole dataset will be cached, and data access anywhere in the scrollable area will be instant.

It would be interesting to see the comparison for the code optimization example separately list the times for the Ext.Array.each vs for loop and menuItem.up(‘dataIndex’) changes. Based on my experiences with IE, I would guess the majority of the time was spent in repeating menuItem.up()? But just how bad is Ext.Array.each vs a for loop?
I created an array of 10000 strings, and iterated over them using Ext.Array.each vs for loop. In each case, the same arbitrary (unrelated to the array element) calculation was performed. I found that Ext.Array.each took consistently 1ms vs for loop took 20-27ms for IE9. hmm.

Great post but I couldn’t find this on the main blog page. Keep in mind that some of this isn’t documented in the ExtJS 4.1 docs. (Ext.suspendLayouts, Ext.resumeLayouts) We appreciate the informative transparency for the rendering and layouts that this article provides.

Thanks for the tips. It would be more helpful if there were some examples with measurements like you have for Ext.Array.each. That would help us validate your findings. The one measurement you have starts by saying : “Firstly, the use of Ext.each…” but jbo showed us that the real culprit is calling up()

My technique for speeding up complex tab and menu scenarios still works more or less on v4. Still 10x on first render. All versions of Ext are fast with the proper tuning. Thanks for the great article Animal!

What’s road map for performance rewrites guys? Will all of the createElement implementation be stripped out and replace with high performance methods like innerHTML? Are there any other areas of the framework that have questionable design decisions that will be reversed? Do you know where the performance bottle necks are? The Major issue IE 7 & 8, however all browsers are effected in 4.1

Holding my breath and I am looking forward to the post that says 4.x is now faster than 3.x :)

Perhaps we can see some of these programming techniques applied to the many examples already provided in the forums documenting the inadequate performance of v4 vs v3. Sencha can apply appropriate techniques and then show how the ‘tuned’ code compares in performance to v3.

@Animal, that’s quite a statement and very bad news if true. If the performance restrictions are now only down to a browsers javascript engine, that means there will be no further performance gains in the 4.x line? So when 5.x drops, which will undoubtedly be a larger, more feature rich framework, this will be slower that 4.x line?!

@Animal, you wrote “In my opinion, the performance restriction now is entirely imposed by the Javascript engine being used.”

Now this is certainly true, and useful if one desires to compare how different browsers perform given identical trees to render. However, that is not the point when discussing the performance of the framework as a whole. For example, you wrote “after the first pass…” but the performance question includes how long does this first pass take to complete as well as the nature of the tree produced and how it effects the performance of the javascript engines. The framework must adapt to the javascript engine behavior if necessary to give acceptable performance or the resulting RIA is unusable.

Last November, Mike Mullany wrote:
[/code]
“Our goal is to get the 4.x line to be faster than 3.4 at the very least for the common app cases. From looking at what people are posting on this thread and our internal testing, it looks like the consensus is that the current 4.1 preview is roughly 2x faster than 4.0 but also still about 2x slower than 3.4. We are working as aggressively as possible and will continue to release performance improvements in 4.1.x and 4.2 releases until we achieve our performance goals. Hence our recommendation. As an aside, we’ve integrated continuous performance benchmarking in our testing process as a result of our 4.0 performance experience.

Our initial comments that 4.x would be faster than 3.x was based on early benchmarks of our refactored layout and rendering pipeline – which we showed at SenchaCON 2010. Subsequent to SenchaCON 201, we integrated the new class system, added fairly involved theming code and also did a grid rewrite to support the new 4.x features. The combination of these, particularly for more complex layouts, reduced performance substantially from our early benchmarks, as all of our, and your, test benchmarks have shown.”[/code]https://www.sencha.com/forum/showthread.php?153008-4.x-Framework-performance-–-Request-for-an-Official-Statement/page11

So admittedly, the performance problems resulted from the design decisions of the framework itself and the goal is to improve performance until it is faster than v3.4. However, I’ve yet to see Sencha produce any benchmarks comparing v4.x to v3.4.

What do your performance benchmarks currently say about v4.1.x vs v3.4?

@MrSparks I think all the createElement stuff has been ripped out. The DOM is still more complex, even if inserted at once, than before. Having layout managers for the header and footer doesn’t help. @Rich02818 has a quote of Mike Mullany talking about performance improvements spanning across 4.1 and 4.2. I don’t have any more information on what that entails, but that doesn’t mean that that everything that can be done is finished.

Replacing functional constructs such as ForEach with purely imperative constructs is very short sighted solution and anti-pattern,its under assumption that dev have enough domain knowledge to optimize his code. For any sufficiently big projects it is rarely the case.

In reality whoever provides API always has more knowledge/control for optimization and can optimize calls in future in multiple ways: memoise futher calls (to ForEach as an example), execute in the most optimized fusion in current environment (no one stops you even from using continuations to simulate parallel/async execution, track current load or merge similar calls).

But if you go ahead and advice devs to go into hardcoded imperative constructs, you essentially cutting off all future optimization path, not only for but for project architects as well.

I just grepped the Ext source code for Ext.each: over 50 occurrences. If not using Ext.each is supposed to increase performance, then Sencha needs to eat their own dog food and get rid of their own internal usage. of it.

It seems that all of the JavaScript engines I tried my tabs still get needlessly relayed out using the code below. I haven’t tried it on Chrome Canary maybe it works there ;). (Run it 4.0.7 to get even more layouts)

One thing that will help do minimize the DOM burden, it will be allow text of both sides of a control. For instead, on a textfield sometimes I need a label (on the left) and a little explanation (on the right). I have to create a hbox container… if there was a property for the textfield, it will be much easier…

I don’t really get it… So we have Ext.each in the API, which I normally tell my developers to use since it looks more clean than using a for because it omits the index, and now if I understood it’s a performance problem.

With a burden of having to render consistent UI on browsers such as IE6, Ext JS 4.1 is probably optimized to the limits, leaving only little space for micro-optimizations. As Nige suggest, a dev is really in control of how fast the application is, given the vast number of the framework’s features.

I’m actually hoping to see Ext JS 5 ditch the old browsers and focus on ES5 and CSS3. Not only will it share more of the Sencha Touch 2 super-coolness, I can also foresee a great number of blog posts bragging how potent and fast the new framework had become.

Unfortunately the above code even with your heavy tuning is slower than a comparable Ext 3 example WITHOUT tuning – that’s the tragedy !. If you bring some Tab components in the game, than the difference is even more catastrophic :(.

I really don’t know what else should we shave off, optimize or cut, in order to bring our Ext 4.1 apps to the performance of the previous versions based on Ext 3 :( , difference mostly visible for IE8 and FF.
(Even if Chrome is faster, because of it’s auto-updates, Chrome is not used in most big companies, since they have their own internal auto-update mechanisms, or simply haven’t approved it’s usage).

And to the eat your own dogfood crowd, I still iterate with each rather than a for loop at times too, where is small sets and in a handler where performance is not important. Maybe the same is true in the Ext framework?
We’re talking about optimisation here, and you’d be nuts not to target your optimisations to where it matters, i.e. startup and rendering.

Regarding the JavaScript engine being the limiting factor. This is of course true, just as CPU, memory and a number of other platform/environment factors. However, the point Animal is missing (as are a few other people) is… The JavaScript engine is the limiting factor on a framework that has an excellent architecture… just as the JavaScript engine is a limiting factor on a framework that has poor architecture. With EXTJS 4.x we are talking about the latter. I’m shocked that the official line is to blame the end users JavaScript engine for problems EXTJS is exhibiting in the production environment, next you’ll be blaming network latency! Oh wait a second… Simply compare EXTJS 4.1 with 3.3 or even 2.3 and you’ll find even simple apps perform worse on all browsers under 4.1, don’t even get me started on how bad 4.1 is under IE7/8! Come-on Sencha, treat us like adults, please! Eat humble pie and give us a real breakdown and time-line on how you are going to sort this mess out.

Agree with MrSparks. Let’s not forget what really happened here. The performance problems are entirely due to design and implementation changes made by Sencha in v4. They have promised repeatedly to undue this damage, but to date have not done so.

“Subsequent to SenchaCON 201, we integrated the new class system, added fairly involved theming code and also did a grid rewrite to support the new 4.x features. The combination of these, particularly for more complex layouts, reduced performance substantially from our early benchmarks” – Sencha

A challenge to Sencha, there’s been a lot of talk concerning how to optimise extjs 4.1 applications. My simple app used to benchmark the framework contains no custom code, everything is out of the box. I can’t see how this can be optimised, especially as the same code runs much faster on 3.4.0 without any optimisations. If you can get this running under 4.1 as efficiently as it does under 3.4, myself and many other developer would be ecstatic!

In the same way that Sencha finally responded to the community cries about API Documentation (which is now much, MUCH better than what it used to be), I hope they will respond to this performance demand. Please remember that for us, your customers, migrating from ExtJS 3.x to 4.x was/is a huge effort and investment. If in top of that we find a big performance hits without responses, basically you are asking people to go somewhere else.

Nice article. We are seeing great results with our app, overview and screen shots at http://www.otusanalytics.com/ built with Ext JS 4 and Rails. We have a lot more chart widgets to implement but right now, it’s performance is pretty damn solid. Thanks for all your hard work!!

I have added twenty columns to the editor grid example and put twenty records in store, number editor is set to all cells. In IE8 in-place editing has significant delay (3-5 second) before render. Could you optimize that?

… but JSON parsing by default does NOT use the native parser, but instead it uses eval, which is both slower and less secure compared native parsing… I know this is done mostly for backward compatibility reasons, but perhaps it’s time to abandon this requirement.