Ignore:
Timestamp:
Nov 28, 2007, 2:08:09 AM (18 years ago)
Author:
[email protected]
Message:

JavaScriptCore:

Reviewed by Darin and Geoff.


Implement mark stack. This version is not suitable for prime time because it makes a
huge allocation on every collect, and potentially makes marking of detached subtrees
slow. But it is an 0.4% SunSpider speedup even without much tweaking.


The basic approach is to replace mark() methods with
markChildren(MarkStack&) methods. Reachable references are pushed
onto a mark stack (which encapsulates ignoring already-marked
references).


Objects are no longer responsible for actually setting their own
mark bits, the collector does that. This means that for objects on
the number heap we don't have to call markChildren() at all since
we know there aren't any.


The mark phase of collect pushes roots onto the mark stack
and drains it as often as possible.


To make this approach viable requires a constant-size mark stack
and a slow fallback approach for when the stack size is exceeded,
plus optimizations to make the required stack small in common
cases. This should be doable.

  • JavaScriptCore.exp: Export new symbols.
  • JavaScriptCore.xcodeproj/project.pbxproj: Add new file.
  • kjs/collector.cpp: (KJS::Collector::heapAllocate): (KJS::drainMarkStack): Helper for all of the below. (KJS::Collector::markStackObjectsConservatively): Use mark stack. (KJS::Collector::markCurrentThreadConservatively): ditto (KJS::Collector::markOtherThreadConservatively): ditto (KJS::Collector::markProtectedObjects): ditto (KJS::Collector::markMainThreadOnlyObjects): ditto (KJS::Collector::collect): ditto
  • kjs/collector.h: (KJS::Collector::cellMayHaveRefs): Helper for MarkStack.
  • kjs/MarkStack.h: Added. The actual mark stack implementation. (KJS::MarkStack::push): (KJS::MarkStack::pushAtom): (KJS::MarkStack::pop): (KJS::MarkStack::isEmpty): (KJS::MarkStack::reserveCapacity):

Changed mark() methods to markChildren() methods:


  • kjs/ExecState.cpp: (KJS::ExecState::markChildren):
  • kjs/ExecState.h:
  • kjs/JSWrapperObject.cpp: (KJS::JSWrapperObject::markChildren):
  • kjs/JSWrapperObject.h:
  • kjs/array_instance.cpp: (KJS::ArrayInstance::markChildren):
  • kjs/array_instance.h:
  • kjs/bool_object.cpp: (BooleanInstance::markChildren):
  • kjs/bool_object.h:
  • kjs/error_object.cpp:
  • kjs/error_object.h:
  • kjs/function.cpp: (KJS::FunctionImp::markChildren): (KJS::Arguments::Arguments): (KJS::Arguments::markChildren): (KJS::ActivationImp::markChildren):
  • kjs/function.h:
  • kjs/internal.cpp: (KJS::GetterSetterImp::markChildren):
  • kjs/interpreter.cpp: (KJS::Interpreter::markRoots):
  • kjs/interpreter.h:
  • kjs/list.cpp: (KJS::List::markProtectedListsSlowCase):
  • kjs/list.h: (KJS::List::markProtectedLists):
  • kjs/object.cpp: (KJS::JSObject::markChildren):
  • kjs/object.h: (KJS::ScopeChain::markChildren):
  • kjs/property_map.cpp: (KJS::PropertyMap::markChildren):
  • kjs/property_map.h:
  • kjs/scope_chain.h:
  • kjs/string_object.cpp: (KJS::StringInstance::markChildren):
  • kjs/string_object.h:

JavaScriptGlue:

Reviewed by Darin and Geoff.

Fixups for JavaScriptCore mark stack.

  • JSObject.cpp: (JSUserObject::Mark):
  • JSObject.h:
  • JSValueWrapper.cpp: (JSValueWrapper::JSObjectMark):
  • JSValueWrapper.h:
  • UserObjectImp.cpp:
  • UserObjectImp.h:

WebCore:

Reviewed by Darin and Geoff.

Implement mark stack. This version is not suitable for prime time because it makes a
huge allocation on every collect, and potentially makes marking of detached subtrees
slow. But it is a .2% - .4% speedup even without much tweaking.

