Title: [116659] trunk/Source/_javascript_Core
Revision
116659
Author
[email protected]
Date
2012-05-10 10:32:39 -0700 (Thu, 10 May 2012)

Log 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):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (116658 => 116659)


--- trunk/Source/_javascript_Core/ChangeLog	2012-05-10 17:26:32 UTC (rev 116658)
+++ trunk/Source/_javascript_Core/ChangeLog	2012-05-10 17:32:39 UTC (rev 116659)
@@ -1,3 +1,49 @@
+2012-05-10  Michael Saboff  <[email protected]>
+
+        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):
+
 2012-05-09  Filip Pizlo  <[email protected]>
 
         JIT memory allocator is not returning memory to the OS on Darwin

Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (116658 => 116659)


--- trunk/Source/_javascript_Core/heap/Heap.cpp	2012-05-10 17:26:32 UTC (rev 116658)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp	2012-05-10 17:32:39 UTC (rev 116659)
@@ -656,6 +656,9 @@
     visitor.doneCopying();
     visitor.reset();
     m_sharedData.reset();
+#if ENABLE(PARALLEL_GC)
+    m_sharedData.resetChildren();
+#endif
     m_storageSpace.doneCopying();
 
     m_operationInProgress = NoOperation;

Modified: trunk/Source/_javascript_Core/heap/MarkStack.cpp (116658 => 116659)


--- trunk/Source/_javascript_Core/heap/MarkStack.cpp	2012-05-10 17:26:32 UTC (rev 116658)
+++ trunk/Source/_javascript_Core/heap/MarkStack.cpp	2012-05-10 17:32:39 UTC (rev 116659)
@@ -36,6 +36,7 @@
 #include "JSObject.h"
 #include "ScopeChain.h"
 #include "Structure.h"
+#include "UString.h"
 #include "WriteBarrier.h"
 #include <wtf/MainThread.h>
 
@@ -218,17 +219,24 @@
 }
 
 #if ENABLE(PARALLEL_GC)
-void MarkStackThreadSharedData::markingThreadMain()
+void MarkStackThreadSharedData::resetChildren()
 {
+    for (unsigned i = 0; i < m_slaveMarkStacks.size(); ++i)
+       m_slaveMarkStacks[i]->reset();
+}
+
+void MarkStackThreadSharedData::markingThreadMain(SlotVisitor* slotVisitor)
+{
     WTF::registerGCThread();
-    SlotVisitor slotVisitor(*this);
-    ParallelModeEnabler enabler(slotVisitor);
-    slotVisitor.drainFromShared(SlotVisitor::SlaveDrain);
+    ParallelModeEnabler enabler(*slotVisitor);
+    slotVisitor->drainFromShared(SlotVisitor::SlaveDrain);
+    delete slotVisitor;
 }
 
-void MarkStackThreadSharedData::markingThreadStartFunc(void* shared)
+void MarkStackThreadSharedData::markingThreadStartFunc(void* myVisitor)
 {
-    static_cast<MarkStackThreadSharedData*>(shared)->markingThreadMain();
+    SlotVisitor* slotVisitor = static_cast<SlotVisitor*>(myVisitor);
+    slotVisitor->sharedData().markingThreadMain(slotVisitor);
 }
 #endif
 
@@ -241,7 +249,9 @@
 {
 #if ENABLE(PARALLEL_GC)
     for (unsigned i = 1; i < Options::numberOfGCMarkers; ++i) {
-        m_markingThreads.append(createThread(markingThreadStartFunc, this, "_javascript_Core::Marking"));
+        SlotVisitor* slotVisitor = new SlotVisitor(*this);
+        m_slaveMarkStacks.append(slotVisitor);
+        m_markingThreads.append(createThread(markingThreadStartFunc, slotVisitor, "_javascript_Core::Marking"));
         ASSERT(m_markingThreads.last());
     }
 #endif
@@ -273,7 +283,6 @@
 #else
     ASSERT(m_opaqueRoots.isEmpty());
 #endif
-    
     m_weakReferenceHarvesters.removeAll();
 }
 
@@ -286,6 +295,7 @@
 #else
     m_opaqueRoots.clear();
 #endif
+    m_uniqueStrings.clear();
 }
 
 void MarkStack::append(ConservativeRoots& conservativeRoots)
@@ -486,6 +496,34 @@
     return CopiedSpace::allocateFromBlock(m_copyBlock, bytes);
 }
 
+inline void MarkStack::internalAppend(JSValue* slot)
+{
+    ASSERT(slot);
+    JSValue value = *slot;
+    ASSERT(value);
+    if (!value.isCell())
+        return;
+
+    if (value.isString()) {
+        JSString* string = jsCast<JSString*>(value.asCell());
+        if (!string->isHashConstSingleton() && string->length() > 1 && !string->isRope()) {
+            UniqueStringMap::AddResult addResult = m_uniqueStrings.add(string->string().impl(), value);
+            if (addResult.isNewEntry)
+                string->setHashConstSingleton();
+            else {
+                JSValue existingJSValue = addResult.iterator->second;
+                if (value != existingJSValue)
+                    jsCast<JSString*>(existingJSValue.asCell())->clearHashConstSingleton();
+                *slot = existingJSValue;
+                return;
+            }
+        }
+    }
+
+    internalAppend(value.asCell());
+}
+
+
 void SlotVisitor::copyAndAppend(void** ptr, size_t bytes, JSValue* values, unsigned length)
 {
     void* oldPtr = *ptr;
@@ -499,7 +537,7 @@
             newValues[i] = value;
             if (!value)
                 continue;
-            internalAppend(value);
+            internalAppend(&newValues[i]);
         }
 
         memcpy(newPtr, oldPtr, jsValuesOffset);

