Changeset 121806 in webkit
- Timestamp:
- Jul 3, 2012 3:57:00 PM (12 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r121801 r121806 1 2012-07-03 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 Oliver Hunt. 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 reduce the performance impact of this change, two throttles have 15 ben added. 1) We only try hash consting if a significant number of new 16 strings have been created since the last hash const. Currently this is 17 set at 100 strings. 2) If a string is unique at the end of a marking 18 it will not be checked during further GC phases. In some cases this 19 won't catch all duplicates, but we are trying to catch the growth of 20 duplicate strings. 21 22 * heap/Heap.cpp: 23 (JSC::Heap::markRoots): 24 * heap/MarkStack.cpp: 25 (JSC::MarkStackThreadSharedData::resetChildren): 26 (JSC::MarkStackThreadSharedData::MarkStackThreadSharedData): 27 (JSC::MarkStackThreadSharedData::reset): 28 (JSC::MarkStack::setup): Check to see if enough strings have been created 29 to hash const. 30 (JSC::MarkStack::reset): Added call to clear m_uniqueStrings. 31 (JSC::JSString::tryHashConstLock): New method to lock JSString for 32 hash consting. 33 (JSC::JSString::releaseHashConstLock): New unlock method. 34 (JSC::JSString::shouldTryHashConst): Set of checks to see if we should 35 try to hash const the string. 36 (JSC::MarkStack::internalAppend): New method that performs the hash consting. 37 (JSC::SlotVisitor::copyAndAppend): Changed to call the new hash 38 consting internalAppend(). 39 * heap/MarkStack.h: 40 (MarkStackThreadSharedData): 41 (MarkStack): 42 * runtime/JSGlobalData.cpp: 43 (JSC::JSGlobalData::JSGlobalData): 44 * runtime/JSGlobalData.h: 45 (JSGlobalData): 46 (JSC::JSGlobalData::haveEnoughNewStringsToHashConst): 47 (JSC::JSGlobalData::resetNewStringsSinceLastHashConst): 48 * runtime/JSString.h: 49 (JSString): Changed from using bool flags to using an unsigned 50 m_flags field. This works better with the weakCompareAndSwap in 51 JSString::tryHashConstLock(). Changed the 8bitness setting and 52 checking to use new accessors. 53 (JSC::JSString::JSString): 54 (JSC::JSString::finishCreation): 55 (JSC::JSString::is8Bit): Updated for new m_flags. 56 (JSC::JSString::setIs8Bit): New setter. 57 New hash const flags accessors: 58 (JSC::JSString::isHashConstSingleton): 59 (JSC::JSString::clearHashConstSingleton): 60 (JSC::JSString::setHashConstSingleton): 61 (JSC::JSRopeString::finishCreation): 62 (JSC::JSRopeString::append): 63 1 64 2012-07-03 Tony Chang <tony@chromium.org> 2 65 -
trunk/Source/JavaScriptCore/heap/Heap.cpp
r121607 r121806 455 455 m_storageSpace.startedCopying(); 456 456 SlotVisitor& visitor = m_slotVisitor; 457 visitor.setup(); 457 458 HeapRootVisitor heapRootVisitor(visitor); 458 459 … … 586 587 587 588 visitor.reset(); 588 m_sharedData.reset();589 589 #if ENABLE(PARALLEL_GC) 590 590 m_sharedData.resetChildren(); 591 591 #endif 592 m_sharedData.reset(); 592 593 m_storageSpace.doneCopying(); 593 594 594 } 595 595 -
trunk/Source/JavaScriptCore/heap/MarkStack.cpp
r121798 r121806 39 39 #include "UString.h" 40 40 #include "WriteBarrier.h" 41 #include <wtf/Atomics.h> 41 42 #include <wtf/DataLog.h> 42 43 #include <wtf/MainThread.h> … … 226 227 { 227 228 for (unsigned i = 0; i < m_markingThreadsMarkStack.size(); ++i) 228 m_markingThreadsMarkStack[i]->reset();229 } 229 m_markingThreadsMarkStack[i]->reset(); 230 } 230 231 231 232 size_t MarkStackThreadSharedData::childVisitCount() … … 258 259 : m_globalData(globalData) 259 260 , m_copiedSpace(&globalData->heap.m_storageSpace) 261 , m_shouldHashConst(false) 260 262 , m_sharedMarkStack(m_segmentAllocator) 261 263 , m_numberOfActiveParallelMarkers(0) … … 299 301 #endif 300 302 m_weakReferenceHarvesters.removeAll(); 303 304 if (m_shouldHashConst) { 305 m_globalData->resetNewStringsSinceLastHashConst(); 306 m_shouldHashConst = false; 307 } 308 } 309 310 void MarkStack::setup() 311 { 312 m_shared.m_shouldHashConst = m_shared.m_globalData->haveEnoughNewStringsToHashConst(); 313 m_shouldHashConst = m_shared.m_shouldHashConst; 314 #if ENABLE(PARALLEL_GC) 315 for (unsigned i = 0; i < m_shared.m_markingThreadsMarkStack.size(); ++i) 316 m_shared.m_markingThreadsMarkStack[i]->m_shouldHashConst = m_shared.m_shouldHashConst; 317 #endif 301 318 } 302 319 … … 310 327 m_opaqueRoots.clear(); 311 328 #endif 329 if (m_shouldHashConst) { 330 m_uniqueStrings.clear(); 331 m_shouldHashConst = false; 332 } 312 333 } 313 334 … … 522 543 } 523 544 545 ALWAYS_INLINE bool JSString::tryHashConstLock() 546 { 547 #if ENABLE(PARALLEL_GC) 548 unsigned currentFlags = m_flags; 549 unsigned newFlags = currentFlags | HashConstLock; 550 551 if (!WTF::weakCompareAndSwap(&m_flags, currentFlags, newFlags)) 552 return false; 553 554 WTF::memoryBarrierAfterLock(); 555 return true; 556 #else 557 if (isHashConstSingleton()) 558 return false; 559 560 m_flags |= HashConstLock; 561 562 return true; 563 #endif 564 } 565 566 ALWAYS_INLINE void JSString::releaseHashConstLock() 567 { 568 #if ENABLE(PARALLEL_GC) 569 WTF::memoryBarrierBeforeUnlock(); 570 #endif 571 m_flags &= ~HashConstLock; 572 } 573 574 ALWAYS_INLINE bool JSString::shouldTryHashConst() 575 { 576 return ((length() > 1) && !isRope() && !isHashConstSingleton()); 577 } 578 579 ALWAYS_INLINE void MarkStack::internalAppend(JSValue* slot) 580 { 581 // This internalAppend is only intended for visits to object and array backing stores. 582 // as it can change the JSValue pointed to be the argument when the original JSValue 583 // is a string that contains the same contents as another string. 584 585 ASSERT(slot); 586 JSValue value = *slot; 587 ASSERT(value); 588 if (!value.isCell()) 589 return; 590 591 JSCell* cell = value.asCell(); 592 593 if (m_shouldHashConst && cell->isString()) { 594 JSString* string = jsCast<JSString*>(cell); 595 if (string->shouldTryHashConst() && string->tryHashConstLock()) { 596 UniqueStringMap::AddResult addResult = m_uniqueStrings.add(string->string().impl(), value); 597 if (addResult.isNewEntry) 598 string->setHashConstSingleton(); 599 else { 600 JSValue existingJSValue = addResult.iterator->second; 601 if (value != existingJSValue) 602 jsCast<JSString*>(existingJSValue.asCell())->clearHashConstSingleton(); 603 *slot = existingJSValue; 604 string->releaseHashConstLock(); 605 return; 606 } 607 string->releaseHashConstLock(); 608 } 609 } 610 611 internalAppend(cell); 612 } 613 524 614 void SlotVisitor::copyAndAppend(void** ptr, size_t bytes, JSValue* values, unsigned length) 525 615 { … … 535 625 if (!value) 536 626 continue; 537 internalAppend( value);627 internalAppend(&newValues[i]); 538 628 } 539 629 -
trunk/Source/JavaScriptCore/heap/MarkStack.h
r121798 r121806 220 220 MarkStackSegmentAllocator m_segmentAllocator; 221 221 222 bool m_shouldHashConst; 223 222 224 Vector<ThreadIdentifier> m_markingThreads; 223 225 Vector<MarkStack*> m_markingThreadsMarkStack; … … 260 262 bool isEmpty() { return m_stack.isEmpty(); } 261 263 264 void setup(); 262 265 void reset(); 263 266 … … 293 296 void internalAppend(JSCell*); 294 297 void internalAppend(JSValue); 298 void internalAppend(JSValue*); 295 299 296 300 JS_EXPORT_PRIVATE void mergeOpaqueRoots(); … … 325 329 326 330 MarkStackThreadSharedData& m_shared; 331 332 bool m_shouldHashConst; // Local per-thread copy of shared flag for performance reasons 333 typedef HashMap<StringImpl*, JSValue> UniqueStringMap; 334 UniqueStringMap m_uniqueStrings; 327 335 328 336 #if ENABLE(OBJECT_MARK_LOGGING) … … 340 348 , m_isInParallelMode(false) 341 349 , m_shared(shared) 350 , m_shouldHashConst(false) 342 351 { 343 352 } -
trunk/Source/JavaScriptCore/runtime/JSGlobalData.cpp
r121798 r121806 171 171 , m_timeoutCount(512) 172 172 #endif 173 , m_newStringsSinceLastHashConst(0) 173 174 #if ENABLE(ASSEMBLER) && (ENABLE(CLASSIC_INTERPRETER) || ENABLE(LLINT)) 174 175 , m_canUseAssembler(enableAssembler(executableAllocator)) -
trunk/Source/JavaScriptCore/runtime/JSGlobalData.h
r121381 r121806 396 396 #endif 397 397 398 unsigned m_newStringsSinceLastHashConst; 399 400 static const unsigned s_minNumberOfNewStringsToHashConst = 100; 401 402 bool haveEnoughNewStringsToHashConst() { return m_newStringsSinceLastHashConst > s_minNumberOfNewStringsToHashConst; } 403 void resetNewStringsSinceLastHashConst() { m_newStringsSinceLastHashConst = 0; } 404 398 405 #define registerTypedArrayFunction(type, capitalizedType) \ 399 406 void registerTypedArrayDescriptor(const capitalizedType##Array*, const TypedArrayDescriptor& descriptor) \ -
trunk/Source/JavaScriptCore/runtime/JSString.h
r119633 r121806 68 68 friend class SpecializedThunkJIT; 69 69 friend class JSRopeString; 70 friend class MarkStack; 70 71 friend class SlotVisitor; 71 72 friend struct ThunkHelpers; … … 78 79 JSString(JSGlobalData& globalData, PassRefPtr<StringImpl> value) 79 80 : JSCell(globalData, globalData.stringStructure.get()) 81 , m_flags(0) 80 82 , m_value(value) 81 83 { … … 84 86 JSString(JSGlobalData& globalData) 85 87 : JSCell(globalData, globalData.stringStructure.get()) 88 , m_flags(0) 86 89 { 87 90 } … … 92 95 Base::finishCreation(globalData); 93 96 m_length = length; 94 m_is8Bit = m_value.impl()->is8Bit(); 97 setIs8Bit(m_value.impl()->is8Bit()); 98 globalData.m_newStringsSinceLastHashConst++; 95 99 } 96 100 … … 100 104 Base::finishCreation(globalData); 101 105 m_length = length; 102 m_is8Bit = m_value.impl()->is8Bit();106 setIs8Bit(m_value.impl()->is8Bit()); 103 107 Heap::heap(this)->reportExtraMemoryCost(cost); 108 globalData.m_newStringsSinceLastHashConst++; 104 109 } 105 110 … … 109 114 Base::finishCreation(globalData); 110 115 m_length = 0; 111 m_is8Bit = true; 116 setIs8Bit(true); 117 globalData.m_newStringsSinceLastHashConst++; 112 118 } 113 119 … … 162 168 protected: 163 169 bool isRope() const { return m_value.isNull(); } 164 bool is8Bit() const { return m_is8Bit; } 170 bool is8Bit() const { return m_flags & Is8Bit; } 171 void setIs8Bit(bool flag) 172 { 173 if (flag) 174 m_flags |= Is8Bit; 175 else 176 m_flags &= ~Is8Bit; 177 } 178 bool shouldTryHashConst(); 179 bool isHashConstSingleton() const { return m_flags & IsHashConstSingleton; } 180 void clearHashConstSingleton() { m_flags &= ~IsHashConstSingleton; } 181 void setHashConstSingleton() { m_flags |= IsHashConstSingleton; } 182 bool tryHashConstLock(); 183 void releaseHashConstLock(); 184 185 unsigned m_flags; 186 187 enum { 188 HashConstLock = 1u << 2, 189 IsHashConstSingleton = 1u << 1, 190 Is8Bit = 1u 191 }; 165 192 166 193 // A string is represented either by a UString or a rope of fibers. 167 bool m_is8Bit : 1;168 194 unsigned m_length; 169 195 mutable UString m_value; … … 232 258 Base::finishCreation(globalData); 233 259 m_length = s1->length() + s2->length(); 234 m_is8Bit =(s1->is8Bit() && s2->is8Bit());260 setIs8Bit(s1->is8Bit() && s2->is8Bit()); 235 261 m_fibers[0].set(globalData, this, s1); 236 262 m_fibers[1].set(globalData, this, s2); … … 241 267 Base::finishCreation(globalData); 242 268 m_length = s1->length() + s2->length() + s3->length(); 243 m_is8Bit =(s1->is8Bit() && s2->is8Bit() && s3->is8Bit());269 setIs8Bit(s1->is8Bit() && s2->is8Bit() && s3->is8Bit()); 244 270 m_fibers[0].set(globalData, this, s1); 245 271 m_fibers[1].set(globalData, this, s2); … … 256 282 m_fibers[index].set(globalData, this, jsString); 257 283 m_length += jsString->m_length; 258 m_is8Bit = m_is8Bit && jsString->m_is8Bit;284 setIs8Bit(is8Bit() && jsString->is8Bit()); 259 285 } 260 286
Note: See TracChangeset
for help on using the changeset viewer.