Ignore:
Timestamp:
Oct 31, 2011, 11:43:37 PM (14 years ago)
Author:
[email protected]
Message:

The GC should be parallel
https://bugs.webkit.org/show_bug.cgi?id=70995

Source/JavaScriptCore:

Reviewed by Geoff Garen.

Added parallel tracing to the GC. This works by having local mark
stacks per thread, and a global shared one. Threads sometimes
donate cells from the mark stack to the global one if the heuristics
tell them that it's affordable to do so. Threads that have depleted
their local mark stacks try to steal some from the shared one.

Marking is now done using an atomic weak relaxed CAS (compare-and-swap).

This is a 23% speed-up on V8-splay when I use 4 marking threads,
leading to a 3.5% speed-up on V8.

It also appears that this reduces GC pause times on real websites by
more than half.

(JSC::Heap::Heap):
(JSC::Heap::~Heap):
(JSC::Heap::markRoots):

  • heap/Heap.h:
  • heap/MarkStack.cpp:

(JSC::MarkStackSegmentAllocator::MarkStackSegmentAllocator):
(JSC::MarkStackSegmentAllocator::~MarkStackSegmentAllocator):
(JSC::MarkStackSegmentAllocator::allocate):
(JSC::MarkStackSegmentAllocator::release):
(JSC::MarkStackSegmentAllocator::shrinkReserve):
(JSC::MarkStackArray::MarkStackArray):
(JSC::MarkStackArray::~MarkStackArray):
(JSC::MarkStackArray::expand):
(JSC::MarkStackArray::refill):
(JSC::MarkStackArray::donateSomeCellsTo):
(JSC::MarkStackArray::stealSomeCellsFrom):
(JSC::MarkStackThreadSharedData::markingThreadMain):
(JSC::MarkStackThreadSharedData::markingThreadStartFunc):
(JSC::MarkStackThreadSharedData::MarkStackThreadSharedData):
(JSC::MarkStackThreadSharedData::~MarkStackThreadSharedData):
(JSC::MarkStackThreadSharedData::reset):
(JSC::MarkStack::reset):
(JSC::SlotVisitor::donateSlow):
(JSC::SlotVisitor::drain):
(JSC::SlotVisitor::drainFromShared):
(JSC::MarkStack::mergeOpaqueRoots):
(JSC::SlotVisitor::harvestWeakReferences):

  • heap/MarkStack.h:

(JSC::MarkStackSegment::data):
(JSC::MarkStackSegment::capacityFromSize):
(JSC::MarkStackSegment::sizeFromCapacity):
(JSC::MarkStackArray::postIncTop):
(JSC::MarkStackArray::preDecTop):
(JSC::MarkStackArray::setTopForFullSegment):
(JSC::MarkStackArray::setTopForEmptySegment):
(JSC::MarkStackArray::top):
(JSC::MarkStackArray::validatePrevious):
(JSC::MarkStack::addWeakReferenceHarvester):
(JSC::MarkStack::mergeOpaqueRootsIfNecessary):
(JSC::MarkStack::mergeOpaqueRootsIfProfitable):
(JSC::MarkStack::MarkStack):
(JSC::MarkStack::addOpaqueRoot):
(JSC::MarkStack::containsOpaqueRoot):
(JSC::MarkStack::opaqueRootCount):
(JSC::MarkStackArray::append):
(JSC::MarkStackArray::canRemoveLast):
(JSC::MarkStackArray::removeLast):
(JSC::MarkStackArray::isEmpty):
(JSC::MarkStackArray::canDonateSomeCells):
(JSC::MarkStackArray::size):
(JSC::ParallelModeEnabler::ParallelModeEnabler):
(JSC::ParallelModeEnabler::~ParallelModeEnabler):

  • heap/MarkedBlock.h:

(JSC::MarkedBlock::testAndSetMarked):

  • heap/SlotVisitor.h:

