Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CPU extreamly high when numerous elements are binded. Suggestion included. #9

Open
sdadds opened this issue Jan 9, 2013 · 5 comments

Comments

@sdadds
Copy link

sdadds commented Jan 9, 2013

Works well even though window resize is called when it really has not changed.

I noticed the CPUs running up over 80% with less than 10 elements registered and
very little re-sizing activity (really sluggish performance). The main elements, jQGrids
embedded inside jQuery-UI tabs and accordions within a jQuery-Layout.

Code below helps for my situation.

Thanks.

function loopy() {
// Start the polling loop, asynchronously.
timeout_id = window[ str_setTimeout ](function(){

// Iterate over all elements to which the 'resize' event is bound.
var pixelTolerance = jq_resize[ str_pixelTolerance ];
elems.each(function() {
    //
    //  Modified to reduce the CPU load when there are numerous
    // elements registered.  Tested with five+ re-sizable jQGrids
    // embedded into various tabs and accordion elements all inside
    // of a jQuery layout.
    //  With the default delay of 250 the CPU usage is reduced and
    // re-sizing snappy.
    //
    var isVisible  = false;
    var hasResized = false;
    var elem       = $(this);

    try {
         if ($(elem).is(':visible') ) {
             isVisible = true;
         }
    } catch (eThrow) {
         //alert(eThrow.message);
    }

    // No reason to resize hidden elements
    if (isVisible) {
        try {
            // For pixels round down to nearest integer
            var width  = Math.floor(elem.width());
            var height = Math.floor(elem.height());
            var data   = $.data( this, str_data );
            data.w     = Math.floor(data.w);
            data.h     = Math.floor(data.h);

            // Reduce number of resizes, default 10 pixels
            if (   (Math.abs(width - data.w)  > pixelTolerance)
                || (Math.abs(height - data.h) > pixelTolerance)) {
                hasResized = true;
            }

            //  If element size has changed
            // trigger the 'resize' event and
            // update the data store
            if (hasResized) {
                elem.trigger( str_resize, [ width, height ] );
                //  Some elements will not re-size exactly
                // so try to get the latest size available
                // before storing
                data.w = Math.floor(elem.width());
                data.h = Math.floor(elem.height());
            }
        } catch (eThrow) {
            //alert(eThrow.message);
        }
    }

  });

  // Loop.
  loopy();

}, jq_resize[ str_delay ] );
@Download
Copy link

Download commented Feb 4, 2013

What I noticed is that the polling loop just loops over the elements that have resize events attached in the order they were attached in (or at least in the default order that it gets from jQuery).

However, when elements are nested inside each other (as is often the case), the resize of one element may cause a resize of another element. This causes duplicate work and a kind of 'ripple effect'.

Imagine a page with these elements on it:

. . Root
. . . . Outer
. . . . . . Inner

And that the order of the elements' event listeners is this:

[Inner, Outer, Root]

When Inner receives the resize event, it's still looking at Outer and Root's old dimensions as these haven't received the event yet... So any changes it makes might be outdated quickly. Because after Inner has run, Outer runs and updates it's size. This may in turn trigger Inner to resize again.

We have a Javascript behaviour which makes an element fill up any space left in it's container. It attaches a resize event listener to it's container. That could have height set as 100% so implicitly depends on the size of it's parent and so on.

In some cases, with a whole bunch of nested elements and a bad ordering, this could mean that a single resize could take a whole bunch of 'runs' of the polling loop (and consequently multiple seconds of time) to update the state of all elements.

What we did was change the code that adds an event listener (inside ba-resize). We first order all elements. The ones that are least nested (closest to the body element) come first and ones that are most deeply nested come last. Then when the polling loop runs, it fires elements in the order in which the resize logically propagates through the document (I think this is the order that 'real' events also normally propagate in, the 'capture' phase), meaning Inner will only receive the resize event after Root and Outer already received and processed theirs, so it will see up-to-date values for the height of width of these elements. This improved things a lot for us.

If you would want to implement this in the best way, you would probably not make loopy() a loop, but a recursive function that iterates over the elements in the same order as they are situated in the document tree. But just changing the add event function to not always append at the end of the list, but insert at the correct position in respect to 'nestedness' already was enough for us.

@pdf
Copy link

pdf commented Jun 5, 2013

@Download - so, where's the pull request with your modification?

@Download
Copy link

Download commented Jan 9, 2014

Lol. I am pretty new to GitHub and was not really using it when I wrote above post. That is also the reason I did not reply to you for 7 months... I never noticed your question.

Pull request would be a good idea... But then again the problem is pretty self-explanatory once you get the concept and my 'fix' not up to the standards I would like for an Open Source project, mostly because I am not familiar enough with jQuery plugin writing to do it the clean way. I pretty much hacked it in there.. Also seeing that Ben did not do much with this project anymore after contributing it four years ago (note that this issue is now open for a year already) I wonder if he could even be bothered to review and commit a pull...

Ben if you do care about this, I can send you the modifications I made and you can do with them what you like. I am warning you though that it's not clean at all. I just hacked around a bit to get rid of the echoing and CPU usage it was causing for me. One thing in favour of the code, hacky as it is, is that it has been running in production for quitte a while now and so far has not given me any issues that I am aware of.

-Stijn

@pdf
Copy link

pdf commented Jan 10, 2014

@Download if you share the code, even if it's not pulled into the pain repo here immediately, others may benefit from it, and/or improve it. This is kind of how open source works.

@Download
Copy link

Yeah I know. But my code is not in GitHub. So it will take some effort on my part to create a pull request. So I'd first like to hear from people that actually want this before I put in the effort.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants