Changeset 183749 in webkit


Ignore:
Timestamp:
May 4, 2015 10:27:34 AM (9 years ago)
Author:
ggaren@apple.com
Message:

REGRESSION(r183570): jslib-traverse-jquery is 22% slower
https://bugs.webkit.org/show_bug.cgi?id=144476

Reviewed by Sam Weinig.

jslib-traverse-jquery is now 31% faster than its unregressed baseline.

The jQuery algorithm for sorting DOM nodes is so pathologically slow that,
to my knowledge, the topic of how to optimize it is not covered in any
literature about sorting.

On the slowest jQuery sorting test -- prevAll -- our new
Array.prototype.sort, compared to its predecessor, performed 12% fewer
comparisons and requireed 10X less overhead per comparison. Yet, it was
slower.

It was slower because it inadvertantly increased the average cost of the
comparison function by 2X. jQuery uses compareDocumentPosition to compare
DOM nodes, and compareDocumentPosition(a, b) is O(N) in the distance
required to traverse backwards from b to a. In prevAll, we encounter the
worst case for merge sort of compareDocumentPosition: A long list of DOM
nodes in mostly reverse order. In this case, merge sort will sequentially
compareDocumentPosition(a, b), where a is not reachable backwards from
b, and therefore compareDocumentPosition will traverse the whole sibling
list.

The solution is simple enough: Call compareDocumentPosition(b, a) instead.

This is a pretty silly thing to do, but it is harmless, and jQuery is
popular, so let's do it.

We do not risk suffering the same problem in reverse when sorting a long
list of DOM nodes in forward order. (We still have a 37% speedup on the
nextAll benchmark.) The reason is that merge sort performs 2X fewer
comparisons when the list is already sorted, so we can worry less about
the cost of each comparison.

A fully principled soultion to this problem would probably do something
like Python's timsort, which special-cases ordered ranges to perform
only O(n) comparisons. But that would contradict our original
goal of just having something simple that works.

Another option is for elements to keep a compareDocumentPosition cache,
like a node list cache, which allows you to determine the absolute
position of a node using a hash lookup. I will leave this as an exercise
for kling.

  • builtins/Array.prototype.js:

(sort.merge): Compare in an order that is favorable to a comparator
that calls compareDocumentPosition.

Location:
trunk/Source/JavaScriptCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r183738 r183749  
     12015-05-01  Geoffrey Garen  <ggaren@apple.com>
     2
     3        REGRESSION(r183570): jslib-traverse-jquery is 22% slower
     4        https://bugs.webkit.org/show_bug.cgi?id=144476
     5
     6        Reviewed by Sam Weinig.
     7
     8        jslib-traverse-jquery is now 31% faster than its unregressed baseline.
     9
     10        The jQuery algorithm for sorting DOM nodes is so pathologically slow that,
     11        to my knowledge, the topic of how to optimize it is not covered in any
     12        literature about sorting.
     13
     14        On the slowest jQuery sorting test -- prevAll -- our new
     15        Array.prototype.sort, compared to its predecessor, performed 12% fewer
     16        comparisons and requireed 10X less overhead per comparison. Yet, it was
     17        slower.
     18
     19        It was slower because it inadvertantly increased the average cost of the
     20        comparison function by 2X. jQuery uses compareDocumentPosition to compare
     21        DOM nodes, and compareDocumentPosition(a, b) is O(N) in the distance
     22        required to traverse backwards from b to a. In prevAll, we encounter the
     23        worst case for merge sort of compareDocumentPosition: A long list of DOM
     24        nodes in mostly reverse order. In this case, merge sort will sequentially
     25        compareDocumentPosition(a, b), where a is not reachable backwards from
     26        b, and therefore compareDocumentPosition will traverse the whole sibling
     27        list.
     28
     29        The solution is simple enough: Call compareDocumentPosition(b, a) instead.
     30
     31        This is a pretty silly thing to do, but it is harmless, and jQuery is
     32        popular, so let's do it.
     33
     34        We do not risk suffering the same problem in reverse when sorting a long
     35        list of DOM nodes in forward order. (We still have a 37% speedup on the
     36        nextAll benchmark.) The reason is that merge sort performs 2X fewer
     37        comparisons when the list is already sorted, so we can worry less about
     38        the cost of each comparison.
     39
     40        A fully principled soultion to this problem would probably do something
     41        like Python's timsort, which special-cases ordered ranges to perform
     42        only O(n) comparisons. But that would contradict our original
     43        goal of just having something simple that works.
     44
     45        Another option is for elements to keep a compareDocumentPosition cache,
     46        like a node list cache, which allows you to determine the absolute
     47        position of a node using a hash lookup. I will leave this as an exercise
     48        for kling.
     49
     50        * builtins/Array.prototype.js:
     51        (sort.merge): Compare in an order that is favorable to a comparator
     52        that calls compareDocumentPosition.
     53
    1542015-05-04  Csaba Osztrogonác  <ossy@webkit.org>
    255
  • trunk/Source/JavaScriptCore/builtins/Array.prototype.js

    r183570 r183749  
    399399        for (var dstIndex = left; dstIndex < rightEnd; ++dstIndex) {
    400400            if (right < rightEnd) {
    401                 if (left >= leftEnd || comparator(src[left], src[right]) > 0) {
     401                if (left >= leftEnd || comparator(src[right], src[left]) < 0) {
    402402                    dst[dstIndex] = src[right++];
    403403                    continue;
Note: See TracChangeset for help on using the changeset viewer.