Changeset 141548 in webkit


Ignore:
Timestamp:
Feb 1, 2013 12:27:10 AM (11 years ago)
Author:
haraken@chromium.org
Message:

[V8] Remove V8GCController::m_edenNodes
https://bugs.webkit.org/show_bug.cgi?id=108579

Reviewed by Adam Barth.

Currently V8GCController::m_edenNodes stores a list of nodes whose
wrappers have been created since the latest GC. The reason why we
needed m_edenNodes is that there was no way to know a list of wrappers
in the new space of V8. By using m_edenNodes, we had been approximating
'wrappers in the new space' by 'wrappers that have been created since
the latest GC'.

Now V8 provides VisitHandlesForPartialDependence(), with which WebKit
can know a list of wrappers in the new space. By using the API, we can
remove V8GCController::m_edenNodes. The benefit is that (1) we no longer
need to keep m_edenNodes and that (2) it enables more precise minor
DOM GC (Remember that m_edenNodes was just an approximation).

Performance benchmark: https://bugs.webkit.org/attachment.cgi?id=185940
The benchmark runs 300 iterations, each of which creates 100000 elements.
The benchmark measures average, min, median, max and stdev of execution times
of the 300 iterations. This will tell us the worst-case overhead of this change.

Before:

mean=59.91ms, min=39ms, median=42ms, max=258ms, stdev=47.48ms

After:

mean=58.75ms, min=35ms, median=41ms, max=250ms, stdev=47.32ms

As shown above, I couldn't observe any performance regression.

No tests. No change in behavior.

  • bindings/v8/DOMDataStore.h:

(WebCore::DOMDataStore::setWrapperInObject):

  • bindings/v8/V8GCController.cpp:

(WebCore::gcTree):
(WebCore):
(MinorGCWrapperVisitor):
(WebCore::MinorGCWrapperVisitor::notifyFinished):
(WebCore::V8GCController::gcPrologue):
(WebCore::V8GCController::minorGCPrologue):
(WebCore::V8GCController::majorGCPrologue):

  • bindings/v8/V8GCController.h:

