Changeset 143133 in webkit


Ignore:
Timestamp:
Feb 17, 2013 11:15:30 AM (11 years ago)
Author:
ggaren@apple.com
Message:

Code cache should be explicit about what it caches
https://bugs.webkit.org/show_bug.cgi?id=110039

Reviewed by Oliver Hunt.

This patch makes the code cache more explicit in two ways:

(1) The cache caches top-level scripts. Any sub-functions executed as a
part of a script are cached with it and evicted with it.

This simplifies things by eliminating out-of-band sub-function tracking,
and fixes pathological cases where functions for live scripts would be
evicted in favor of functions for dead scripts, and/or high probability
functions executed early in script lifetime would be evicted in favor of
low probability functions executed late in script lifetime, due to LRU.

Statistical data from general browsing and PLT confirms that caching
functions independently of scripts is not profitable.

(2) The cache tracks script size, not script count.

This reduces the worst-case cache size by a factor of infinity.

Script size is a reasonable first-order estimate of in-memory footprint
for a cached script because there are no syntactic constructs that have
super-linear memory footprint.

  • bytecode/UnlinkedCodeBlock.cpp:

(JSC::generateFunctionCodeBlock): Moved this function out of the cache
because it does not consult the cache, and is not managed by it.

(JSC::UnlinkedFunctionExecutable::visitChildren): Visit our code blocks
because they are strong references now, rather than weak, a la (1).

(JSC::UnlinkedFunctionExecutable::codeBlockFor): Updated for interface changes.

  • bytecode/UnlinkedCodeBlock.h:

(UnlinkedFunctionExecutable):
(UnlinkedFunctionCodeBlock): Strong now, not weak, a la (1).

  • runtime/CodeCache.cpp:

(JSC::CodeCache::CodeCache):

  • runtime/CodeCache.h:

(JSC::SourceCodeKey::length):
(SourceCodeKey):
(CodeCacheMap):
(JSC::CodeCacheMap::CodeCacheMap):
(JSC::CodeCacheMap::find):
(JSC::CodeCacheMap::set):
(JSC::CodeCacheMap::clear):
(CodeCache):
(JSC::CodeCache::clear): Removed individual function tracking, due to (1).
Added explicit character counting, for (2).

You might think 16000000 characters is a lot. It is. But this patch
didn't establish that limit -- it just took the existing limit and
made it more visible. I intend to reduce the size of the cache in a
future patch.

