Gelmiş geçmiş en büyük porno sitemiz olan 2pe de her zaman en kaliteli pornoları sunmayı hedefledik. Diğer video sitemiz olan vuam da ise hd porno ağırlıklı çalışmalara başladık.

  1. #1
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Portland, OR
    Posts
    61
    Vote Rating
    0
    MahlerFreak is on a distinguished road

      0  

    Default List performance consolidation thread

    List performance consolidation thread


    I'm building a music control application, consisting of a dedicated LAN-only HTTP server, and Sencha-based clients. The nature of this application means that I must deal gracefully with lists of data potentially in the 10,000 to 100,000 item range, with a high degree of interactivity. There have been several threads commenting that list performance, especially scrolling performance, degrades badly as the number of items goes up. So I'm starting this thread as a way of consolidating tips and strategies to dealing with large lists.

    Two general approaches to take are 1) minimize the number of items displayed, and/or 2) investigate alternative scrolling algorithms.

    The first approach is to minimize the number of items actually displayed in the list at any one time. My experiments show that scrolling performance is almost entirely a function of the number of nodes actually displayed in the scrolling div. So if we hide the list elements that are well off the top or bottom of the list viewport, and then replace the space those items take with a blank spacer div, we'll maintain scrolling performance for the items actually in front of the user. Of course, this means we have to update the items that are hidden/displayed, and the size of those top and bottom spacers, when the list gets scrolled.

    So the markup structure inside the list component will look like:

    Code:
    <div class="top-list-spacer" height="(number of hidden items at top) * (height of list item)"></div>
    <div class="x-list-item" style="display:none">/*hidden list item*/</div>
    /*repeat hidden list items at top of list*/
    <div class="x-list-item" style="display:block">/*visible list item*/</div>
    /*repeat visible list items*/
    <div class="x-list-item" style="display:none">/*hidden list item*/</div>
    /*repeat hidden list items at top of list*/
    <div class="bottom-list-spacer" height="(number of hidden items at bottom) * (height of list item)"></div>
    How do the spacer divs get there? Well, either by modifying the main list template (not the item template), or by injecting them after rendering, using a render listener. In any case, to keep this structure up to date, we have to know when the user has scrolled above the top or below the bottom of our displayed items (they have scrolled into the areas of the blank spacer divs). This means listening for "scrollend" events from the list scroller. Code outline:

    Code:
    var myList = new Ext.List({...});
    myList.mon(myList.scroller,
            {
               scrollend: handleScrollEnd,
               scope:myList
            }
    );
    var handleScrollEnd = function() {
        // Note: "this" inside this function refers to my list component
        // Compute how many list items are past the top of the list viewport
            .................
        // Hide items that are too far past top of list viewport
            .................
        // Set height of top spacer div to match height of top list items just hidden
            .................
        // Compute how many list items are past the bottom of the list viewport
            .................
        // Hide items that are too far past the bottom of list viewport
            .................
        // Set height of bottom spacer div to match height of bottom list items just hidden
            .................
    }
    There are obvious variations and extensions of this first strategy that could be done. For instance, instead of rendering the whole list up front and then hiding/displaying, we could render just the items that are visible at any one time, and even request just those items from the server. This is what I have to do, to minimize data transfer overhead. Also, instead of rendering single blank spacer divs, you could render several divs that provide "hints" to the user as to where they are in the blank parts of the list - which is what I do in my alphabetically grouped lists.

    If people are interested, and willing to contribute their own ideas and feedback, I'm willing to spend a couple of days creating more generic working versions of this kind of list extension.

    The second strategy, investigating other kinds of scrolling algorithms, is not something I've done yet. I know the Sencha team has already put in quite a bit of time here. But I'd also say, check out the "List Performance" example on jquerymobile.com:

    http://jquerymobile.com/demos/1.0a2/...rformance.html

    The rendering is very slow. The scrolling is very responsive, but very unnatural. There are obviously some tradeoffs around this, and some more investigation might lead to a set of tradeoffs that work better for large lists.

    EDIT: I screwed up. The above jquery mobile example does not do local scrolling at all - the "scrolling" is simply native scrolling of the entire document, not an element within the document. So there is very likely no magic to be found in changing the scrolling algorithm.

  2. #2
    Touch Premium Member
    Join Date
    Apr 2007
    Posts
    139
    Vote Rating
    0
    mherger is on a distinguished road

      0  

    Default


    Very interesting. Today, while waiting at the train station, I've been chewing this very issue, and I had the same idea (no tests yet, just been thinking). Do you have some working code?

    One other approach to reduce load time is dynamically rendering items. It's helping a lot, but not enough for my taste. See http://www.sencha.com/forum/showthre...877#post547877. What I'm doing there is render empty items of a fixed height, but render every item. Then render the full content in a sliding window of items while scrolling. It greatly helped performance, but still won't scale to > 1000 items, as even with a simplified item structure the DOM becomes too large.

    Maybe some of my code might become useful to your approach too.
    HTC Desire - Android: 2.2 - Kernel: 2.6.32.15-gf5a401c - Build: 2.29.405.2 - WebKit: 3.1

  3. #3
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Portland, OR
    Posts
    61
    Vote Rating
    0
    MahlerFreak is on a distinguished road

      0  

    Default


    mherger,

    Yes, I'm working right now on a custom component grouped list that dynamically renders. If everything goes well, it should be ready in a couple of hours. We can exchange code when I post it up As you have found, I've found that simply rendering empty items doesn't help the performance as much as I'd like. Instead, I'm using a single "proxy" item to represent a group that isn't rendered - you'll see what I mean when I post my extension later.

    Scott

  4. #4
    Sencha User
    Join Date
    Dec 2010
    Posts
    48
    Vote Rating
    0
    Shijutv is on a distinguished road

      0  

    Default


    Hi MahlerFreak,
    I have a list component with about 1000 items . Can you help me to implement the above things ?

    iGlossary.views.AlphabeticList = Ext.extend(Ext.List, {
    singleSelect: true,
    grouped: true,
    indexBar: true,
    onItemDisclosure:true,
    initComponent: function() {
    this.store = iGlossary.stores.AlphabeticItems;
    this.itemTpl = '{term}';
    iGlossary.views.AlphabeticList.superclass.initComponent.call(this);
    },
    show: function(){
    this.scroller.on('scrollend', function (comp, offset) {

    });
    },
    hide: function(){
    this.scroller.un('scrollend', function (comp, offset) {

    });
    }
    });

    Shiju

  5. #5
    Ext User
    Join Date
    Jul 2010
    Posts
    57
    Vote Rating
    1
    konki_vienna is on a distinguished road

      0  

    Default


    I am very much curious on MahlerFreak's solution and hope that it can be applied to SenchaTouch-Lists as well... Good luck with that! It is great, that you will share the fruits of your brain with the community!

  6. #6
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Portland, OR
    Posts
    61
    Vote Rating
    0
    MahlerFreak is on a distinguished road

      0  

    Default


    OK, here is a custom grouped list that manages scroll performance by rendering only one group of items at a time. The other groups are represented by a single proxy item. If you scroll or use the index bar to go to a new group, that group becomes the rendered group. If you're scrolling and tap on one of the proxy groups to stop the scroll, it becomes the rendered group. You can obviously scrub up and down on the index bar to get to any one particular group.

    The performance with the provided example is quite good on iPad, OK on iPod touch, not too great on Samsung Galaxy S, brilliant on desktop Chrome The code below is quite functional, but still has some things to be finished (I think item selection probably won't work correctly as is - it will give the wrong record/index) and some other bugs to work out. But I'm not going to spend much more time on this until I get some feedback from folks - does this get enough performance? Is the user interaction acceptable, or not? It certainly is a non-standard way to interact with lists on these devices, and may be too confusing. Be completely honest; I don't want to put more work into something people won't use.

    The code for the custom list component is below. It is quite copiously commented, so you can see what's
    going on.

    EDIT: updated the code to reflect bug fixes in rev 2

    Code:
    Ext.namespace('Ext.ux.touch');
    
    
    Ext.ux.touch.GroupRevealList = Ext.extend(Ext.List,{
    
    	initComponent: function() {
    
    
    		
    		// This is a grouped list only, regardless of config options
    		this.grouped = true;
    
    		// We're going to replace the default group template with our own special version.
    		// It's not much more complicated than the original. Only the list items inside
    		// the group specified by the "activeRenderGroup" member of the template will be 
    		// actually be rendered; the rest of the groups are each represented by a single "proxy"
    		// item.
    		this.groupTpl = [
    	        '<tpl for=".">',
    				'<tpl if="this.shouldRenderItems(group)">',
    		            '{[this.incRenderCount(values.nitems)]}',
    		        	'<div class="x-list-group x-group-{id}">',
    		                '<h3 class="x-list-header">{group}</h3>',
    		                '<div class="x-list-group-items">',
    		                    '{items}',
    		                '</div>',
    		            '</div>',
    	            '</tpl>',
    				'<tpl if="!this.itemsRendered">',
    		            '{[this.incStartCount(values.group,values.nitems)]}',
    		        	'<div class="x-list-group x-group-{id}">',
    		                '<h3 class="x-list-header">{group}</h3>',
    		                '<div class="x-list-group-items x-list-group-proxy" groupProxyId="{group}">',
    		                    '{[values.group + "..."]}',
    		                '</div>',
    		            '</div>',
    	            '</tpl>',
    	        '</tpl>'
    	    ];
    		
    		// Save the super class in an object variable. This saves both typing, and
    		// two levels of object dereferencing when calling superclass functions.
    		this.mySuper = Ext.ux.touch.GroupRevealList.superclass;
    
    		// superclass constructor will now set up the render template including our
    	    // new group template. It will also create our index bar, if any.
    		this.mySuper.initComponent.call(this);
    	    
    		// add a member to this object to save the number of items not rendered
    		// before the start of rendered items. This offset is needed to make
    		// selection work - see below.
    		this.renderIndexOffset = 0;
    		
    		// Create the active group member in our render template
    		// Add some new data members and functions to our template,
    		// to be used by our new group template above. It is simpler
    		// to add them here, with the template object otherwise complete, than 
    		// to figure out how to add them to the complicated process of 
    		// building this template in DataView and List.
    		this.tpl.activeRenderGroup = "A";
    		this.tpl.nRendered = 0;
    		this.tpl.startRenderIndex = 0;
    		this.tpl.itemsRendered = false;
    		this.tpl.shouldRenderItems = function(group) {
    			this.itemsRendered = (group >= this.activeRenderGroup && this.nRendered < 20);
    			return this.itemsRendered;
    		}
    		this.tpl.incRenderCount = function(nitems) {
    				this.nRendered += nitems;
    				return "";
    		}
    		this.tpl.incStartCount = function(group,nitems) {
    				if ( group < this.activeRenderGroup ) {
    					this.startRenderIndex += nitems;
    				}
    				return "";
    		}
    		this.tpl.prepareNewRender = function(group) {
    			this.activeRenderGroup = group;
    			this.nRendered = 0;
    			this.startRenderIndex = 0;
    			this.itemsRendered = false;
    		}
     
    
    	},
    	
    	// we override this purely to be able to collect the number of children in a group as part of 
    	// the data passed to our template. Otherwise, this is an exact copy of List:collectData
    	collectData : function(records, startIndex) {
            if (!this.grouped) {
                return this.mySuper.collectData.call(this, records, startIndex);
            }
    
            var results = [],
                groups = this.store.getGroups(),
                ln = groups.length,
                children, cln, c,
                group, i;
    
            for (i = 0, ln = groups.length; i < ln; i++) {
                group = groups[i];
                children = group.children;
                for (c = 0, cln = children.length; c < cln; c++) {
                    children[c] = children[c].data;
                }
                results.push({
                    group: group.name,
                    id: this.getGroupId(group),
    				// This is our mod
                    nitems: cln,
                    items: this.listItemTpl.apply(children)
                });
            }
    
            return results;
        },
    
        // override of inherited list function - need to monitor scrollend
    	// we need to update our list rendering any time scrolling ends, or
    	// any time the user has tapped or scrubbed our index bar.
    	initEvents: function() {
    		// call base class method
    		this.mySuper.initEvents.call(this);
    		
    		// monitor scroll end
    		this.mon(this.scroller,
    			{
    				scrollend: this.onScrollEnd,
    				scope: this
    			}
    		);
    	
    		// monitoring the index bar is a bit tricky. We don't want to re-render
    		// every time the index changes, only when the listener ends his touch
    		// on the desired index. What makes this tricky is that the index bar
    		// itself listens for touchend, and stops the event from reaching us.
    		// So we create a sequence instead, and tell the index bar to call that
    		// sequence on touchend.
    		if ( this.indexBar ) {
    			var bar = this.indexBar;
    			// remove existing touchend listener on index bar
    			bar.mun(bar.el,'touchend',bar.onTouchEnd);
    			// create function sequence
    			bar.onTouchEnd = Ext.createSequence(bar.onTouchEnd,this.onLastIndex,this);
    			// and re-establish the touchend listener on the index bar
    			bar.mon(bar.el,
    				{
    					touchend: bar.onTouchEnd,
    					scope: bar
    				}
    			);
    		}
    		
    	},
    	
    	// overriden method - if tap was on a group proxy element, make that the
    	// current rendered group.
    	onTap: function(e) {
    		var proxyEl, groupId;
    		// clear any pending updates
    		if ( this.scrollEndTimer ) {
    			clearTimeout(this.scrollEndTimer);
    			this.scrollEndTimer = null;
    		}
    		// check if this is tap on proxy group element. If so, make that
    		// group the current rendered group.
    		proxyEl = e.getTarget('.x-list-group-proxy', this.getTargetEl());
    		if ( proxyEl ) {
    			groupId = proxyEl.getAttribute('groupProxyId');
    			if ( groupId ) {
    				this.setRenderGroup(groupId);
    			}
    		}
    		else {
    			this.mySuper.onTap.apply(this,arguments);
    		}
    	},
    	
    	// Override of inherited Ext:List function. If the user has suddenly flicked the scroller
    	// again, cancel any pending render of the new group.
    	onScrollStart: function() {
    		if ( this.scrollEndTimer ) {
    			clearTimeout(this.scrollEndTimer);
    			this.scrollEndTimer = null;
    		}
    		this.mySuper.onScrollStart.call(this);
    	},
    	
    	// This gets called when the scroll ends. Must check if there is a new group
    	// visible, and render it if so. We delay execution of this, so that the
    	// user can flick the list some more, or change scroll direction.
    	onScrollEnd: function() {
    		if ( this.scrollEndTimer ) {
    			clearTimeout(this.scrollEndTimer);
    			this.scrollEndTimer = null;
    		}
    		this.scrollEndTimer = Ext.defer(this.deferredScrollEnd,300,this);
    	},
    	
    	// do the acutal work of rendering a new group on scrollend.
    	deferredScrollEnd: function() {
    		// null out the scroll timer - it is no longer valid since we have already gotten here.
    		if ( this.scrollEndTimer ) {
    			this.scrollEndTimer = null;
    		}
    		// try/catch/finally block to make sure any errors don't kill our scroll events
    		// permanently.
    		try {
    			// ignore scrolling events while rendering is happening
    			this.scroller.suspendEvents();
    			// scoller position
    			var scrollPos = this.scroller.getOffset();
    			// height of list viewport
    			var vpHeight = this.getTargetEl().getHeight();
    			// what are the group(s) in view?
    			var closest = this.getClosestGroups(scrollPos);
    			// which group should we render? The one that takes up more of the
    			// screen ...
    			var group;
    			if ( closest.current.offset >= scrollPos.y ) {
    				group = closest.current;
    			}
    			else if ( closest.next && (closest.next.offset - scrollPos.y) < (vpHeight/2) ) {
    				group = closest.next;
    			}
    			else {
    				group = closest.current;
    			}
    			// get the id of the group to update
    			var groupId = group.header.getHTML();
    			// render the group to update (does nothing if group is the same as previous)
    			this.setRenderGroup(groupId);
    		}
    		catch(e) {
    			this.scroller.resumeEvents();
    			throw(e);
    		}
    		finally {
    			this.scroller.resumeEvents();
    		}
    	},
    	
    	// override of inherited Ext:List function
    	onIndex: function(record,target,index) {
    		// save the last index record, to use when the user ends touch 
    		// on the index bar
    		this.lastIndexRecord = record;
    		// call overridden base class method
    		this.mySuper.onIndex.apply(this,arguments);
    	},
    	
    	// this gets called after the user has finished tapping or scrubbing on the index bar
    	onLastIndex: function() {
    		if ( this.lastIndexRecord ) {
    			// see which group we've just indexed to
    			var groupId = this.lastIndexRecord.get('key').toUpperCase();
    			this.lastIndexRecord = null;
    			// render the group (does nothing if group is the same as previous)
    			this.setRenderGroup(groupId);
    		}
    	},
    	
    	// We need to create our own, overridden version of updateIndexes (from DataView)
    	// Our version corrects the index for all of the items not rendered.
        updateIndexes : function(startIndex, endIndex){
     		var indexOffset = this.tpl.startRenderIndex || 0;
        	var ns = this.all.elements;
            startIndex = startIndex || 0;
            endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
            for(var i = startIndex; i <= endIndex; i++){
                ns[i].viewIndex = i + indexOffset;
            }
        },
    
     	// Unfortunately, we also need to override getNode, which needs to account
        // for the offset in render index also
        getNode : function(nodeInfo) {
    		if ( nodeInfo instanceof Ext.data.Model ) {
                var idx = this.store.indexOf(nodeInfo);
                return this.all.elements[idx - this.renderIndexOffset];
    		}
            return this.mySuper.getNode.call(this,nodeInfo);
        },
    
    	// As the name says, set the active group using the group string as identifier
        setActiveGroupById: function(groupId) {
    		var groups = this.groupOffsets;
    		var ngrp = groups.length;
    		var i, group;
    		for ( i = 0; i < ngrp; i++ ) {
    			group = groups[i];
    			if ( group.header.getHTML() == groupId )
    				break;
    		}
    		if ( group ) 
    			this.setActiveGroup(group);
    	},
        
        // Do the actual work of re-rendering a new active group. Trivially simple at
    	// this point. Returns true if new rendering was done, false otherwise.
    	setRenderGroup: function(/*string*/groupId) {
    		if ( groupId != this.tpl.activeRenderGroup ) {
    			this.tpl.prepareNewRender(groupId);
    			// calling refresh() causes the list to be re-rendered, with the new
    			// group set. refresh() also does some extra work we don't need, but 
    			// it will do for now ...
    			this.refresh();
    			// call updateOffsets and updateBoundary to compensate for the new dimensions
    			// of the group div, and the overall list div
    			this.updateOffsets();
    			this.scroller.updateBoundary();
    			// scroll to the top of the newly rendered group
    			this.scrollToGroup(groupId);
    			// set the active group
    			this.setActiveGroupById(groupId);
    			// save the render index offset
    			this.renderIndexOffset = this.tpl.startRenderIndex;
    			return true;
    		}
    		return false;
    	},
    	
    	// Scroll to the top of the specified group. Code stolen from List:onIndex function.
    	scrollToGroup: function(/*string*/groupId) {
            var closest = this.getTargetEl().down('.x-group-' + groupId.toLowerCase());
            if (closest) {
                this.scroller.scrollTo({x: 0, y: closest.getOffsetsTo(this.scrollEl)[1]}, false, null, true);
            }
    	}
    
    });
    The class "x-list-group-proxy" is used to style group proxy items; it is defined in the sample code below
    as follows:

    Code:
    .x-list-group-proxy {
    	height: 500px;
    	text-align:center; 
    	line-height:500px; 
    	font-size:5em;
    }
    The attached zip file contains a working example, with about 1400 items defined. You'll have to edit listplug.html to point to the correct include paths for your sencha-touch 1.01 library.
    Attached Files

  7. #7
    Sencha User
    Join Date
    Dec 2010
    Posts
    48
    Vote Rating
    0
    Shijutv is on a distinguished road

      0  

    Default


    Hi MahlerFreak,

    First of all , i want to thank you for your great effort.
    I have just integrated this with my project.it is working good with high performance.
    There are some bugs we to fix,
    As you told when i click an item it fetches a wrong item
    When we scroll to another group we have seen the detailed group but in the bottom we again see the same group

    Shiju

  8. #8
    Touch Premium Member
    Join Date
    Apr 2007
    Posts
    139
    Vote Rating
    0
    mherger is on a distinguished road

      0  

    Default


    Thanks a lot MahlerFreak! You've taken a slightly different approach than I would have, but your code reveals some great ideas. And I learned about sequences :-).

    I don't want to restrict myself to lists with index bars, as I don't always have them. Maybe I'll just add a numerical index bar at some point :-). But for now my simplification is a unique item height, which allows to do the buffer calculation as you suggested it in your initial posting. And I'm hiding the group headings, as otherwise the calculation becomes a bit more complex too.

    I'll spend some time trying to figure out what exactly you're doing and how I could re-use some of this in my own solution. I'll let you know. Thanks a lot!
    HTC Desire - Android: 2.2 - Kernel: 2.6.32.15-gf5a401c - Build: 2.29.405.2 - WebKit: 3.1

  9. #9
    Ext User
    Join Date
    Jul 2010
    Posts
    57
    Vote Rating
    1
    konki_vienna is on a distinguished road

      0  

    Default


    I tested your zip on my HTC Desire HD - performance is amazing - no screen-freezing anymore...
    On the iPad it works good as well, but on an iPod (2nd Generation, iOS 4) normal scrolling (without using the index) does not work good at all: the list does not move when you move your finger away from the screen...

    I'll try to take a look at the code within the next days - right know it seems to be magic mysteries to me... Do you think it can be adapted for list without index bar and with scroll-bar instead (the problem here probably is that you do not know the length of the total list, which is crucial for the length of the scroll-bar).

  10. #10
    Ext User
    Join Date
    Jul 2010
    Posts
    57
    Vote Rating
    1
    konki_vienna is on a distinguished road

      0  

    Default


    Yes, of course it is possible to not show the index bar: 'indexBar: false'

    Why does every group, that is rendered with content ('this.shouldRenderItems(group)') appear underneath the rendered group again ('!this.shouldRenderItems(group)')? - Is there a simple way to fix this 'bug'? (Perhaps I'll find out by myself, when I try to implement your version into my application...)

Similar Threads

  1. Sencha Touch and long list/store performance
    By bklaas in forum Sencha Touch 1.x: Discussion
    Replies: 3
    Last Post: 20 Dec 2010, 10:41 AM
  2. Massive Performance Issue with Custom List (XTemplate) on Android
    By konki_vienna in forum Sencha Touch 1.x: Discussion
    Replies: 1
    Last Post: 20 Dec 2010, 10:40 AM
  3. where's my thread?
    By innivodave in forum Ext 3.x: Help & Discussion
    Replies: 4
    Last Post: 8 Dec 2009, 11:43 AM
  4. List of performance strategies for extjs
    By mavenn in forum Community Discussion
    Replies: 9
    Last Post: 5 Mar 2009, 8:54 AM

Thread Participants: 15