Changeset 143133 in webkit
- Timestamp:
- Feb 17, 2013 11:15:30 AM (11 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r143122 r143133 1 2013-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 1 62 2013-02-16 Filip Pizlo <fpizlo@apple.com> 2 63 -
trunk/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
r142649 r143133 47 47 const ClassInfo UnlinkedFunctionCodeBlock::s_info = { "UnlinkedFunctionCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedFunctionCodeBlock) }; 48 48 49 static 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 49 72 unsigned UnlinkedCodeBlock::addOrFindConstant(JSValue v) 50 73 { … … 83 106 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); 84 107 Base::visitChildren(thisObject, visitor); 108 visitor.append(&thisObject->m_codeBlockForCall); 109 visitor.append(&thisObject->m_codeBlockForConstruct); 85 110 visitor.append(&thisObject->m_nameValue); 86 111 visitor.append(&thisObject->m_symbolTableForCall); … … 113 138 switch (specializationKind) { 114 139 case CodeForCall: 115 if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get()) { 116 globalData.codeCache()->usedFunctionCode(globalData, codeBlock); 140 if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get()) 117 141 return codeBlock; 118 }119 142 break; 120 143 case CodeForConstruct: 121 if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get()) { 122 globalData.codeCache()->usedFunctionCode(globalData, codeBlock); 144 if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get()) 123 145 return codeBlock; 124 }125 146 break; 126 147 } 127 148 128 UnlinkedFunctionCodeBlock* result = g lobalData.codeCache()->getFunctionCodeBlock(globalData, this, source, specializationKind, debuggerMode, profilerMode, error);149 UnlinkedFunctionCodeBlock* result = generateFunctionCodeBlock(globalData, this, source, specializationKind, debuggerMode, profilerMode, error); 129 150 130 151 if (error.m_type != ParserError::ErrorNone) … … 133 154 switch (specializationKind) { 134 155 case CodeForCall: 135 m_codeBlockForCall = PassWeak<UnlinkedFunctionCodeBlock>(result);156 m_codeBlockForCall.set(globalData, this, result); 136 157 m_symbolTableForCall.set(globalData, this, result->symbolTable()); 137 158 break; 138 159 case CodeForConstruct: 139 m_codeBlockForConstruct = PassWeak<UnlinkedFunctionCodeBlock>(result);160 m_codeBlockForConstruct.set(globalData, this, result); 140 161 m_symbolTableForConstruct.set(globalData, this, result->symbolTable()); 141 162 break; -
trunk/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
r142649 r143133 140 140 private: 141 141 UnlinkedFunctionExecutable(JSGlobalData*, Structure*, const SourceCode&, FunctionBodyNode*); 142 W eak<UnlinkedFunctionCodeBlock> m_codeBlockForCall;143 W eak<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct;142 WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForCall; 143 WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct; 144 144 145 145 unsigned m_numCapturedVariables : 29; … … 676 676 677 677 class UnlinkedFunctionCodeBlock : public UnlinkedCodeBlock { 678 private: 679 friend class CodeCache; 680 678 public: 681 679 static UnlinkedFunctionCodeBlock* create(JSGlobalData* globalData, CodeType codeType, const ExecutableInfo& info) 682 680 { … … 686 684 } 687 685 688 public:689 686 typedef UnlinkedCodeBlock Base; 690 687 static void destroy(JSCell*); -
trunk/Source/JavaScriptCore/runtime/CodeCache.cpp
r142966 r143133 38 38 39 39 CodeCache::CodeCache() 40 : m_sourceCode(CacheSize) 40 41 { 41 42 } … … 103 104 } 104 105 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 134 106 UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, ParserError& error) 135 107 { … … 163 135 } 164 136 165 void CodeCache::usedFunctionCode(JSGlobalData& globalData, UnlinkedFunctionCodeBlock* codeBlock)166 {167 m_recentlyUsedFunctions.set(codeBlock, Strong<UnlinkedFunctionCodeBlock>(globalData, codeBlock));168 137 } 169 170 } -
trunk/Source/JavaScriptCore/runtime/CodeCache.h
r143027 r143133 1 1 /* 2 * Copyright (C) 2012 Apple Inc. All Rights Reserved.2 * Copyright (C) 2012, 2013 Apple Inc. All Rights Reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 54 54 class SourceProvider; 55 55 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 84 56 class SourceCodeKey { 85 57 public: … … 106 78 107 79 unsigned hash() const { return m_sourceString.impl()->hash(); } 80 81 size_t length() const { return m_sourceString.length(); } 108 82 109 83 bool isNull() const { return m_sourceString.isNull(); } … … 133 107 }; 134 108 109 class CodeCacheMap { 110 typedef HashMap<SourceCodeKey, Strong<JSCell>, SourceCodeKeyHash, SourceCodeKeyHashTraits> MapType; 111 typedef typename MapType::iterator iterator; 112 113 public: 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 146 private: 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(). 135 153 class CodeCache { 136 154 public: … … 139 157 UnlinkedProgramCodeBlock* getProgramCodeBlock(JSGlobalData&, ProgramExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); 140 158 UnlinkedEvalCodeBlock* getEvalCodeBlock(JSGlobalData&, EvalExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); 141 UnlinkedFunctionCodeBlock* getFunctionCodeBlock(JSGlobalData&, UnlinkedFunctionExecutable*, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, ParserError&);142 159 UnlinkedFunctionExecutable* getFunctionExecutableFromGlobalCode(JSGlobalData&, const Identifier&, const SourceCode&, ParserError&); 143 void usedFunctionCode(JSGlobalData&, UnlinkedFunctionCodeBlock*);144 160 ~CodeCache(); 145 161 … … 147 163 { 148 164 m_sourceCode.clear(); 149 m_recentlyUsedFunctions.clear();150 165 } 151 166 … … 153 168 CodeCache(); 154 169 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&); 156 172 157 template <class UnlinkedCodeBlockType, class ExecutableType> inline UnlinkedCodeBlockType* getCodeBlock(JSGlobalData&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);173 enum { CacheSize = 16000000 }; // Size in characters 158 174 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; 166 176 }; 167 177
Note: See TracChangeset
for help on using the changeset viewer.