I re-wrote .filter() this morning: http://gist.github.com/169693 Here are the new profiling outputs (I repeat the previous ones for convenience):
1. With the orginal .filter() and .closest() implementations: - entering the ul: function calls = 76, time = 1.5 to 2.5 ms - moving from a li to a li.blue: calls = 144, time ~= 3.2ms - moving from a li.blue to a li: calls = 103, time ~= 2.2ms - moving from a li to a green span: calls = 124, time ~= 2.2ms - moving from a green span to a red span: calls = 145, time ~= 3.2ms 2. With the modified .closest() implementation: - entering the ul: function calls = 13, time ~= 0.25ms - moving from a li to a li.blue: calls = 27, time ~= 0.55ms - moving from a li.blue to a li: calls = 13, time ~= 0.25ms - moving from a li to a green span: calls = 13, time ~= 0.3ms - moving from a green span to a red span: calls = 13, time ~= 0.3ms 3. With the modified .filter() implementation and a specific cache: - entering the ul: function calls = 28, time ~= 0.57ms - moving from a li to a li.blue: calls = 52, time ~= 0.9ms - moving from a li.blue to a li: calls = 33, time ~= 0.6ms - moving from a li to a green span: calls = 38, time ~= 0.67ms - moving from a green span to a red span: calls = 43, time ~= 0.73ms It's approximately 2x slower when the optimization is in .filter(). Compared to the original implementation there is still a clear benefit. Again, this is a very simple page that I'm testing on a pretty fast configuration. The benefit in real conditions for IE users would be even more significant. I've made two other tests to inform the way of caching the parsed selector 4. With the modified .filter() implementation and using jQuery.data (window, "parsedCache"): - entering the ul: function calls = 31, time = 0.6ms to 0.95ms - moving from a li to a li.blue: calls = 57, time ~= 1.3ms - moving from a li.blue to a li: calls = 37, time ~= 0.75ms - moving from a li to a green span: calls = 43, time ~= 0.8ms - moving from a green span to a red span: calls = 49, time ~= 0.9ms 5. With the modified .filter() implementation without caching the parsed selector: - entering the ul: function calls = 28, time = 0.6ms to 0.9ms - moving from a li to a li.blue: calls = 52, time ~= 1ms to 1.2ms - moving from a li.blue to a li: calls = 33, time ~= 0.7ms - moving from a li to a green span: calls = 38, time ~= 0.84ms - moving from a green span to a red span: calls = 43, time ~= 0.93ms As I expected, there is a benefit in using a cache, but there doesn't appear to be any benefit in using .data() for that purpose. Regards, Louis-Rémi On Aug 18, 2:02 am, lrbabe <lrb...@gmail.com> wrote: > I was also wondering what kind of selectors jQuery.expr was used for. > From my own experience, there is a clear benefit in .closest because I > almost exclusively use selectors of the form "div" or ".class". > But I just realized that .hasClass() would also benefit from this > optimization with its current implementation, and I use .hasClass() a > lot! > > On Aug 18, 1:00 am, lrbabe <lrb...@gmail.com> wrote: > > > It would definitely make sense of course. > > > What I really wanted was to minimize the impact of this extra- > > processing on complex selectors. > > In .closest(), even if you don't use any kind of cache, you parse the > > selector only once every time you use the function. > > If the optimization lies jQuery.filter then the cache becomes much > > more important. > > Since I'm not sure what kind of caching mechanism to use (would .data > > () be faster than re-parsing the selector?), I wasn't feeling > > confident enough to propose this change for .filter(). > > > There was also an optimization in .closest() for position selectors > > (:first, :last, ...). I don't know what it was here for (I never use > > such selectors for event delegation), but it wasn't in jQuery.filter. > > > What would you recommend? > > > Louis-Rémi > > > On Aug 17, 10:44 pm, John Resig <jere...@gmail.com> wrote: > > > > I'm curious as to why you only chose to optimize the selectors in .live(). > > > Why not optimize .is()? or jQuery.filter? Optimizing jQuery.filter would > > > yield faster results for .filter(), .is(), and .live(). > > > > --John > > > > On Mon, Aug 17, 2009 at 1:38 PM, lrbabe <lrb...@gmail.com> wrote: > > > > > I've made some minor updates to the code to reduce the code size: > > > > - it now uses the internal jQuery.nodeName function to check for > > > > simple selectors involving a node name (which is also safer) > > > > - it checks for simple selectors before checking for position > > > > selectors > > > > The size difference for the minified version between the current > > > > implementation and this new one should be around 250B > > > > The code is at the same address:http://gist.github.com/168158 > > > > > I'm also willing to write on learningjquery.com about the new features > > > > for event delegation introduced in jQuery 1.3: live and closest > > > > Who should I contact for that purpose? > > > > > Regards, > > > > Louis-Rémi Babé > > > > > On Aug 15, 2:23 am, lrbabe <lrb...@gmail.com> wrote: > > > > > Thank for your quick answer John, > > > > > > All right, I take the code of the example, remove the part that > > > > > updates the counter and wraps the rest with a console.profile() > > > > > > 1. With the orginal .closest() implementation: > > > > > - entering the ul: function calls = 76, time = 1.5 to 2.5 ms > > > > > - moving from a li to a li.blue: calls = 144, time ~= 3.2ms > > > > > - moving from a li.blue to a li: calls = 103, time ~= 2.2ms > > > > > - moving from a li to a green span: calls = 124, time ~= 2.2ms > > > > > - moving from a green span to a red span: calls = 145, time ~= 3.2ms > > > > > 2. With the modified .closest() implementation: > > > > > - entering the ul: function calls = 13, time ~= 0.25ms > > > > > - moving from a li to a li.blue: calls = 27, time ~= 0.55ms > > > > > - moving from a li.blue to a li: calls = 13, time ~= 0.25ms > > > > > - moving from a li to a green span: calls = 13, time ~= 0.3ms > > > > > - moving from a green span to a red span: calls = 13, time ~= 0.3ms > > > > > > ...and we have only three levels of elements here. > > > > > > On Aug 15, 1:51 am, John Resig <jere...@gmail.com> wrote: > > > > > > > An interesting proposition - although before making a change of this > > > > > > magnitude it would be good to get some performance numbers outlined > > > > > > so > > > > that > > > > > > we know how worthwhile it is. > > > > > > > --John > > > > > > > On Fri, Aug 14, 2009 at 8:33 PM, lrbabe <lrb...@gmail.com> wrote: > > > > > > > > Hi, > > > > > > > > The principle of .closest( selector ) is that it cycles through > > > > > > > the > > > > > > > ancestors of an event target until it finds an element > > > > > > > corresponding > > > > > > > to the event target, or hits the root. > > > > > > > To check for an element matching the selectors it uses the .is > > > > > > > ( selector ) function which collects all elements corresponding to > > > > the > > > > > > > selector and cycles through them to find if "this" is any of those > > > > > > > elements. > > > > > > > > If my memories about my algorithm lectures are correct, the > > > > complexity > > > > > > > of this algorithm is O(n²). Only in the case of a selector of the > > > > form > > > > > > > "#id" we have an O(n) complexity. > > > > > > > However, there is another range of selectors that could be checked > > > > > > > with an O(n) algorithm: selectors such as "div", ".class" and > > > > > > > "div.class". In those cases, .is( selector ) is not needed > > > > > > > because we > > > > > > > can directly check the ancestor's nodeType and className. > > > > > > > > Reducing the complexity of the .closest() function is particularly > > > > > > > important when using event delegation with the mouseover and > > > > > > > mouseout > > > > > > > events: those events fire really often as the user moves his/her > > > > > > > mouse, and the function needs to be used twice: one to check the > > > > > > > the > > > > > > > target is in the selector, and one to check that the related > > > > > > > target > > > > is > > > > > > > in a different ancestor. > > > > > > > > I propose a new implementation of .closest() that is able to > > > > > > > detect > > > > > > > those selectors and use them to "fast-check" ancestors. The last > > > > > > > parsed selector is cached to further improve the performances (I'm > > > > > > > just not sure where to cache the parsed selector). > > > > > > > > The code is available as a gist:http://gist.github.com/168158 > > > > > > > and can be tested here:http://www.lrbabe.com/sdoms/closest/ > > > > > > > > Together with the recent addition of the "context" parameter > > > > > > > in .closest(), it makes one of the most efficient event delegation > > > > > > > helper out there. > > > > > > > > Feedback would be much appreciated, > > > > > > > > Regards, > > > > > > > lrbabe --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "jQuery Development" group. To post to this group, send email to jquery-dev@googlegroups.com To unsubscribe from this group, send email to jquery-dev+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/jquery-dev?hl=en -~----------~----~----~----~------~----~------~--~---