Thank you for reporting this bug. We will make it our priority to review this report.
  1. #1
    Sencha Premium Member
    Join Date
    Jan 2010
    Location
    Vancouver, Canada
    Posts
    137
    Vote Rating
    29
    bogc has a spectacular aura about bogc has a spectacular aura about

      1  

    Default TreePanel is too slow

    TreePanel is too slow


    Ext version tested:
    • Ext 3.4.0 (I used this as reference - in this version the TreePanel is very good)
    • Ext 4.1.1
    • Ext 4.2 beta

    Browser versions tested against:
    • Chrome 23.0.1271.97 m
    • IE9

    Description:
    • The TreePanel component is too slow for trees with with around 400 nodes and a depth level of 6-7. This manifests in two ways:
      • the tree rendering process is slow. This can be somehow alleviated using Ext.suspendLayouts()/Ext.resumeLayouts().
      • expanding collapsed nodes with lots of descendants and a high depth takes an unacceptable long time.
    I used as reference the ExtJs 3.4 implementation of the TreeGrid component (which is based on the TreePanel). Compared to 3.4 the TreePanel in version 4.x is much slower.

    Steps to reproduce the problem:
    • Unzip the TestExtJs4.zip file in a folder of your choice. The zip file contains a small app wired to work with 4.1.1. The app can be launched through the app.html file. The zip file also contains a data.json file with data. It is very important to use this data for testing. You might have to change the paths inside app.html file to point it to different versions of ExtJs 4.
    • Assuming that you made the app available under web server etc. etc, run app.html inside IE or Chrome or FireFox. Open the development tools specific to the browser in order to be able to see the console messages produced with Ext.log.
    • Click on the Load Tree button. The application will display how long it took to load the nodes. Make a note of that. On my computer and using IE9 it took ~ 14s to render the tree.
    • Now, collapse the first folder node (llprqpgrhgzqyzvi) and after that collapse the second folder node (yvcrjbllfshgtv)
    • Expand the second folder node (yvcrjbllfshgtv)
    • Check the time it took to expand the node. On my computer and using IE9, it took 8.405 seconds to expand the node!
    • Try the previous steps with ExtJs 4.2 and different browsers.
    • Now, unzip the TreeGridExtJs34.zip in the folder of your choice etc.
    • Open treegrid.html in the browser of your choice (the same one you used to test ExtJs 4) and open the development tools specific to the browser
    • Click the Load Tree node button and check the console that shows you how long it took to load the nodes. On my computer and using IE9 it took 1.1s to render the tree.
    • Now, collapse the first folder node (llprqpgrhgzqyzvi) and after that collapse the second folder node (yvcrjbllfshgtv)
    • Expand the second folder node (yvcrjbllfshgtv)
    • Check the time it took to expand the node. On my computer and using IE9 it took 0.038 seconds to expand the nodes.

    The result that was expected:
    • See the same performance level in Ext Js 4.x as is in Ext Js 3.4
    • Performance in Ext Js 4.2 to be better than ExtJs 4.1
    • In Ext Js 4.x expanding any node in the tree to be instantaneous for a reasonable amount of nodes (it's subjective but I think 500 nodes should be fine in my mind) that are underneath that node and no matter the depth level.

    The result that occurs instead:
    • Overall poor performance of the tree in ExtJs 4.x. Even when using Ext.suspendLayouts()/Ext.resumeLayouts() the performance is worse than it is in ExtJs 3.4 (by more than 100% worse).
    • The performance in Ext Js 4.2 is worse than it is in ExtJs 4.1!
    • Expanding the second node took an unacceptable amount of time (8s under IE). Business users won't be happy at all if they have to wait such a long time to expand nodes of trees already loaded in the memory.

    Test Case:

    I don't have a test case, but you could transform the sample I have provided in a test case by comparing the amount of time to load the tree against a hard value such as 1s or 2s.


    Operating System:
    • Windows 7 64 bit
    Attached Files

  2. #2
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,508
    Vote Rating
    57
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    In 4.2, the solution is to configure the TreePanel with

    Code:
    plugins: {
        ptype: 'bufferedrenderer'
    }

  3. #3
    Sencha Premium Member
    Join Date
    Jan 2010
    Location
    Vancouver, Canada
    Posts
    137
    Vote Rating
    29
    bogc has a spectacular aura about bogc has a spectacular aura about

      0  

    Default


    @animal:

    Do you have a sample, or point me to a sample where I can try this?

    I tried to add the plugin to the sample app that I attached to the bug but it ultimately gives an error:

    It fails at line 143 in BufferedRenderer.js because store is undefined and store is undefined because view.dataSource is undefined (init function line 128).

    Code:
            me.storeListeners = store.on({
    thanks

  4. #4
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,508
    Vote Rating
    57
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    There will be buffer rendered tree examples in 4.2 final.

    It does not work with the current beta code. It was some work to integrate it into the Tree!

  5. #5
    Sencha Premium Member
    Join Date
    Jan 2010
    Location
    Vancouver, Canada
    Posts
    137
    Vote Rating
    29
    bogc has a spectacular aura about bogc has a spectacular aura about

      0  

    Default


    I just wanted to point out another side effect, and I am not too sure that this is related to the treepanel specifically, but it can be reproduced in the context of this issue.

    The selection in a treepanel context menu menu becomes jittery when the tree contains lots of nodes, let's say 400. The phenomenon is more pronounced in slower browsers such as IE 9.

    Basically, when I hover the mouse above a menu item of a treepanel context menu, it takes a bit of time until the selection moves to that menu item. Usually the selection changes instantaneously.

    I modified the MyViewport.js file for you to try and see what I mean. To reproduce:

    1. Update the MyViewport.js based on the code in this post
    2. Load the page in IE 9 and click on the Load Tree button
    3. Right-click on the tree and hover the mouse above the menu items. On my computer the selection doesn't change immediately, it takes a delay. This never happened in 3.4. Lesser nodes in the tree makes this less noticeable. In my application it was very pronounced because I had two trees. In the context of my app the selection of a button menu items was also jittery (the button with the menu was placed above the trees). So, is this sensitive to the number of html elements in the page? I am not too sure but it looks that way.

    There is also some weirdness to it. If another window has the focus and the menu is still visible, if I hover the mouse above the menu the menu item selection changes instantaneously, as long as the browser window containing the page with page with the context menu doesn't have the focus.

    I am using Win 7 64 bit and I tested this with 4.1.1/4.2 (no noticeable difference between the two).

    Code:
    /*
     * File: app/view/MyViewport.js
     *
     * This file was generated by Sencha Architect version 2.1.0.
     * http://www.sencha.com/products/architect/
     *
     * This file requires use of the Ext JS 4.1.x library, under independent license.
     * License of Sencha Architect does not include license for Ext JS 4.1.x. For more
     * details see http://www.sencha.com/license or contact license@sencha.com.
     *
     * This file will be auto-generated each and everytime you save your project.
     *
     * Do NOT hand edit this file.
     */
    
    Ext.define('App.view.MyViewport', {
      extend: 'Ext.container.Viewport',
      
      requires: [
        'Ext.menu.Menu'
      ],
    
      layout: {
        type: 'fit'
      },
    
      initComponent: function () {
        var me = this;
    
        var contextMenu = Ext.create('Ext.menu.Menu', {
    
         items: [
                {
                  xtype: 'menuitem',
                  text: 'Menu Item 1'
                },
                {
                  xtype: 'menuitem',
                  text: 'Menu Item 2'
                },
                {
                  xtype: 'menuitem',
                  text: 'Menu Item 3'
                }
            ]
        });
    
        Ext.applyIf(me, {
          items: [
            {
              xtype: 'treepanel',
              height: 250,
              id: 'treePanel',
              itemId: 'treePanel',
              width: 400,
              title: 'My Tree Grid Panel',
              store: 'MyJsonTreeStore',
              rootVisible: false,
              useArrows: true,
              animate: false,
              viewConfig: {
    
              },
              
    
              listeners: {
                
                beforeitemexpand: function( node, eOpts )
                {
                  node.startTimeMs = new Date().getTime();
                  Ext.suspendLayouts();
                },
    
                afteritemexpand: function ( node, index, item, eOpts )
                {
                  Ext.resumeLayouts(true);
                  Ext.log({level: 'info'}, 'Expanded ' + node.data.text + ' in ' + (new Date().getTime() - node.startTimeMs) / 1000 + 'seconds')
                },
    
                itemcontextmenu: function (view, rec, node, index, e) 
                {
                  e.stopEvent();
                  contextMenu.showAt(e.getXY());
                  return false;
                }
                
              },
    
              columns: [
                {
                  xtype: 'treecolumn',
                  width: 319,
                  dataIndex: 'text',
                  text: 'Title'
                },
                {
                  xtype: 'gridcolumn',
                  width: 29,
                  text: ' '
                },
                {
                  xtype: 'gridcolumn',
                  dataIndex: 'TagInstance',
                  text: 'Tag Instance'
                },
                {
                  xtype: 'gridcolumn',
                  dataIndex: 'State',
                  text: 'State'
                },
                {
                  xtype: 'booleancolumn',
                  align: 'center',
                  dataIndex: 'HasNotes',
                  text: 'Has Notes'
                },
                {
                  xtype: 'booleancolumn',
                  align: 'center',
                  dataIndex: 'IsUsedByACanvas',
                  text: 'Used by a Canvas'
                }
              ],
              dockedItems: [
                {
                  xtype: 'toolbar',
                  dock: 'top',
                  items: [
                    {
                      xtype: 'button',
                      text: 'Load Tree',
                      listeners: {
                        click: {
                          fn: me.onLoadTreeButtonClick,
                          scope: me
                        }
                      }
                    }
                  ]
                }
              ]
            }
          ]
        });
    
        me.callParent(arguments);
      },
    
      onLoadTreeButtonClick: function (button, e, options) {
        //debugger;
        var treePanel = Ext.getCmp('treePanel');
        treePanel.el.mask('Loading...', 'x-mask-loading');
        Ext.Ajax.request(
        {
          url: 'data.json',
          method: 'GET',
          params: {},
    
          success: function onSuccess(result, request) {
            var nodes = Ext.JSON.decode(result.responseText).result;
            startTimeMs = new Date().getTime();
    
            treePanel.suspendEvents(false);
            Ext.suspendLayouts();
            // batch of updates
            treePanel.setRootNode(nodes[0]);
            //treePanel.getRootNode().expand(true);
            treePanel.el.unmask();
            Ext.resumeLayouts(true);
            //treePanel.getStore().resumeEvents();
            treePanel.resumeEvents();
            Ext.log({level: 'info'}, (new Date().getTime() - startTimeMs) / 1000 + 'seconds');
          },
          failure: function (result, request) {
            treePanel.el.unmask();
            Ext.net.DirectEvent.showFailure(result);
          }
        });
    
      }
    
    });

  6. #6
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,508
    Vote Rating
    57
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    Buffered rendering will help this.

    There will only be a few rows in the table. Just a few more (configurable using buffered renderer plugin's leadingBufferZone and trailingBufferZone) than are actually visible in the Panel body.

  7. #7
    Sencha Premium Member
    Join Date
    Jan 2010
    Location
    Vancouver, Canada
    Posts
    137
    Vote Rating
    29
    bogc has a spectacular aura about bogc has a spectacular aura about

      0  

    Default


    @Animal:

    Your answers got me worried, because what you are telling me is that 4.2 will not fix these underlying problems by addressing the original cause (i.e. the rendering code). Rather, it is my responsibility to improve the performance by employing ways to limit the number of html elements rendered, which is fine but unexpected for tree panels given the behaviour in the previous major version.

    The treepanel is a very complex component because it has to remember the expanded/collapsed state for each node, plus the child/parent relationships. If the tree gets buffered whose responsibility is to keep track of the expanded flag? Is the Treestore going to be stored in the memory entirely or only a range? In my app I also provide search functionality where users can search for nodes and the app selects the matching node. I assume that the library will provide the api to select a node is not visible.

    I really hope that this is going to work...

  8. #8
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,508
    Vote Rating
    57
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    The TreeStore handles all that.

    The hierarchy and expanded/collapsed state are tracked there.

    The flattened NodeStore is what the view is rendered from, and that doesn't care about all that.

    If you search the TreeStore, you will find whatever is in it.

    NodeStore is updated in response to node expand/collapse events, and the view updates in response to that.

  9. #9
    Sencha Premium Member
    Join Date
    Jan 2010
    Location
    Vancouver, Canada
    Posts
    137
    Vote Rating
    29
    bogc has a spectacular aura about bogc has a spectacular aura about

      0  

    Default


    @Animal: the performance is very good, however see this issue:
    http://www.sencha.com/forum/showthre...840#post931840

Thread Participants: 1