Changeset 198778 in webkit
- Timestamp:
- Mar 29, 2016, 3:25:44 AM (9 years ago)
- Location:
- trunk/Source
- Files:
-
- 1 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r198776 r198778 1 2016-03-29 Yusuke Suzuki <utatane.tea@gmail.com> 2 3 REGRESSION(r192914): 10% regression on Sunspider's date-format-tofte 4 https://bugs.webkit.org/show_bug.cgi?id=155559 5 6 Reviewed by Saam Barati. 7 8 The fast path of the eval function is the super hot path in date-format-tofte. 9 Any performance regression is not allowed here. 10 Before this patch, we allocated SourceCode in the fast path. 11 This allocation incurs 10% performance regression. 12 13 This patch removes this allocation in the fast path. 14 And change the key of the EvalCodeCache to EvalCodeCache::CacheKey. 15 It combines RefPtr<StringImpl> and isArrowFunctionContext. 16 Since EvalCodeCache does not cache any eval code evaluated under the strict mode, 17 it is unnecessary to include several options (ThisTDZMode, and DerivedContextType) in the cache map's key. 18 But isArrowFunctionContext is necessary since the sloppy mode arrow function exists. 19 20 To validate this change, we add a new test that evaluates the same code 21 under the non-arrow function context and the arrow function context. 22 23 After introducing CacheKey, we observed 1% regression compared to the RefPtr<StringImpl> keyed case. 24 This is because HashMap<RefPtr<T>, ...>::get(T*) is specially optimized; this path is inlined while the normal ::get() is not inlined. 25 To avoid this performance regression, we introduce HashMap::fastGet, that aggressively encourages inlining. 26 The relationship between fastGet() and get() is similar to fastAdd() and add(). 27 After applying this change, the evaluation shows no performance regression in comparison with the RefPtr<StringImpl> keyed case. 28 29 * bytecode/EvalCodeCache.h: 30 (JSC::EvalCodeCache::CacheKey::CacheKey): 31 (JSC::EvalCodeCache::CacheKey::hash): 32 (JSC::EvalCodeCache::CacheKey::isEmptyValue): 33 (JSC::EvalCodeCache::CacheKey::operator==): 34 (JSC::EvalCodeCache::CacheKey::isHashTableDeletedValue): 35 (JSC::EvalCodeCache::CacheKey::Hash::hash): 36 (JSC::EvalCodeCache::CacheKey::Hash::equal): 37 (JSC::EvalCodeCache::tryGet): 38 (JSC::EvalCodeCache::getSlow): 39 (JSC::EvalCodeCache::isCacheable): 40 * interpreter/Interpreter.cpp: 41 (JSC::eval): 42 * tests/stress/eval-in-arrow-function.js: Added. 43 (shouldBe): 44 (i): 45 1 46 2016-03-29 Joseph Pecoraro <pecoraro@apple.com> 2 47 -
trunk/Source/JavaScriptCore/bytecode/EvalCodeCache.h
r194449 r198778 35 35 #include "Options.h" 36 36 #include "SourceCode.h" 37 #include "SourceCodeKey.h"38 37 #include <wtf/HashMap.h> 39 38 #include <wtf/RefPtr.h> … … 46 45 class EvalCodeCache { 47 46 public: 48 EvalExecutable* tryGet(bool inStrictContext, const SourceCode& evalSource, ThisTDZMode thisTDZMode, JSScope* scope) 47 class CacheKey { 48 public: 49 CacheKey(const String& source, bool isArrowFunctionContext) 50 : m_source(source.impl()) 51 , m_isArrowFunctionContext(isArrowFunctionContext) 52 { 53 } 54 55 CacheKey(WTF::HashTableDeletedValueType) 56 : m_source(WTF::HashTableDeletedValue) 57 { 58 } 59 60 CacheKey() = default; 61 62 unsigned hash() const { return m_source->hash(); } 63 64 bool isEmptyValue() const { return !m_source; } 65 66 bool operator==(const CacheKey& other) const 67 { 68 return m_source == other.m_source && m_isArrowFunctionContext == other.m_isArrowFunctionContext; 69 } 70 71 bool isHashTableDeletedValue() const { return m_source.isHashTableDeletedValue(); } 72 73 struct Hash { 74 static unsigned hash(const CacheKey& key) 75 { 76 return key.hash(); 77 } 78 static bool equal(const CacheKey& lhs, const CacheKey& rhs) 79 { 80 return StringHash::equal(lhs.m_source, rhs.m_source) && lhs.m_isArrowFunctionContext == rhs.m_isArrowFunctionContext; 81 } 82 static const bool safeToCompareToEmptyOrDeleted = false; 83 }; 84 85 typedef SimpleClassHashTraits<CacheKey> HashTraits; 86 87 private: 88 RefPtr<StringImpl> m_source; 89 bool m_isArrowFunctionContext { false }; 90 }; 91 92 EvalExecutable* tryGet(bool inStrictContext, const String& evalSource, bool isArrowFunctionContext, JSScope* scope) 49 93 { 50 94 if (isCacheable(inStrictContext, evalSource, scope)) { 51 95 ASSERT(!inStrictContext); 52 SourceCodeKey sourceCodeKey(evalSource, String(), SourceCodeKey::EvalType, JSParserBuiltinMode::NotBuiltin, JSParserStrictMode::NotStrict, thisTDZMode); 53 return m_cacheMap.get(sourceCodeKey).get(); 96 return m_cacheMap.fastGet(CacheKey(evalSource, isArrowFunctionContext)).get(); 54 97 } 55 98 return nullptr; 56 99 } 57 100 58 EvalExecutable* getSlow(ExecState* exec, JSCell* owner, bool inStrictContext, ThisTDZMode thisTDZMode, DerivedContextType derivedContextType, bool isArrowFunctionContext, const S ourceCode& evalSource, JSScope* scope)101 EvalExecutable* getSlow(ExecState* exec, JSCell* owner, bool inStrictContext, ThisTDZMode thisTDZMode, DerivedContextType derivedContextType, bool isArrowFunctionContext, const String& evalSource, JSScope* scope) 59 102 { 60 103 VariableEnvironment variablesUnderTDZ; 61 104 JSScope::collectVariablesUnderTDZ(scope, variablesUnderTDZ); 62 EvalExecutable* evalExecutable = EvalExecutable::create(exec, evalSource, inStrictContext, thisTDZMode, derivedContextType, isArrowFunctionContext, &variablesUnderTDZ); 63 105 EvalExecutable* evalExecutable = EvalExecutable::create(exec, makeSource(evalSource), inStrictContext, thisTDZMode, derivedContextType, isArrowFunctionContext, &variablesUnderTDZ); 64 106 if (!evalExecutable) 65 107 return nullptr; … … 67 109 if (isCacheable(inStrictContext, evalSource, scope) && m_cacheMap.size() < maxCacheEntries) { 68 110 ASSERT(!inStrictContext); 69 SourceCodeKey sourceCodeKey(evalSource, String(), SourceCodeKey::EvalType, JSParserBuiltinMode::NotBuiltin, JSParserStrictMode::NotStrict, thisTDZMode); 70 m_cacheMap.set(sourceCodeKey, WriteBarrier<EvalExecutable>(exec->vm(), owner, evalExecutable)); 111 ASSERT_WITH_MESSAGE(thisTDZMode == ThisTDZMode::CheckIfNeeded, "Always CheckIfNeeded because the caching is enabled only in the sloppy mode."); 112 ASSERT_WITH_MESSAGE(derivedContextType == DerivedContextType::None, "derivedContextType is always None because class methods and class constructors are always evaluated as the strict code."); 113 m_cacheMap.set(CacheKey(evalSource, isArrowFunctionContext), WriteBarrier<EvalExecutable>(exec->vm(), owner, evalExecutable)); 71 114 } 72 115 … … 89 132 } 90 133 91 ALWAYS_INLINE bool isCacheable(bool inStrictContext, const S ourceCode& evalSource, JSScope* scope)134 ALWAYS_INLINE bool isCacheable(bool inStrictContext, const String& evalSource, JSScope* scope) 92 135 { 93 136 // If eval() is called and it has access to a lexical scope, we can't soundly cache it. … … 99 142 static const int maxCacheEntries = 64; 100 143 101 typedef HashMap< SourceCodeKey, WriteBarrier<EvalExecutable>, SourceCodeKeyHash, SourceCodeKeyHashTraits> EvalCacheMap;144 typedef HashMap<CacheKey, WriteBarrier<EvalExecutable>, CacheKey::Hash, CacheKey::HashTraits> EvalCacheMap; 102 145 EvalCacheMap m_cacheMap; 103 146 }; -
trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp
r198364 r198778 158 158 UnlinkedCodeBlock* callerUnlinkedCodeBlock = callerCodeBlock->unlinkedCodeBlock(); 159 159 160 ThisTDZMode thisTDZMode = ThisTDZMode::CheckIfNeeded; 161 if (callerUnlinkedCodeBlock->constructorKind() == ConstructorKind::Derived) 162 thisTDZMode = ThisTDZMode::AlwaysCheck; 163 164 SourceCode sourceCode(makeSource(programSource)); 165 EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), sourceCode, thisTDZMode, callerScopeChain); 160 bool isArrowFunctionContext = callerUnlinkedCodeBlock->isArrowFunction() || callerUnlinkedCodeBlock->isArrowFunctionContext(); 161 EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, isArrowFunctionContext, callerScopeChain); 166 162 167 163 if (!eval) { … … 180 176 // If the literal parser bailed, it should not have thrown exceptions. 181 177 ASSERT(!callFrame->vm().exception()); 182 bool isInArrowFunctionContext = callerCodeBlock->unlinkedCodeBlock()->isArrowFunction() || callerCodeBlock->unlinkedCodeBlock()->isArrowFunctionContext(); 183 184 DerivedContextType derivedContextType = callerCodeBlock->unlinkedCodeBlock()->derivedContextType(); 178 179 ThisTDZMode thisTDZMode = ThisTDZMode::CheckIfNeeded; 180 if (callerUnlinkedCodeBlock->constructorKind() == ConstructorKind::Derived) 181 thisTDZMode = ThisTDZMode::AlwaysCheck; 182 183 DerivedContextType derivedContextType = callerUnlinkedCodeBlock->derivedContextType(); 185 184 186 if (!is InArrowFunctionContext && callerCodeBlock->unlinkedCodeBlock()->isClassContext()) {187 derivedContextType = caller CodeBlock->unlinkedCodeBlock()->isConstructor()185 if (!isArrowFunctionContext && callerUnlinkedCodeBlock->isClassContext()) { 186 derivedContextType = callerUnlinkedCodeBlock->isConstructor() 188 187 ? DerivedContextType::DerivedConstructorContext 189 188 : DerivedContextType::DerivedMethodContext; 190 189 } 191 190 192 eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock, callerCodeBlock->isStrictMode(), thisTDZMode, derivedContextType, isInArrowFunctionContext, sourceCode, callerScopeChain); 193 191 eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock, callerCodeBlock->isStrictMode(), thisTDZMode, derivedContextType, isArrowFunctionContext, programSource, callerScopeChain); 194 192 if (!eval) 195 193 return jsUndefined(); -
trunk/Source/WTF/ChangeLog
r198673 r198778 1 2016-03-29 Yusuke Suzuki <utatane.tea@gmail.com> 2 3 REGRESSION(r192914): 10% regression on Sunspider's date-format-tofte 4 https://bugs.webkit.org/show_bug.cgi?id=155559 5 6 Reviewed by Saam Barati. 7 8 Add HashTable::inlineLookup and HashMap::fastGet. 9 10 * wtf/HashMap.h: 11 * wtf/HashTable.h: 12 1 13 2016-03-25 Alex Christensen <achristensen@webkit.org> 2 14 -
trunk/Source/WTF/wtf/HashMap.h
r197123 r198778 103 103 MappedPeekType get(const KeyType&) const; 104 104 105 // Same as get(), but aggressively inlined. 106 MappedPeekType fastGet(const KeyType&) const; 107 105 108 // Replaces the value but not the key if the key is already present. 106 109 // Return value includes both an iterator to the key location, … … 393 396 } 394 397 398 template<typename T, typename U, typename V, typename W, typename MappedTraits> 399 ALWAYS_INLINE auto HashMap<T, U, V, W, MappedTraits>::fastGet(const KeyType& key) const -> MappedPeekType 400 { 401 KeyValuePairType* entry = const_cast<HashTableType&>(m_impl).template inlineLookup<typename HashTableType::IdentityTranslatorType>(key); 402 if (!entry) 403 return MappedTraits::peek(MappedTraits::emptyValue()); 404 return MappedTraits::peek(entry->value); 405 } 406 395 407 template<typename T, typename U, typename V, typename W, typename X> 396 408 inline bool HashMap<T, U, V, W, X>::remove(iterator it) … … 458 470 inline auto HashMap<T, U, V, W, X>::inlineGet(typename GetPtrHelper<K>::PtrType key) const -> typename std::enable_if<IsSmartPtr<K>::value, MappedPeekType>::type 459 471 { 460 KeyValuePairType* entry = const_cast<HashTableType&>(m_impl).template lookup<HashMapTranslator<KeyValuePairTraits, HashFunctions>>(key);472 KeyValuePairType* entry = const_cast<HashTableType&>(m_impl).template inlineLookup<HashMapTranslator<KeyValuePairTraits, HashFunctions>>(key); 461 473 if (!entry) 462 474 return MappedTraits::peek(MappedTraits::emptyValue()); -
trunk/Source/WTF/wtf/HashTable.h
r195629 r198778 413 413 ValueType* lookup(const Key& key) { return lookup<IdentityTranslatorType>(key); } 414 414 template<typename HashTranslator, typename T> ValueType* lookup(const T&); 415 template<typename HashTranslator, typename T> ValueType* inlineLookup(const T&); 415 416 416 417 #if !ASSERT_DISABLED … … 595 596 template<typename HashTranslator, typename T> 596 597 inline auto HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::lookup(const T& key) -> ValueType* 598 { 599 return inlineLookup<HashTranslator>(key); 600 } 601 602 template<typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits> 603 template<typename HashTranslator, typename T> 604 ALWAYS_INLINE auto HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits>::inlineLookup(const T& key) -> ValueType* 597 605 { 598 606 checkKey<HashTranslator>(key);
Note:
See TracChangeset
for help on using the changeset viewer.