I replaced mark() methods with markChildren() as usual. One
optimization that is lost is avoiding walking detached DOM
subtrees more than once to mark them; since marking is not
recursive there's no obvious way to bracket operation on the tree
any more.

  • bindings/js/JSDocumentCustom.cpp: (WebCore::JSDocument::markChildren):
  • bindings/js/JSNodeCustom.cpp: (WebCore::JSNode::markChildren):
  • bindings/js/JSNodeFilterCondition.cpp:
  • bindings/js/JSNodeFilterCondition.h:
  • bindings/js/JSNodeFilterCustom.cpp: (WebCore::JSNodeFilter::markChildren):
  • bindings/js/JSNodeIteratorCustom.cpp: (WebCore::JSNodeIterator::markChildren):
  • bindings/js/JSTreeWalkerCustom.cpp: (WebCore::JSTreeWalker::markChildren):
  • bindings/js/JSXMLHttpRequest.cpp: (KJS::JSXMLHttpRequest::markChildren):
  • bindings/js/JSXMLHttpRequest.h:
  • bindings/js/kjs_binding.cpp: (KJS::ScriptInterpreter::markDOMNodesForDocument):
  • bindings/js/kjs_binding.h:
  • bindings/js/kjs_events.cpp: (WebCore::JSUnprotectedEventListener::markChildren):
  • bindings/js/kjs_events.h:
  • bindings/js/kjs_window.cpp: (KJS::Window::markChildren):
  • bindings/js/kjs_window.h:
  • bindings/scripts/CodeGeneratorJS.pm:
  • dom/Node.cpp: (WebCore::Node::Node):
  • dom/Node.h:
  • dom/NodeFilter.h:
  • dom/NodeFilterCondition.h:

LayoutTests:

Not reviewed.