(V8GCController):

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r141547 r141548  
     12013-02-01  Kentaro Hara  <haraken@chromium.org>
     2
     3        [V8] Remove V8GCController::m_edenNodes
     4        https://bugs.webkit.org/show_bug.cgi?id=108579
     5
     6        Reviewed by Adam Barth.
     7
     8        Currently V8GCController::m_edenNodes stores a list of nodes whose
     9        wrappers have been created since the latest GC. The reason why we
     10        needed m_edenNodes is that there was no way to know a list of wrappers
     11        in the new space of V8. By using m_edenNodes, we had been approximating
     12        'wrappers in the new space' by 'wrappers that have been created since
     13        the latest GC'.
     14
     15        Now V8 provides VisitHandlesForPartialDependence(), with which WebKit
     16        can know a list of wrappers in the new space. By using the API, we can
     17        remove V8GCController::m_edenNodes. The benefit is that (1) we no longer
     18        need to keep m_edenNodes and that (2) it enables more precise minor
     19        DOM GC (Remember that m_edenNodes was just an approximation).
     20
     21        Performance benchmark: https://bugs.webkit.org/attachment.cgi?id=185940
     22        The benchmark runs 300 iterations, each of which creates 100000 elements.
     23        The benchmark measures average, min, median, max and stdev of execution times
     24        of the 300 iterations. This will tell us the worst-case overhead of this change.
     25
     26        Before:
     27          mean=59.91ms, min=39ms, median=42ms, max=258ms, stdev=47.48ms
     28
     29        After:
     30          mean=58.75ms, min=35ms, median=41ms, max=250ms, stdev=47.32ms
     31
     32        As shown above, I couldn't observe any performance regression.
     33
     34        No tests. No change in behavior.
     35
     36        * bindings/v8/DOMDataStore.h:
     37        (WebCore::DOMDataStore::setWrapperInObject):
     38        * bindings/v8/V8GCController.cpp:
     39        (WebCore::gcTree):
     40        (WebCore):
     41        (MinorGCWrapperVisitor):
     42        (WebCore::MinorGCWrapperVisitor::notifyFinished):
     43        (WebCore::V8GCController::gcPrologue):
     44        (WebCore::V8GCController::minorGCPrologue):
     45        (WebCore::V8GCController::majorGCPrologue):
     46        * bindings/v8/V8GCController.h:
     47        (V8GCController):
     48
    1492013-02-01  Vsevolod Vlasov  <vsevik@chromium.org>
    250
  • trunk/Source/WebCore/bindings/v8/DOMDataStore.h

    r139458 r141548  
    165165        wrapper.MakeWeak(object, weakCallback);
    166166    }
    167     static void setWrapperInObject(Node* object, v8::Persistent<v8::Object> wrapper)
    168     {
    169         ASSERT(object->wrapper().IsEmpty());
    170         object->setWrapper(wrapper);
    171         V8GCController::didCreateWrapperForNode(object);
    172         wrapper.MakeWeak(static_cast<ScriptWrappable*>(object), weakCallback);
    173     }
    174167
    175168    static void weakCallback(v8::Persistent<v8::Value>, void* context);
  • trunk/Source/WebCore/bindings/v8/V8GCController.cpp

    r141524 r141548  
    179179}
    180180
    181 class WrapperVisitor : public v8::PersistentHandleVisitor {
     181static void gcTree(Node* startNode)
     182{
     183    Vector<v8::Persistent<v8::Value>, initialNodeVectorSize> newSpaceWrappers;
     184
     185    // We traverse a DOM tree in the DFS order starting from startNode.
     186    // The traversal order does not matter for correctness but does matter for performance.
     187    Node* node = startNode;
     188    // To make each minor GC time bounded, we might need to give up
     189    // traversing at some point for a large DOM tree. That being said,
     190    // I could not observe the need even in pathological test cases.
     191    do {
     192        ASSERT(node);
     193        if (!node->wrapper().IsEmpty()) {
     194            // FIXME: Remove the special handling for image elements.
     195            // The same special handling is in V8GCController::opaqueRootForGC().
     196            // Maybe should image elements be active DOM nodes?
     197            // See https://code.google.com/p/chromium/issues/detail?id=164882
     198            if (!node->isV8CollectableDuringMinorGC() || (node->hasTagName(HTMLNames::imgTag) && static_cast<HTMLImageElement*>(node)->hasPendingActivity())) {
     199                // This node is not in the new space of V8. This indicates that
     200                // the minor GC cannot anyway judge reachability of this DOM tree.
     201                // Thus we give up traversing the DOM tree.
     202                return;
     203            }
     204            node->setV8CollectableDuringMinorGC(false);
     205            newSpaceWrappers.append(node->wrapper());
     206        }
     207        if (node->firstChild()) {
     208            node = node->firstChild();
     209            continue;
     210        }
     211        while (!node->nextSibling()) {
     212            if (!node->parentNode())
     213                break;
     214            node = node->parentNode();
     215        }
     216        if (node->parentNode())
     217            node = node->nextSibling();
     218    } while (node != startNode);
     219
     220    // We completed the DOM tree traversal. All wrappers in the DOM tree are
     221    // stored in newSpaceWrappers and are expected to exist in the new space of V8.
     222    // We report those wrappers to V8 as an object group.
     223    for (size_t i = 0; i < newSpaceWrappers.size(); i++)
     224        newSpaceWrappers[i].MarkPartiallyDependent();
     225    if (newSpaceWrappers.size() > 0)
     226        v8::V8::AddObjectGroup(&newSpaceWrappers[0], newSpaceWrappers.size());
     227}
     228
     229// Regarding a minor GC algorithm for DOM nodes, see this document:
     230// https://docs.google.com/a/google.com/presentation/d/1uifwVYGNYTZDoGLyCb7sXa7g49mWNMW2gaWvMN5NLk8/edit#slide=id.p
     231class MinorGCWrapperVisitor : public v8::PersistentHandleVisitor {
     232public:
     233    virtual void VisitPersistentHandle(v8::Persistent<v8::Value> value, uint16_t classId) OVERRIDE
     234    {
     235        // A minor DOM GC can collect only Nodes.
     236        if (classId != v8DOMNodeClassId)
     237            return;
     238
     239        // To make minor GC cycle time bounded, we limit the number of wrappers handled
     240        // by each minor GC cycle to 10000. This value was selected so that the minor
     241        // GC cycle time is bounded to 20 ms in a case where the new space size
     242        // is 16 MB and it is full of wrappers (which is almost the worst case).
     243        // Practically speaking, as far as I crawled real web applications,
     244        // the number of wrappers handled by each minor GC cycle is at most 3000.
     245        // So this limit is mainly for pathological micro benchmarks.
     246        const unsigned wrappersHandledByEachMinorGC = 10000;
     247        if (m_nodesInNewSpace.size() >= wrappersHandledByEachMinorGC)
     248            return;
     249
     250        ASSERT(value->IsObject());
     251        v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>::Cast(value);
     252        ASSERT(V8DOMWrapper::maybeDOMWrapper(value));
     253        ASSERT(V8Node::HasInstance(wrapper));
     254        Node* node = V8Node::toNative(wrapper);
     255        m_nodesInNewSpace.append(node);
     256        node->setV8CollectableDuringMinorGC(true);
     257    }
     258
     259    void notifyFinished()
     260    {
     261        for (size_t i = 0; i < m_nodesInNewSpace.size(); i++) {
     262            ASSERT(!m_nodesInNewSpace[i]->wrapper().IsEmpty());
     263            if (m_nodesInNewSpace[i]->isV8CollectableDuringMinorGC()) // This branch is just for performance.
     264                gcTree(m_nodesInNewSpace[i]);
     265        }
     266    }
     267
     268private:
     269    Vector<Node*> m_nodesInNewSpace;
     270};
     271
     272class MajorGCWrapperVisitor : public v8::PersistentHandleVisitor {
    182273public:
    183274    virtual void VisitPersistentHandle(v8::Persistent<v8::Value> value, uint16_t classId) OVERRIDE
     
    242333};
    243334
    244 // Regarding a minor GC algorithm for DOM nodes, see this document:
    245 // https://docs.google.com/a/google.com/presentation/d/1uifwVYGNYTZDoGLyCb7sXa7g49mWNMW2gaWvMN5NLk8/edit#slide=id.p
    246 //
    247 // m_edenNodes stores nodes that have wrappers that have been created since the last minor/major GC.
    248 Vector<Node*>* V8GCController::m_edenNodes = 0;
    249 
    250 static void gcTree(Node* startNode)
    251 {
    252     Vector<v8::Persistent<v8::Value>, initialNodeVectorSize> newSpaceWrappers;
    253 
    254     // We traverse a DOM tree in the DFS order starting from startNode.
    255     // The traversal order does not matter for correctness but does matter for performance.
    256     Node* node = startNode;
    257     // To make each minor GC time bounded, we might need to give up
    258     // traversing at some point for a large DOM tree. That being said,
    259     // I could not observe the need even in pathological test cases.
    260     do {
    261         ASSERT(node);
    262         if (!node->wrapper().IsEmpty()) {
    263             // FIXME: Remove the special handling for image elements.
    264             // The same special handling is in V8GCController::opaqueRootForGC().
    265             // Maybe should image elements be active DOM nodes?
    266             // See https://code.google.com/p/chromium/issues/detail?id=164882
    267             if (!node->isV8CollectableDuringMinorGC() || (node->hasTagName(HTMLNames::imgTag) && static_cast<HTMLImageElement*>(node)->hasPendingActivity())) {
    268                 // The fact that we encounter a node that is not in the Eden space
    269                 // implies that its wrapper might be in the old space of V8.
    270                 // This indicates that the minor GC cannot anyway judge reachability
    271                 // of this DOM tree. Thus we give up traversing the DOM tree.
    272                 return;
    273             }
    274             // A once traversed node is removed from the Eden space.
    275             node->setV8CollectableDuringMinorGC(false);
    276             newSpaceWrappers.append(node->wrapper());
    277         }
    278         if (node->firstChild()) {
    279             node = node->firstChild();
    280             continue;
    281         }
    282         while (!node->nextSibling()) {
    283             if (!node->parentNode())
    284                 break;
    285             node = node->parentNode();
    286         }
    287         if (node->parentNode())
    288             node = node->nextSibling();
    289     } while (node != startNode);
    290 
    291     // We completed the DOM tree traversal. All wrappers in the DOM tree are
    292     // stored in newSpaceWrappers and are expected to exist in the new space of V8.
    293     // We report those wrappers to V8 as an object group.
    294     for (size_t i = 0; i < newSpaceWrappers.size(); i++)
    295         newSpaceWrappers[i].MarkPartiallyDependent();
    296     if (newSpaceWrappers.size() > 0)
    297         v8::V8::AddObjectGroup(&newSpaceWrappers[0], newSpaceWrappers.size());
    298 }
    299 
    300 void V8GCController::didCreateWrapperForNode(Node* node)
    301 {
    302     // To make minor GC cycle time bounded, we limit the number of wrappers handled
    303     // by each minor GC cycle to 10000. This value was selected so that the minor
    304     // GC cycle time is bounded to 20 ms in a case where the new space size
    305     // is 16 MB and it is full of wrappers (which is almost the worst case).
    306     // Practically speaking, as far as I crawled real web applications,
    307     // the number of wrappers handled by each minor GC cycle is at most 3000.
    308     // So this limit is mainly for pathological micro benchmarks.
    309     const unsigned wrappersHandledByEachMinorGC = 10000;
    310     ASSERT(!node->wrapper().IsEmpty());
    311     if (!m_edenNodes)
    312         m_edenNodes = adoptPtr(new Vector<Node*>).leakPtr();
    313     if (m_edenNodes->size() <= wrappersHandledByEachMinorGC) {
    314         // A node of a newly created wrapper is put into the Eden space.
    315         m_edenNodes->append(node);
    316         node->setV8CollectableDuringMinorGC(true);
    317     }
    318 }
    319 
    320335void V8GCController::gcPrologue(v8::GCType type, v8::GCCallbackFlags flags)
    321336{
     
    324339    else if (type == v8::kGCTypeMarkSweepCompact)
    325340        majorGCPrologue();
    326 
    327     if (isMainThreadOrGCThread() && m_edenNodes) {
    328         // The Eden space is cleared at every minor/major GC.
    329         m_edenNodes->clear();
    330     }
    331341}
    332342
     
    335345    TRACE_EVENT_BEGIN0("v8", "GC");
    336346
    337     if (isMainThreadOrGCThread() && m_edenNodes) {
    338         for (size_t i = 0; i < m_edenNodes->size(); i++) {
    339             ASSERT(!m_edenNodes->at(i)->wrapper().IsEmpty());
    340             if (m_edenNodes->at(i)->isV8CollectableDuringMinorGC()) // This branch is just for performance.
    341                 gcTree(m_edenNodes->at(i));
    342         }
    343     }
     347    v8::HandleScope scope;
     348
     349    MinorGCWrapperVisitor visitor;
     350    v8::V8::VisitHandlesForPartialDependence(v8::Isolate::GetCurrent(), &visitor);
     351    visitor.notifyFinished();
    344352}
    345353
     
    351359    v8::HandleScope scope;
    352360
    353     WrapperVisitor visitor;
     361    MajorGCWrapperVisitor visitor;
    354362    v8::V8::VisitHandlesWithClassIds(&visitor);
    355363    visitor.notifyFinished();
  • trunk/Source/WebCore/bindings/v8/V8GCController.h

    r140949 r141548  
    5353
    5454    static Node* opaqueRootForGC(Node*);
    55     static void didCreateWrapperForNode(Node*);
    5655
    5756private:
Note: See TracChangeset for help on using the changeset viewer.