Ignore:
Timestamp:
Mar 29, 2016, 3:25:44 AM (9 years ago)
Author:
Yusuke Suzuki
Message:

REGRESSION(r192914): 10% regression on Sunspider's date-format-tofte
https://bugs.webkit.org/show_bug.cgi?id=155559

Reviewed by Saam Barati.

Source/JavaScriptCore:

The fast path of the eval function is the super hot path in date-format-tofte.
Any performance regression is not allowed here.
Before this patch, we allocated SourceCode in the fast path.
This allocation incurs 10% performance regression.

This patch removes this allocation in the fast path.
And change the key of the EvalCodeCache to EvalCodeCache::CacheKey.
It combines RefPtr<StringImpl> and isArrowFunctionContext.
Since EvalCodeCache does not cache any eval code evaluated under the strict mode,
it is unnecessary to include several options (ThisTDZMode, and DerivedContextType) in the cache map's key.
But isArrowFunctionContext is necessary since the sloppy mode arrow function exists.

To validate this change, we add a new test that evaluates the same code
under the non-arrow function context and the arrow function context.

After introducing CacheKey, we observed 1% regression compared to the RefPtr<StringImpl> keyed case.
This is because HashMap<RefPtr<T>, ...>::get(T*) is specially optimized; this path is inlined while the normal ::get() is not inlined.
To avoid this performance regression, we introduce HashMap::fastGet, that aggressively encourages inlining.
The relationship between fastGet() and get() is similar to fastAdd() and add().
After applying this change, the evaluation shows no performance regression in comparison with the RefPtr<StringImpl> keyed case.

  • bytecode/EvalCodeCache.h:

(JSC::EvalCodeCache::CacheKey::CacheKey):
(JSC::EvalCodeCache::CacheKey::hash):
(JSC::EvalCodeCache::CacheKey::isEmptyValue):
(JSC::EvalCodeCache::CacheKey::operator==):
(JSC::EvalCodeCache::CacheKey::isHashTableDeletedValue):
(JSC::EvalCodeCache::CacheKey::Hash::hash):
(JSC::EvalCodeCache::CacheKey::Hash::equal):
(JSC::EvalCodeCache::tryGet):
(JSC::EvalCodeCache::getSlow):
(JSC::EvalCodeCache::isCacheable):

  • interpreter/Interpreter.cpp:

(JSC::eval):

  • tests/stress/eval-in-arrow-function.js: Added.

(shouldBe):
(i):

Source/WTF:

Add HashTable::inlineLookup and HashMap::fastGet.

  • wtf/HashMap.h:
  • wtf/HashTable.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecode/EvalCodeCache.h

    r194449 r198778  
    3535#include "Options.h"
    3636#include "SourceCode.h"
    37 #include "SourceCodeKey.h"
    3837#include <wtf/HashMap.h>
    3938#include <wtf/RefPtr.h>
     
    4645    class EvalCodeCache {
    4746    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)
    4993        {
    5094            if (isCacheable(inStrictContext, evalSource, scope)) {
    5195                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();
    5497            }
    5598            return nullptr;
    5699        }
    57100       
    58         EvalExecutable* getSlow(ExecState* exec, JSCell* owner, bool inStrictContext, ThisTDZMode thisTDZMode, DerivedContextType derivedContextType, bool isArrowFunctionContext, const SourceCode& evalSource, JSScope* scope)
     101        EvalExecutable* getSlow(ExecState* exec, JSCell* owner, bool inStrictContext, ThisTDZMode thisTDZMode, DerivedContextType derivedContextType, bool isArrowFunctionContext, const String& evalSource, JSScope* scope)
    59102        {
    60103            VariableEnvironment variablesUnderTDZ;
    61104            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);
    64106            if (!evalExecutable)
    65107                return nullptr;
     
    67109            if (isCacheable(inStrictContext, evalSource, scope) && m_cacheMap.size() < maxCacheEntries) {
    68110                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));
    71114            }
    72115           
     
    89132        }
    90133
    91         ALWAYS_INLINE bool isCacheable(bool inStrictContext, const SourceCode& evalSource, JSScope* scope)
     134        ALWAYS_INLINE bool isCacheable(bool inStrictContext, const String& evalSource, JSScope* scope)
    92135        {
    93136            // If eval() is called and it has access to a lexical scope, we can't soundly cache it.
     
    99142        static const int maxCacheEntries = 64;
    100143
    101         typedef HashMap<SourceCodeKey, WriteBarrier<EvalExecutable>, SourceCodeKeyHash, SourceCodeKeyHashTraits> EvalCacheMap;
     144        typedef HashMap<CacheKey, WriteBarrier<EvalExecutable>, CacheKey::Hash, CacheKey::HashTraits> EvalCacheMap;
    102145        EvalCacheMap m_cacheMap;
    103146    };
Note: See TracChangeset for help on using the changeset viewer.