Changeset 116659 in webkit


Ignore:
Timestamp:
May 10, 2012 10:32:39 AM (12 years ago)
Author:
msaboff@apple.com
Message:

Enh: Hash Const JSString in Backing Stores to Save Memory
https://bugs.webkit.org/show_bug.cgi?id=86024

Reviewed by Filip Pizlo.

During garbage collection, each marking thread keeps a HashMap of
strings. While visiting via MarkStack::copyAndAppend(), we check to
see if the string we are visiting is already in the HashMap. If not
we add it. If so, we change the reference to the current string we're
visiting to the prior string.

To somewhat reduce the performance impact of this change, if a string
is unique at the end of a marking it will not be checked during further
GC phases. In some cases this won't catch all duplicates, but we are
trying to catch the growth of duplicate strings.

  • heap/Heap.cpp:

(JSC::Heap::markRoots):

  • heap/MarkStack.cpp:

(JSC::MarkStackThreadSharedData::resetChildren): New method called by the
main thread to reset the slave threads. This is primarily done to
clear the m_uniqueStrings HashMap.
(JSC):
(JSC::MarkStackThreadSharedData::markingThreadMain):
(JSC::MarkStackThreadSharedData::markingThreadStartFunc):
(JSC::MarkStackThreadSharedData::MarkStackThreadSharedData):
(JSC::MarkStackThreadSharedData::reset):
(JSC::MarkStack::reset): Added call to clear m_uniqueStrings.
(JSC::MarkStack::internalAppend): New method that performs the hash consting.
(JSC::SlotVisitor::copyAndAppend): Changed to call the new hash consting
internalAppend()

  • heap/MarkStack.h:

(MarkStackThreadSharedData):
(MarkStack):
(JSC::MarkStack::sharedData):

  • runtime/JSString.h:

(JSString): Added m_isHashConstSingleton flag, accessors for the flag and
code to initialize the flag.
(JSC::JSString::finishCreation):
(JSC::JSString::isHashConstSingleton):
(JSC::JSString::clearHashConstSingleton):
(JSC::JSString::setHashConstSingleton):
(JSC::JSRopeString::finishCreation):