I have fixed this with the mark stack work.


  • fast/js/gc-breadth-2-expected.txt: Added.
  • fast/js/gc-breadth-2.html: Added.
  • fast/js/gc-breadth-expected.txt: Added.
  • fast/js/gc-breadth.html: Added.
  • fast/js/gc-depth-expected.txt: Added.
  • fast/js/gc-depth.html: Added.
  • fast/js/resources/gc-breadth-2.js: Added.
  • fast/js/resources/gc-breadth.js: Added.
  • fast/js/resources/gc-depth.js: Added.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/collector.cpp

    r27574 r28106  
    2727#include "internal.h"
    2828#include "list.h"
     29#include "MarkStack.h"
    2930#include "value.h"
    3031#include <algorithm>
     
    278279    targetBlock = (Block*)allocateBlock();
    279280    targetBlock->freeList = targetBlock->cells;
     281    if (heapType == PrimaryHeap)
     282        targetBlock->mayHaveRefs = 1;
    280283    targetBlockUsedCells = 0;
    281284    heap.blocks[usedBlocks] = (CollectorBlock*)targetBlock;
     
    480483#define IS_HALF_CELL_ALIGNED(p) (((intptr_t)(p) & (CELL_MASK >> 1)) == 0)
    481484
    482 void Collector::markStackObjectsConservatively(void *start, void *end)
     485static inline void drainMarkStack(MarkStack& stack)
     486{
     487    while (!stack.isEmpty())
     488        stack.pop()->markChildren(stack);
     489}
     490
     491
     492void Collector::markStackObjectsConservatively(MarkStack& stack, void *start, void *end)
    483493{
    484494  if (start > end) {
     
    522532                  if (((CollectorCell*)xAsBits)->u.freeCell.zeroIfFree != 0) {
    523533                      JSCell* imp = reinterpret_cast<JSCell*>(xAsBits);
    524                       if (!imp->marked())
    525                           imp->mark();
     534                      stack.push(imp);
     535                      drainMarkStack(stack);
    526536                  }
    527537                  break;
     
    534544}
    535545
    536 void Collector::markCurrentThreadConservatively()
     546void Collector::markCurrentThreadConservatively(MarkStack& stack)
    537547{
    538548    // setjmp forces volatile registers onto the stack
     
    551561    void* stackBase = currentThreadStackBase();
    552562
    553     markStackObjectsConservatively(stackPointer, stackBase);
     563    markStackObjectsConservatively(stack, stackPointer, stackBase);
    554564}
    555565
     
    694704}
    695705
    696 void Collector::markOtherThreadConservatively(Thread* thread)
     706void Collector::markOtherThreadConservatively(MarkStack& stack, Thread* thread)
    697707{
    698708  suspendThread(thread->platformThread);
     
    702712
    703713  // mark the thread's registers
    704   markStackObjectsConservatively((void*)&regs, (void*)((char*)&regs + regSize));
     714  markStackObjectsConservatively(stack, (void*)&regs, (void*)((char*)&regs + regSize));
    705715 
    706716  void* stackPointer = otherThreadStackPointer(regs);
    707717  void* stackBase = otherThreadStackBase(regs, thread);
    708   markStackObjectsConservatively(stackPointer, stackBase);
     718  markStackObjectsConservatively(stack, stackPointer, stackBase);
    709719
    710720  resumeThread(thread->platformThread);
     
    713723#endif
    714724
    715 void Collector::markStackObjectsConservatively()
    716 {
    717   markCurrentThreadConservatively();
     725void Collector::markStackObjectsConservatively(MarkStack& stack)
     726{
     727  markCurrentThreadConservatively(stack);
    718728
    719729#if USE(MULTIPLE_THREADS)
    720730  for (Thread *thread = registeredThreads; thread != NULL; thread = thread->next) {
    721731    if (!pthread_equal(thread->posixThread, pthread_self())) {
    722       markOtherThreadConservatively(thread);
     732        markOtherThreadConservatively(stack, thread);
    723733    }
    724734  }
     
    772782}
    773783
    774 void Collector::markProtectedObjects()
     784void Collector::markProtectedObjects(MarkStack& stack)
    775785{
    776786  ProtectCountSet& protectedValues = KJS::protectedValues();
    777787  ProtectCountSet::iterator end = protectedValues.end();
    778788  for (ProtectCountSet::iterator it = protectedValues.begin(); it != end; ++it) {
    779     JSCell *val = it->first;
    780     if (!val->marked())
    781       val->mark();
    782   }
    783 }
    784 
    785 void Collector::markMainThreadOnlyObjects()
     789    stack.push(it->first);
     790    drainMarkStack(stack);
     791  }
     792}
     793
     794void Collector::markMainThreadOnlyObjects(MarkStack& stack)
    786795{
    787796#if USE(MULTIPLE_THREADS)
     
    815824                    if (!curBlock->marked.get(i)) {
    816825                        JSCell* imp = reinterpret_cast<JSCell*>(cell);
    817                         imp->mark();
     826                        stack.push(imp);
     827                        drainMarkStack(stack);
    818828                    }
    819829                    if (++count == mainThreadOnlyObjectCount)
     
    951961  // MARK: first mark all referenced objects recursively starting out from the set of root objects
    952962
     963  size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects;
     964
     965  MarkStack stack;
     966  stack.reserveCapacity(primaryHeap.numLiveObjects);
     967
    953968#ifndef NDEBUG
    954969  // Forbid malloc during the mark phase. Marking a thread suspends it, so
    955   // a malloc inside mark() would risk a deadlock with a thread that had been
     970  // a malloc inside markChildren() would risk a deadlock with a thread that had been
    956971  // suspended while holding the malloc lock.
    957972  fastMallocForbid();
     
    961976    Interpreter* scr = Interpreter::s_hook;
    962977    do {
    963       scr->mark();
     978      scr->markRoots(stack);
     979      drainMarkStack(stack);
    964980      scr = scr->next;
    965981    } while (scr != Interpreter::s_hook);
    966982  }
    967983
    968   markStackObjectsConservatively();
    969   markProtectedObjects();
    970   List::markProtectedLists();
     984  markStackObjectsConservatively(stack);
     985  markProtectedObjects(stack);
     986  List::markProtectedLists(stack);
     987  drainMarkStack(stack);
    971988#if USE(MULTIPLE_THREADS)
    972989  if (!currentThreadIsMainThread)
    973     markMainThreadOnlyObjects();
     990    markMainThreadOnlyObjects(stack);
    974991#endif
    975992
     
    978995#endif
    979996   
    980   size_t originalLiveObjects = primaryHeap.numLiveObjects + numberHeap.numLiveObjects;
    981997  size_t numLiveObjects = sweep<PrimaryHeap>(currentThreadIsMainThread);
    982998  numLiveObjects += sweep<NumberHeap>(currentThreadIsMainThread);
Note: See TracChangeset for help on using the changeset viewer.