Modified: trunk/Source/_javascript_Core/heap/MarkStack.h (116658 => 116659)


--- trunk/Source/_javascript_Core/heap/MarkStack.h	2012-05-10 17:26:32 UTC (rev 116658)
+++ trunk/Source/_javascript_Core/heap/MarkStack.h	2012-05-10 17:32:39 UTC (rev 116659)
@@ -34,12 +34,14 @@
 #include "UnconditionalFinalizer.h"
 #include "VTableSpectrum.h"
 #include "WeakReferenceHarvester.h"
+#include <wtf/Forward.h>
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
 #include <wtf/Vector.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/OSAllocator.h>
 #include <wtf/PageBlock.h>
+#include <wtf/text/StringHash.h>
 
 namespace JSC {
 
@@ -171,13 +173,17 @@
         ~MarkStackThreadSharedData();
         
         void reset();
-    
+
+#if ENABLE(PARALLEL_GC)
+        void resetChildren();
+#endif
+
     private:
         friend class MarkStack;
         friend class SlotVisitor;
 
 #if ENABLE(PARALLEL_GC)
-        void markingThreadMain();
+        void markingThreadMain(SlotVisitor*);
         static void markingThreadStartFunc(void* heap);
 #endif
 
@@ -187,6 +193,7 @@
         MarkStackSegmentAllocator m_segmentAllocator;
         
         Vector<ThreadIdentifier> m_markingThreads;
+        Vector<MarkStack*> m_slaveMarkStacks;
         
         Mutex m_markingLock;
         ThreadCondition m_markingCondition;
@@ -221,13 +228,14 @@
         void addOpaqueRoot(void*);
         bool containsOpaqueRoot(void*);
         int opaqueRootCount();
-        
+
+        MarkStackThreadSharedData& sharedData() { return m_shared; }
         bool isEmpty() { return m_stack.isEmpty(); }
 
         void reset();
 
         size_t visitCount() const { return m_visitCount; }
-
+        
 #if ENABLE(SIMPLE_HEAP_PROFILING)
         VTableSpectrum m_visitedTypeCounts;
 #endif
@@ -251,6 +259,7 @@
 
         void internalAppend(JSCell*);
         void internalAppend(JSValue);
+        void internalAppend(JSValue*);
         
         JS_EXPORT_PRIVATE void mergeOpaqueRoots();
         
@@ -270,6 +279,8 @@
         
         MarkStackArray m_stack;
         HashSet<void*> m_opaqueRoots; // Handle-owning data structures not visible to the garbage collector.
+        typedef HashMap<StringImpl*, JSValue> UniqueStringMap;
+        UniqueStringMap m_uniqueStrings;
         
 #if !ASSERT_DISABLED
     public:

Modified: trunk/Source/_javascript_Core/runtime/JSString.h (116658 => 116659)


--- trunk/Source/_javascript_Core/runtime/JSString.h	2012-05-10 17:26:32 UTC (rev 116658)
+++ trunk/Source/_javascript_Core/runtime/JSString.h	2012-05-10 17:32:39 UTC (rev 116659)
@@ -67,6 +67,7 @@
         friend class JSGlobalData;
         friend class SpecializedThunkJIT;
         friend class JSRopeString;
+        friend class MarkStack;
         friend struct ThunkHelpers;
 
         typedef JSCell Base;
@@ -91,6 +92,7 @@
             Base::finishCreation(globalData);
             m_length = length;
             m_is8Bit = m_value.impl()->is8Bit();
+            m_isHashConstSingleton = false;
         }
 
         void finishCreation(JSGlobalData& globalData, size_t length, size_t cost)
@@ -99,6 +101,7 @@
             Base::finishCreation(globalData);
             m_length = length;
             m_is8Bit = m_value.impl()->is8Bit();
+            m_isHashConstSingleton = false;
             Heap::heap(this)->reportExtraMemoryCost(cost);
         }
 
@@ -108,6 +111,7 @@
             Base::finishCreation(globalData);
             m_length = 0;
             m_is8Bit = true;
+            m_isHashConstSingleton = false;
         }
         
     public:
@@ -161,9 +165,13 @@
     protected:
         bool isRope() const { return m_value.isNull(); }
         bool is8Bit() const { return m_is8Bit; }
+        bool isHashConstSingleton() const { return m_isHashConstSingleton; }
+        void clearHashConstSingleton() { m_isHashConstSingleton = false; }
+        void setHashConstSingleton() { m_isHashConstSingleton = true; }
 
         // A string is represented either by a UString or a rope of fibers.
         bool m_is8Bit : 1;
+        bool m_isHashConstSingleton : 1;
         unsigned m_length;
         mutable UString m_value;
 
@@ -233,6 +241,7 @@
             Base::finishCreation(globalData);
             m_length = s1->length() + s2->length();
             m_is8Bit = (s1->is8Bit() && s2->is8Bit());
+            m_isHashConstSingleton = false;
             m_fibers[0].set(globalData, this, s1);
             m_fibers[1].set(globalData, this, s2);
         }
@@ -242,6 +251,7 @@
             Base::finishCreation(globalData);
             m_length = s1->length() + s2->length() + s3->length();
             m_is8Bit = (s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
+            m_isHashConstSingleton = false;
             m_fibers[0].set(globalData, this, s1);
             m_fibers[1].set(globalData, this, s2);
             m_fibers[2].set(globalData, this, s3);
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to