Location:
trunk/Source/JavaScriptCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r116593 r116659  
     12012-05-10  Michael Saboff  <msaboff@apple.com>
     2
     3        Enh: Hash Const JSString in Backing Stores to Save Memory
     4        https://bugs.webkit.org/show_bug.cgi?id=86024
     5
     6        Reviewed by Filip Pizlo.
     7
     8        During garbage collection, each marking thread keeps a HashMap of
     9        strings.  While visiting via MarkStack::copyAndAppend(), we check to
     10        see if the string we are visiting is already in the HashMap.  If not
     11        we add it.  If so, we change the reference to the current string we're
     12        visiting to the prior string.
     13
     14        To somewhat reduce the performance impact of this change, if a string
     15        is unique at the end of a marking it will not be checked during further
     16        GC phases.  In some cases this won't catch all duplicates, but we are
     17        trying to catch the growth of duplicate strings.
     18
     19        * heap/Heap.cpp:
     20        (JSC::Heap::markRoots):
     21        * heap/MarkStack.cpp:
     22        (JSC::MarkStackThreadSharedData::resetChildren): New method called by the
     23        main thread to reset the slave threads.  This is primarily done to
     24        clear the m_uniqueStrings HashMap.
     25        (JSC):
     26        (JSC::MarkStackThreadSharedData::markingThreadMain):
     27        (JSC::MarkStackThreadSharedData::markingThreadStartFunc):
     28        (JSC::MarkStackThreadSharedData::MarkStackThreadSharedData):
     29        (JSC::MarkStackThreadSharedData::reset):
     30        (JSC::MarkStack::reset): Added call to clear m_uniqueStrings.
     31        (JSC::MarkStack::internalAppend): New method that performs the hash consting.
     32        (JSC::SlotVisitor::copyAndAppend): Changed to call the new hash consting
     33        internalAppend()
     34        * heap/MarkStack.h:
     35        (MarkStackThreadSharedData):
     36        (MarkStack):
     37        (JSC::MarkStack::sharedData):
     38        * runtime/JSString.h:
     39        (JSString): Added m_isHashConstSingleton flag, accessors for the flag and
     40        code to initialize the flag.
     41        (JSC::JSString::finishCreation):
     42        (JSC::JSString::isHashConstSingleton):
     43        (JSC::JSString::clearHashConstSingleton):
     44        (JSC::JSString::setHashConstSingleton):
     45        (JSC::JSRopeString::finishCreation):
     46
    1472012-05-09  Filip Pizlo  <fpizlo@apple.com>
    248
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r116044 r116659  
    657657    visitor.reset();
    658658    m_sharedData.reset();
     659#if ENABLE(PARALLEL_GC)
     660    m_sharedData.resetChildren();
     661#endif
    659662    m_storageSpace.doneCopying();
    660663
  • trunk/Source/JavaScriptCore/heap/MarkStack.cpp

    r113455 r116659  
    3737#include "ScopeChain.h"
    3838#include "Structure.h"
     39#include "UString.h"
    3940#include "WriteBarrier.h"
    4041#include <wtf/MainThread.h>
     
    219220
    220221#if ENABLE(PARALLEL_GC)
    221 void MarkStackThreadSharedData::markingThreadMain()
     222void MarkStackThreadSharedData::resetChildren()
     223{
     224    for (unsigned i = 0; i < m_slaveMarkStacks.size(); ++i)
     225       m_slaveMarkStacks[i]->reset();
     226}
     227
     228void MarkStackThreadSharedData::markingThreadMain(SlotVisitor* slotVisitor)
    222229{
    223230    WTF::registerGCThread();
    224     SlotVisitor slotVisitor(*this);
    225     ParallelModeEnabler enabler(slotVisitor);
    226     slotVisitor.drainFromShared(SlotVisitor::SlaveDrain);
    227 }
    228 
    229 void MarkStackThreadSharedData::markingThreadStartFunc(void* shared)
    230 {
    231     static_cast<MarkStackThreadSharedData*>(shared)->markingThreadMain();
     231    ParallelModeEnabler enabler(*slotVisitor);
     232    slotVisitor->drainFromShared(SlotVisitor::SlaveDrain);
     233    delete slotVisitor;
     234}
     235
     236void MarkStackThreadSharedData::markingThreadStartFunc(void* myVisitor)
     237{
     238    SlotVisitor* slotVisitor = static_cast<SlotVisitor*>(myVisitor);
     239    slotVisitor->sharedData().markingThreadMain(slotVisitor);
    232240}
    233241#endif
     
    242250#if ENABLE(PARALLEL_GC)
    243251    for (unsigned i = 1; i < Options::numberOfGCMarkers; ++i) {
    244         m_markingThreads.append(createThread(markingThreadStartFunc, this, "JavaScriptCore::Marking"));
     252        SlotVisitor* slotVisitor = new SlotVisitor(*this);
     253        m_slaveMarkStacks.append(slotVisitor);
     254        m_markingThreads.append(createThread(markingThreadStartFunc, slotVisitor, "JavaScriptCore::Marking"));
    245255        ASSERT(m_markingThreads.last());
    246256    }
     
    274284    ASSERT(m_opaqueRoots.isEmpty());
    275285#endif
    276    
    277286    m_weakReferenceHarvesters.removeAll();
    278287}
     
    287296    m_opaqueRoots.clear();
    288297#endif
     298    m_uniqueStrings.clear();
    289299}
    290300
     
    487497}
    488498
     499inline void MarkStack::internalAppend(JSValue* slot)
     500{
     501    ASSERT(slot);
     502    JSValue value = *slot;
     503    ASSERT(value);
     504    if (!value.isCell())
     505        return;
     506
     507    if (value.isString()) {
     508        JSString* string = jsCast<JSString*>(value.asCell());
     509        if (!string->isHashConstSingleton() && string->length() > 1 && !string->isRope()) {
     510            UniqueStringMap::AddResult addResult = m_uniqueStrings.add(string->string().impl(), value);
     511            if (addResult.isNewEntry)
     512                string->setHashConstSingleton();
     513            else {
     514                JSValue existingJSValue = addResult.iterator->second;
     515                if (value != existingJSValue)
     516                    jsCast<JSString*>(existingJSValue.asCell())->clearHashConstSingleton();
     517                *slot = existingJSValue;
     518                return;
     519            }
     520        }
     521    }
     522
     523    internalAppend(value.asCell());
     524}
     525
     526
    489527void SlotVisitor::copyAndAppend(void** ptr, size_t bytes, JSValue* values, unsigned length)
    490528{
     
    500538            if (!value)
    501539                continue;
    502             internalAppend(value);
     540            internalAppend(&newValues[i]);
    503541        }
    504542
  • trunk/Source/JavaScriptCore/heap/MarkStack.h

    r108119 r116659  
    3535#include "VTableSpectrum.h"
    3636#include "WeakReferenceHarvester.h"
     37#include <wtf/Forward.h>
    3738#include <wtf/HashMap.h>
    3839#include <wtf/HashSet.h>
     
    4142#include <wtf/OSAllocator.h>
    4243#include <wtf/PageBlock.h>
     44#include <wtf/text/StringHash.h>
    4345
    4446namespace JSC {
     
    172174       
    173175        void reset();
    174    
     176
     177#if ENABLE(PARALLEL_GC)
     178        void resetChildren();
     179#endif
     180
    175181    private:
    176182        friend class MarkStack;
     
    178184
    179185#if ENABLE(PARALLEL_GC)
    180         void markingThreadMain();
     186        void markingThreadMain(SlotVisitor*);
    181187        static void markingThreadStartFunc(void* heap);
    182188#endif
     
    188194       
    189195        Vector<ThreadIdentifier> m_markingThreads;
     196        Vector<MarkStack*> m_slaveMarkStacks;
    190197       
    191198        Mutex m_markingLock;
     
    222229        bool containsOpaqueRoot(void*);
    223230        int opaqueRootCount();
    224        
     231
     232        MarkStackThreadSharedData& sharedData() { return m_shared; }
    225233        bool isEmpty() { return m_stack.isEmpty(); }
    226234
     
    228236
    229237        size_t visitCount() const { return m_visitCount; }
    230 
     238       
    231239#if ENABLE(SIMPLE_HEAP_PROFILING)
    232240        VTableSpectrum m_visitedTypeCounts;
     
    252260        void internalAppend(JSCell*);
    253261        void internalAppend(JSValue);
     262        void internalAppend(JSValue*);
    254263       
    255264        JS_EXPORT_PRIVATE void mergeOpaqueRoots();
     
    271280        MarkStackArray m_stack;
    272281        HashSet<void*> m_opaqueRoots; // Handle-owning data structures not visible to the garbage collector.
     282        typedef HashMap<StringImpl*, JSValue> UniqueStringMap;
     283        UniqueStringMap m_uniqueStrings;
    273284       
    274285#if !ASSERT_DISABLED
  • trunk/Source/JavaScriptCore/runtime/JSString.h

    r115656 r116659  
    6868        friend class SpecializedThunkJIT;
    6969        friend class JSRopeString;
     70        friend class MarkStack;
    7071        friend struct ThunkHelpers;
    7172
     
    9293            m_length = length;
    9394            m_is8Bit = m_value.impl()->is8Bit();
     95            m_isHashConstSingleton = false;
    9496        }
    9597
     
    100102            m_length = length;
    101103            m_is8Bit = m_value.impl()->is8Bit();
     104            m_isHashConstSingleton = false;
    102105            Heap::heap(this)->reportExtraMemoryCost(cost);
    103106        }
     
    109112            m_length = 0;
    110113            m_is8Bit = true;
     114            m_isHashConstSingleton = false;
    111115        }
    112116       
     
    162166        bool isRope() const { return m_value.isNull(); }
    163167        bool is8Bit() const { return m_is8Bit; }
     168        bool isHashConstSingleton() const { return m_isHashConstSingleton; }
     169        void clearHashConstSingleton() { m_isHashConstSingleton = false; }
     170        void setHashConstSingleton() { m_isHashConstSingleton = true; }
    164171
    165172        // A string is represented either by a UString or a rope of fibers.
    166173        bool m_is8Bit : 1;
     174        bool m_isHashConstSingleton : 1;
    167175        unsigned m_length;
    168176        mutable UString m_value;
     
    234242            m_length = s1->length() + s2->length();
    235243            m_is8Bit = (s1->is8Bit() && s2->is8Bit());
     244            m_isHashConstSingleton = false;
    236245            m_fibers[0].set(globalData, this, s1);
    237246            m_fibers[1].set(globalData, this, s2);
     
    243252            m_length = s1->length() + s2->length() + s3->length();
    244253            m_is8Bit = (s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
     254            m_isHashConstSingleton = false;
    245255            m_fibers[0].set(globalData, this, s1);
    246256            m_fibers[1].set(globalData, this, s2);
Note: See TracChangeset for help on using the changeset viewer.