Location:
trunk/Source/JavaScriptCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r143122 r143133  
     12013-02-16  Geoffrey Garen  <ggaren@apple.com>
     2
     3        Code cache should be explicit about what it caches
     4        https://bugs.webkit.org/show_bug.cgi?id=110039
     5
     6        Reviewed by Oliver Hunt.
     7
     8        This patch makes the code cache more explicit in two ways:
     9
     10        (1) The cache caches top-level scripts. Any sub-functions executed as a
     11        part of a script are cached with it and evicted with it.
     12
     13        This simplifies things by eliminating out-of-band sub-function tracking,
     14        and fixes pathological cases where functions for live scripts would be
     15        evicted in favor of functions for dead scripts, and/or high probability
     16        functions executed early in script lifetime would be evicted in favor of
     17        low probability functions executed late in script lifetime, due to LRU.
     18
     19        Statistical data from general browsing and PLT confirms that caching
     20        functions independently of scripts is not profitable.
     21
     22        (2) The cache tracks script size, not script count.
     23
     24        This reduces the worst-case cache size by a factor of infinity.
     25
     26        Script size is a reasonable first-order estimate of in-memory footprint
     27        for a cached script because there are no syntactic constructs that have
     28        super-linear memory footprint.
     29
     30        * bytecode/UnlinkedCodeBlock.cpp:
     31        (JSC::generateFunctionCodeBlock): Moved this function out of the cache
     32        because it does not consult the cache, and is not managed by it.
     33
     34        (JSC::UnlinkedFunctionExecutable::visitChildren): Visit our code blocks
     35        because they are strong references now, rather than weak, a la (1).
     36
     37        (JSC::UnlinkedFunctionExecutable::codeBlockFor): Updated for interface changes.
     38
     39        * bytecode/UnlinkedCodeBlock.h:
     40        (UnlinkedFunctionExecutable):
     41        (UnlinkedFunctionCodeBlock): Strong now, not weak, a la (1).
     42
     43        * runtime/CodeCache.cpp:
     44        (JSC::CodeCache::CodeCache):
     45        * runtime/CodeCache.h:
     46        (JSC::SourceCodeKey::length):
     47        (SourceCodeKey):
     48        (CodeCacheMap):
     49        (JSC::CodeCacheMap::CodeCacheMap):
     50        (JSC::CodeCacheMap::find):
     51        (JSC::CodeCacheMap::set):
     52        (JSC::CodeCacheMap::clear):
     53        (CodeCache):
     54        (JSC::CodeCache::clear): Removed individual function tracking, due to (1).
     55        Added explicit character counting, for (2).
     56
     57        You might think 16000000 characters is a lot. It is. But this patch
     58        didn't establish that limit -- it just took the existing limit and
     59        made it more visible. I intend to reduce the size of the cache in a
     60        future patch.
     61
    1622013-02-16  Filip Pizlo  <fpizlo@apple.com>
    263
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp

    r142649 r143133  
    4747const ClassInfo UnlinkedFunctionCodeBlock::s_info = { "UnlinkedFunctionCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedFunctionCodeBlock) };
    4848
     49static UnlinkedFunctionCodeBlock* generateFunctionCodeBlock(JSGlobalData& globalData, UnlinkedFunctionExecutable* executable, const SourceCode& source, CodeSpecializationKind kind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
     50{
     51    RefPtr<FunctionBodyNode> body = parse<FunctionBodyNode>(&globalData, source, executable->parameters(), executable->name(), executable->isInStrictContext() ? JSParseStrict : JSParseNormal, JSParseFunctionCode, error);
     52
     53    if (!body) {
     54        ASSERT(error.m_type != ParserError::ErrorNone);
     55        return 0;
     56    }
     57
     58    if (executable->forceUsesArguments())
     59        body->setUsesArguments();
     60    body->finishParsing(executable->parameters(), executable->name(), executable->functionNameIsInScopeToggle());
     61    executable->recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine());
     62   
     63    UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&globalData, FunctionCode, ExecutableInfo(body->needsActivation(), body->usesEval(), body->isStrictMode(), kind == CodeForConstruct));
     64    OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(globalData, body.get(), result, debuggerMode, profilerMode)));
     65    error = generator->generate();
     66    body->destroyData();
     67    if (error.m_type != ParserError::ErrorNone)
     68        return 0;
     69    return result;
     70}
     71
    4972unsigned UnlinkedCodeBlock::addOrFindConstant(JSValue v)
    5073{
     
    83106    ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
    84107    Base::visitChildren(thisObject, visitor);
     108    visitor.append(&thisObject->m_codeBlockForCall);
     109    visitor.append(&thisObject->m_codeBlockForConstruct);
    85110    visitor.append(&thisObject->m_nameValue);
    86111    visitor.append(&thisObject->m_symbolTableForCall);
     
    113138    switch (specializationKind) {
    114139    case CodeForCall:
    115         if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get()) {
    116             globalData.codeCache()->usedFunctionCode(globalData, codeBlock);
     140        if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get())
    117141            return codeBlock;
    118         }
    119142        break;
    120143    case CodeForConstruct:
    121         if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get()) {
    122             globalData.codeCache()->usedFunctionCode(globalData, codeBlock);
     144        if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get())
    123145            return codeBlock;
    124         }
    125146        break;
    126147    }
    127148
    128     UnlinkedFunctionCodeBlock* result = globalData.codeCache()->getFunctionCodeBlock(globalData, this, source, specializationKind, debuggerMode, profilerMode, error);
     149    UnlinkedFunctionCodeBlock* result = generateFunctionCodeBlock(globalData, this, source, specializationKind, debuggerMode, profilerMode, error);
    129150   
    130151    if (error.m_type != ParserError::ErrorNone)
     
    133154    switch (specializationKind) {
    134155    case CodeForCall:
    135         m_codeBlockForCall = PassWeak<UnlinkedFunctionCodeBlock>(result);
     156        m_codeBlockForCall.set(globalData, this, result);
    136157        m_symbolTableForCall.set(globalData, this, result->symbolTable());
    137158        break;
    138159    case CodeForConstruct:
    139         m_codeBlockForConstruct = PassWeak<UnlinkedFunctionCodeBlock>(result);
     160        m_codeBlockForConstruct.set(globalData, this, result);
    140161        m_symbolTableForConstruct.set(globalData, this, result->symbolTable());
    141162        break;
  • trunk/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h

    r142649 r143133  
    140140private:
    141141    UnlinkedFunctionExecutable(JSGlobalData*, Structure*, const SourceCode&, FunctionBodyNode*);
    142     Weak<UnlinkedFunctionCodeBlock> m_codeBlockForCall;
    143     Weak<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct;
     142    WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForCall;
     143    WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct;
    144144
    145145    unsigned m_numCapturedVariables : 29;
     
    676676
    677677class UnlinkedFunctionCodeBlock : public UnlinkedCodeBlock {
    678 private:
    679     friend class CodeCache;
    680 
     678public:
    681679    static UnlinkedFunctionCodeBlock* create(JSGlobalData* globalData, CodeType codeType, const ExecutableInfo& info)
    682680    {
     
    686684    }
    687685
    688 public:
    689686    typedef UnlinkedCodeBlock Base;
    690687    static void destroy(JSCell*);
  • trunk/Source/JavaScriptCore/runtime/CodeCache.cpp

    r142966 r143133  
    3838
    3939CodeCache::CodeCache()
     40    : m_sourceCode(CacheSize)
    4041{
    4142}
     
    103104}
    104105
    105 UnlinkedFunctionCodeBlock* CodeCache::generateFunctionCodeBlock(JSGlobalData& globalData, UnlinkedFunctionExecutable* executable, const SourceCode& source, CodeSpecializationKind kind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
    106 {
    107     RefPtr<FunctionBodyNode> body = parse<FunctionBodyNode>(&globalData, source, executable->parameters(), executable->name(), executable->isInStrictContext() ? JSParseStrict : JSParseNormal, JSParseFunctionCode, error);
    108 
    109     if (!body) {
    110         ASSERT(error.m_type != ParserError::ErrorNone);
    111         return 0;
    112     }
    113 
    114     if (executable->forceUsesArguments())
    115         body->setUsesArguments();
    116     body->finishParsing(executable->parameters(), executable->name(), executable->functionNameIsInScopeToggle());
    117     executable->recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine());
    118    
    119     UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&globalData, FunctionCode, ExecutableInfo(body->needsActivation(), body->usesEval(), body->isStrictMode(), kind == CodeForConstruct));
    120     OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(globalData, body.get(), result, debuggerMode, profilerMode)));
    121     error = generator->generate();
    122     body->destroyData();
    123     if (error.m_type != ParserError::ErrorNone)
    124         return 0;
    125     m_recentlyUsedFunctions.set(result, Strong<UnlinkedFunctionCodeBlock>(globalData, result));
    126     return result;
    127 }
    128 
    129 UnlinkedFunctionCodeBlock* CodeCache::getFunctionCodeBlock(JSGlobalData& globalData, UnlinkedFunctionExecutable* executable, const SourceCode& source, CodeSpecializationKind kind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
    130 {
    131     return generateFunctionCodeBlock(globalData, executable, source, kind, debuggerMode, profilerMode, error);
    132 }
    133 
    134106UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, ParserError& error)
    135107{
     
    163135}
    164136
    165 void CodeCache::usedFunctionCode(JSGlobalData& globalData, UnlinkedFunctionCodeBlock* codeBlock)
    166 {
    167     m_recentlyUsedFunctions.set(codeBlock, Strong<UnlinkedFunctionCodeBlock>(globalData, codeBlock));
    168137}
    169 
    170 }
  • trunk/Source/JavaScriptCore/runtime/CodeCache.h

    r143027 r143133  
    11/*
    2  * Copyright (C) 2012 Apple Inc. All Rights Reserved.
     2 * Copyright (C) 2012, 2013 Apple Inc. All Rights Reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    5454class SourceProvider;
    5555
    56 template <int CacheSize, typename KeyType, typename EntryType, typename HashArg = typename DefaultHash<KeyType>::Hash, typename KeyTraitsArg = HashTraits<KeyType> >
    57 class CacheMap {
    58     typedef HashMap<KeyType, EntryType, HashArg, KeyTraitsArg> MapType;
    59     typedef typename MapType::iterator iterator;
    60 
    61 public:
    62     const EntryType* find(const KeyType& key)
    63     {
    64         iterator result = m_map.find(key);
    65         if (result == m_map.end())
    66             return 0;
    67         return &result->value;
    68     }
    69 
    70     void set(const KeyType& key, const EntryType& value)
    71     {
    72         if (m_map.size() >= CacheSize)
    73             m_map.remove(m_map.begin());
    74 
    75         m_map.set(key, value);
    76     }
    77 
    78     void clear() { m_map.clear(); }
    79 
    80 private:
    81     MapType m_map;
    82 };
    83 
    8456class SourceCodeKey {
    8557public:
     
    10678
    10779    unsigned hash() const { return m_sourceString.impl()->hash(); }
     80
     81    size_t length() const { return m_sourceString.length(); }
    10882
    10983    bool isNull() const { return m_sourceString.isNull(); }
     
    133107};
    134108
     109class CodeCacheMap {
     110    typedef HashMap<SourceCodeKey, Strong<JSCell>, SourceCodeKeyHash, SourceCodeKeyHashTraits> MapType;
     111    typedef typename MapType::iterator iterator;
     112
     113public:
     114    CodeCacheMap(size_t capacity)
     115        : m_size(0)
     116        , m_capacity(capacity)
     117    {
     118    }
     119
     120    const Strong<JSCell>* find(const SourceCodeKey& key)
     121    {
     122        iterator result = m_map.find(key);
     123        if (result == m_map.end())
     124            return 0;
     125        return &result->value;
     126    }
     127
     128    void set(const SourceCodeKey& key, const Strong<JSCell>& value)
     129    {
     130        while (m_size >= m_capacity) {
     131            MapType::iterator it = m_map.begin();
     132            m_size -= it->key.length();
     133            m_map.remove(it);
     134        }
     135
     136        m_size += key.length();
     137        m_map.set(key, value);
     138    }
     139
     140    void clear()
     141    {
     142        m_size = 0;
     143        m_map.clear();
     144    }
     145
     146private:
     147    MapType m_map;
     148    size_t m_size;
     149    size_t m_capacity;
     150};
     151
     152// Caches top-level code such as <script>, eval(), new Function, and JSEvaluateScript().
    135153class CodeCache {
    136154public:
     
    139157    UnlinkedProgramCodeBlock* getProgramCodeBlock(JSGlobalData&, ProgramExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
    140158    UnlinkedEvalCodeBlock* getEvalCodeBlock(JSGlobalData&, EvalExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
    141     UnlinkedFunctionCodeBlock* getFunctionCodeBlock(JSGlobalData&, UnlinkedFunctionExecutable*, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, ParserError&);
    142159    UnlinkedFunctionExecutable* getFunctionExecutableFromGlobalCode(JSGlobalData&, const Identifier&, const SourceCode&, ParserError&);
    143     void usedFunctionCode(JSGlobalData&, UnlinkedFunctionCodeBlock*);
    144160    ~CodeCache();
    145161
     
    147163    {
    148164        m_sourceCode.clear();
    149         m_recentlyUsedFunctions.clear();
    150165    }
    151166
     
    153168    CodeCache();
    154169
    155     UnlinkedFunctionCodeBlock* generateFunctionCodeBlock(JSGlobalData&, UnlinkedFunctionExecutable*, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, ParserError&);
     170    template <class UnlinkedCodeBlockType, class ExecutableType>
     171    UnlinkedCodeBlockType* getCodeBlock(JSGlobalData&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
    156172
    157     template <class UnlinkedCodeBlockType, class ExecutableType> inline UnlinkedCodeBlockType* getCodeBlock(JSGlobalData&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
     173    enum { CacheSize = 16000000 }; // Size in characters
    158174
    159     enum {
    160         MaxRootEntries = 1280, // Top-level code such as <script>, eval(), JSEvaluateScript(), etc.
    161         MaxChildFunctionEntries = MaxRootEntries * 8 // Sampling shows that each root holds about 6 functions. 8 is enough to usually cache all the child functions for each top-level entry.
    162     };
    163 
    164     CacheMap<MaxRootEntries, SourceCodeKey, Strong<JSCell>, SourceCodeKeyHash, SourceCodeKeyHashTraits> m_sourceCode;
    165     CacheMap<MaxChildFunctionEntries, UnlinkedFunctionCodeBlock*, Strong<UnlinkedFunctionCodeBlock> > m_recentlyUsedFunctions;
     175    CodeCacheMap m_sourceCode;
    166176};
    167177
Note: See TracChangeset for help on using the changeset viewer.