(JSC::SlotVisitor::donate):
(JSC::SlotVisitor::donateAndDrain):
(JSC::SlotVisitor::donateKnownParallel):
(JSC::SlotVisitor::SlotVisitor):

  • heap/WeakReferenceHarvester.h:
  • runtime/Heuristics.cpp:

(JSC::Heuristics::initializeHeuristics):

  • runtime/Heuristics.h:
  • wtf/Atomics.h:

(WTF::weakCompareAndSwap):

  • wtf/Bitmap.h:

(WTF::::Bitmap):
(WTF::::get):
(WTF::::set):
(WTF::::testAndSet):
(WTF::::testAndClear):
(WTF::::concurrentTestAndSet):
(WTF::::concurrentTestAndClear):
(WTF::::clear):
(WTF::::clearAll):
(WTF::::nextPossiblyUnset):
(WTF::::findRunOfZeros):
(WTF::::count):
(WTF::::isEmpty):
(WTF::::isFull):

  • wtf/MainThread.h:

(WTF::isMainThreadOrGCThread):

  • wtf/Platform.h:
  • wtf/ThreadSpecific.h:

(WTF::::isSet):

  • wtf/mac/MainThreadMac.mm:

(WTF::initializeGCThreads):
(WTF::initializeMainThreadPlatform):
(WTF::initializeMainThreadToProcessMainThreadPlatform):
(WTF::registerGCThread):
(WTF::isMainThreadOrGCThread):

Source/WebCore:

Reviewed by Geoff Garen.

Added parallel tracing to the GC. This required loosening some assertions,
since some code may now be called from outside the main thread.

No new tests, since no behavior was changed.

  • platform/TreeShared.h:

(WebCore::TreeShared::parent):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/heap/MarkStack.h

    r97642 r98937  
    2828
    2929#include "HandleTypes.h"
     30#include "Heuristics.h"
    3031#include "JSValue.h"
    3132#include "Register.h"
     
    4445    class JSGlobalData;
    4546    class MarkStack;
     47    class ParallelModeEnabler;
    4648    class Register;
     49    class SlotVisitor;
    4750    template<typename T> class WriteBarrierBase;
    4851    template<typename T> class JITWriteBarrier;
    4952   
     53    struct MarkStackSegment {
     54        MarkStackSegment* m_previous;
     55#if !ASSERT_DISABLED
     56        size_t m_top;
     57#endif
     58       
     59        const JSCell** data()
     60        {
     61            return bitwise_cast<const JSCell**>(this + 1);
     62        }
     63       
     64        static size_t capacityFromSize(size_t size)
     65        {
     66            return (size - sizeof(MarkStackSegment)) / sizeof(const JSCell*);
     67        }
     68       
     69        static size_t sizeFromCapacity(size_t capacity)
     70        {
     71            return sizeof(MarkStackSegment) + capacity * sizeof(const JSCell*);
     72        }
     73    };
     74
     75    class MarkStackSegmentAllocator {
     76    public:
     77        MarkStackSegmentAllocator();
     78        ~MarkStackSegmentAllocator();
     79       
     80        MarkStackSegment* allocate();
     81        void release(MarkStackSegment*);
     82       
     83        void shrinkReserve();
     84       
     85    private:
     86        Mutex m_lock;
     87        MarkStackSegment* m_nextFreeSegment;
     88    };
     89
    5090    class MarkStackArray {
    5191    public:
    52         MarkStackArray();
     92        MarkStackArray(MarkStackSegmentAllocator&);
    5393        ~MarkStackArray();
    5494
     95        void append(const JSCell*);
     96
     97        bool canRemoveLast();
     98        const JSCell* removeLast();
     99        bool refill();
     100       
     101        bool isEmpty();
     102       
     103        bool canDonateSomeCells(); // Returns false if you should definitely not call doanteSomeCellsTo().
     104        bool donateSomeCellsTo(MarkStackArray& other); // Returns true if some cells were donated.
     105       
     106        void stealSomeCellsFrom(MarkStackArray& other);
     107
     108        size_t size();
     109
     110    private:
     111        MarkStackSegment* m_topSegment;
     112       
    55113        void expand();
    56         void append(const JSCell*);
    57 
    58         const JSCell* removeLast();
    59 
    60         bool isEmpty();
    61 
    62         void shrinkAllocation(size_t);
    63 
     114       
     115        MarkStackSegmentAllocator& m_allocator;
     116
     117        size_t m_segmentCapacity;
     118        size_t m_top;
     119        size_t m_numberOfPreviousSegments;
     120       
     121        size_t postIncTop()
     122        {
     123            size_t result = m_top++;
     124            ASSERT(result == m_topSegment->m_top++);
     125            return result;
     126        }
     127       
     128        size_t preDecTop()
     129        {
     130            size_t result = --m_top;
     131            ASSERT(result == --m_topSegment->m_top);
     132            return result;
     133        }
     134       
     135        void setTopForFullSegment()
     136        {
     137            ASSERT(m_topSegment->m_top == m_segmentCapacity);
     138            m_top = m_segmentCapacity;
     139        }
     140       
     141        void setTopForEmptySegment()
     142        {
     143            ASSERT(!m_topSegment->m_top);
     144            m_top = 0;
     145        }
     146       
     147        size_t top()
     148        {
     149            ASSERT(m_top == m_topSegment->m_top);
     150            return m_top;
     151        }
     152       
     153#if ASSERT_DISABLED
     154        void validatePrevious() { }
     155#else
     156        void validatePrevious()
     157        {
     158            unsigned count = 0;
     159            for (MarkStackSegment* current = m_topSegment->m_previous; current; current = current->m_previous)
     160                count++;
     161            ASSERT(count == m_numberOfPreviousSegments);
     162        }
     163#endif
     164    };
     165
     166    class MarkStackThreadSharedData {
     167    public:
     168        MarkStackThreadSharedData(JSGlobalData*);
     169        ~MarkStackThreadSharedData();
     170       
     171        void reset();
     172   
    64173    private:
    65         const JSCell** m_data;
    66         size_t m_top;
    67         size_t m_capacity;
    68         size_t m_allocated;
     174        friend class MarkStack;
     175        friend class SlotVisitor;
     176
     177#if ENABLE(PARALLEL_GC)
     178        void markingThreadMain();
     179        static void* markingThreadStartFunc(void* heap);
     180#endif
     181
     182        JSGlobalData* m_globalData;
     183       
     184        MarkStackSegmentAllocator m_segmentAllocator;
     185       
     186        Vector<ThreadIdentifier> m_markingThreads;
     187       
     188        Mutex m_markingLock;
     189        ThreadCondition m_markingCondition;
     190        MarkStackArray m_sharedMarkStack;
     191        unsigned m_numberOfActiveParallelMarkers;
     192        bool m_parallelMarkersShouldExit;
     193
     194        Mutex m_opaqueRootsLock;
     195        HashSet<void*> m_opaqueRoots;
     196
     197        Mutex m_weakReferenceHarvesterLock;
     198        WeakReferenceHarvester* m_firstWeakReferenceHarvester;
    69199    };
    70200
     
    74204
    75205    public:
    76         static void* allocateStack(size_t);
    77         static void releaseStack(void*, size_t);
    78 
    79         MarkStack(void* jsArrayVPtr, void* jsFinalObjectVPtr, void* jsStringVPtr);
     206        MarkStack(MarkStackThreadSharedData&, void* jsArrayVPtr, void* jsFinalObjectVPtr, void* jsStringVPtr);
    80207        ~MarkStack();
    81208
     
    89216        void appendUnbarrieredPointer(T**);
    90217       
    91         bool addOpaqueRoot(void*);
     218        void addOpaqueRoot(void*);
    92219        bool containsOpaqueRoot(void*);
    93220        int opaqueRootCount();
     
    103230        void addWeakReferenceHarvester(WeakReferenceHarvester* weakReferenceHarvester)
    104231        {
     232            MutexLocker locker(m_shared.m_weakReferenceHarvesterLock);
    105233            if (weakReferenceHarvester->m_nextAndFlag & 1)
    106234                return;
    107             weakReferenceHarvester->m_nextAndFlag = reinterpret_cast<uintptr_t>(m_firstWeakReferenceHarvester) | 1;
    108             m_firstWeakReferenceHarvester = weakReferenceHarvester;
     235            weakReferenceHarvester->m_nextAndFlag = reinterpret_cast<uintptr_t>(m_shared.m_firstWeakReferenceHarvester) | 1;
     236            m_shared.m_firstWeakReferenceHarvester = weakReferenceHarvester;
    109237        }
    110238
     
    118246        void internalAppend(JSCell*);
    119247        void internalAppend(JSValue);
    120 
     248       
     249        void mergeOpaqueRoots();
     250       
     251        void mergeOpaqueRootsIfNecessary()
     252        {
     253            if (m_opaqueRoots.isEmpty())
     254                return;
     255            mergeOpaqueRoots();
     256        }
     257       
     258        void mergeOpaqueRootsIfProfitable()
     259        {
     260            if (static_cast<unsigned>(m_opaqueRoots.size()) < Heuristics::opaqueRootMergeThreshold)
     261                return;
     262            mergeOpaqueRoots();
     263        }
     264       
    121265        MarkStackArray m_stack;
    122266        void* m_jsArrayVPtr;
     
    124268        void* m_jsStringVPtr;
    125269        HashSet<void*> m_opaqueRoots; // Handle-owning data structures not visible to the garbage collector.
    126         WeakReferenceHarvester* m_firstWeakReferenceHarvester;
    127270       
    128271#if !ASSERT_DISABLED
     
    132275#endif
    133276    protected:
     277        friend class ParallelModeEnabler;
     278       
    134279        size_t m_visitCount;
    135     };
    136 
    137     inline MarkStack::MarkStack(void* jsArrayVPtr, void* jsFinalObjectVPtr, void* jsStringVPtr)
    138         : m_jsArrayVPtr(jsArrayVPtr)
     280        bool m_isInParallelMode;
     281       
     282        MarkStackThreadSharedData& m_shared;
     283    };
     284
     285    inline MarkStack::MarkStack(MarkStackThreadSharedData& shared, void* jsArrayVPtr, void* jsFinalObjectVPtr, void* jsStringVPtr)
     286        : m_stack(shared.m_segmentAllocator)
     287        , m_jsArrayVPtr(jsArrayVPtr)
    139288        , m_jsFinalObjectVPtr(jsFinalObjectVPtr)
    140289        , m_jsStringVPtr(jsStringVPtr)
    141         , m_firstWeakReferenceHarvester(0)
    142290#if !ASSERT_DISABLED
    143291        , m_isCheckingForDefaultMarkViolation(false)
     
    145293#endif
    146294        , m_visitCount(0)
     295        , m_isInParallelMode(false)
     296        , m_shared(shared)
    147297    {
    148298    }
     
    153303    }
    154304
    155     inline bool MarkStack::addOpaqueRoot(void* root)
    156     {
    157         return m_opaqueRoots.add(root).second;
     305    inline void MarkStack::addOpaqueRoot(void* root)
     306    {
     307#if ENABLE(PARALLEL_GC)
     308        if (Heuristics::numberOfGCMarkers == 1) {
     309            // Put directly into the shared HashSet.
     310            m_shared.m_opaqueRoots.add(root);
     311            return;
     312        }
     313        // Put into the local set, but merge with the shared one every once in
     314        // a while to make sure that the local sets don't grow too large.
     315        mergeOpaqueRootsIfProfitable();
     316        m_opaqueRoots.add(root);
     317#else
     318        m_opaqueRoots.add(root);
     319#endif
    158320    }
    159321
    160322    inline bool MarkStack::containsOpaqueRoot(void* root)
    161323    {
     324        ASSERT(!m_isInParallelMode);
     325#if ENABLE(PARALLEL_GC)
     326        ASSERT(m_opaqueRoots.isEmpty());
     327        return m_shared.m_opaqueRoots.contains(root);
     328#else
    162329        return m_opaqueRoots.contains(root);
     330#endif
    163331    }
    164332
    165333    inline int MarkStack::opaqueRootCount()
    166334    {
     335        ASSERT(!m_isInParallelMode);
     336#if ENABLE(PARALLEL_GC)
     337        ASSERT(m_opaqueRoots.isEmpty());
     338        return m_shared.m_opaqueRoots.size();
     339#else
    167340        return m_opaqueRoots.size();
    168     }
    169 
    170     inline void* MarkStack::allocateStack(size_t size)
    171     {
    172         return OSAllocator::reserveAndCommit(size);
    173     }
    174 
    175     inline void MarkStack::releaseStack(void* addr, size_t size)
    176     {
    177         OSAllocator::decommitAndRelease(addr, size);
     341#endif
    178342    }
    179343
    180344    inline void MarkStackArray::append(const JSCell* cell)
    181345    {
    182         if (m_top == m_capacity)
     346        if (m_top == m_segmentCapacity)
    183347            expand();
    184         m_data[m_top++] = cell;
     348        m_topSegment->data()[postIncTop()] = cell;
     349    }
     350
     351    inline bool MarkStackArray::canRemoveLast()
     352    {
     353        return !!m_top;
    185354    }
    186355
    187356    inline const JSCell* MarkStackArray::removeLast()
    188357    {
    189         ASSERT(m_top);
    190         return m_data[--m_top];
    191     }
    192    
     358        return m_topSegment->data()[preDecTop()];
     359    }
     360
    193361    inline bool MarkStackArray::isEmpty()
    194362    {
    195         return !m_top;
     363        if (m_top)
     364            return false;
     365        if (m_topSegment->m_previous) {
     366            ASSERT(m_topSegment->m_previous->m_top == m_segmentCapacity);
     367            return false;
     368        }
     369        return true;
     370    }
     371
     372    inline bool MarkStackArray::canDonateSomeCells()
     373    {
     374        size_t numberOfCellsToKeep = Heuristics::minimumNumberOfCellsToKeep;
     375        // Another check: see if we have enough cells to warrant donation.
     376        if (m_top <= numberOfCellsToKeep) {
     377            // This indicates that we might not want to donate anything; check if we have
     378            // another full segment. If not, then don't donate.
     379            if (!m_topSegment->m_previous)
     380                return false;
     381           
     382            ASSERT(m_topSegment->m_previous->m_top == m_segmentCapacity);
     383        }
     384       
     385        return true;
     386    }
     387
     388    inline size_t MarkStackArray::size()
     389    {
     390        return m_top + m_segmentCapacity * m_numberOfPreviousSegments;
    196391    }
    197392
     
    235430    }
    236431
    237     class SlotVisitor;
     432    class ParallelModeEnabler {
     433    public:
     434        ParallelModeEnabler(MarkStack& stack)
     435            : m_stack(stack)
     436        {
     437            ASSERT(!m_stack.m_isInParallelMode);
     438            m_stack.m_isInParallelMode = true;
     439        }
     440       
     441        ~ParallelModeEnabler()
     442        {
     443            ASSERT(m_stack.m_isInParallelMode);
     444            m_stack.m_isInParallelMode = false;
     445        }
     446       
     447    private:
     448        MarkStack& m_stack;
     449    };
    238450
    239451} // namespace JSC
Note: See TracChangeset for help on using the changeset viewer.