Commit 27e0eed2 authored by oliver@apple.com's avatar oliver@apple.com

Improve effectiveness of function-level caching

https://bugs.webkit.org/show_bug.cgi?id=101667

Reviewed by Filip Pizlo.

Added a random-eviction based cache for unlinked functions, and switch
UnlinkedFunctionExecutable's code references to Weak<>, thereby letting
us remove the explicit UnlinkedFunctionExecutable::clearCode() calls that
were being triggered by GC.

Refactored the random eviction part of the CodeCache into a separate data
structure so that I didn't have to duplicate the code again, and then used
that for the new function cache.

* bytecode/UnlinkedCodeBlock.cpp:
(JSC::UnlinkedFunctionExecutable::visitChildren):
(JSC::UnlinkedFunctionExecutable::codeBlockFor):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedFunctionExecutable::clearCodeForRecompilation):
(UnlinkedFunctionExecutable):
* debugger/Debugger.cpp:
* runtime/CodeCache.cpp:
(JSC::CodeCache::getCodeBlock):
(JSC::CodeCache::generateFunctionCodeBlock):
(JSC::CodeCache::getFunctionExecutableFromGlobalCode):
(JSC::CodeCache::usedFunctionCode):
(JSC):
* runtime/Executable.cpp:
(JSC::FunctionExecutable::clearUnlinkedCodeForRecompilationIfNotCompiling):
(JSC::FunctionExecutable::clearCode):
* runtime/Executable.h:
(FunctionExecutable):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@133975 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 5b7d03f1
2012-11-08 Oliver Hunt <oliver@apple.com>
Improve effectiveness of function-level caching
https://bugs.webkit.org/show_bug.cgi?id=101667
Reviewed by Filip Pizlo.
Added a random-eviction based cache for unlinked functions, and switch
UnlinkedFunctionExecutable's code references to Weak<>, thereby letting
us remove the explicit UnlinkedFunctionExecutable::clearCode() calls that
were being triggered by GC.
Refactored the random eviction part of the CodeCache into a separate data
structure so that I didn't have to duplicate the code again, and then used
that for the new function cache.
* bytecode/UnlinkedCodeBlock.cpp:
(JSC::UnlinkedFunctionExecutable::visitChildren):
(JSC::UnlinkedFunctionExecutable::codeBlockFor):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedFunctionExecutable::clearCodeForRecompilation):
(UnlinkedFunctionExecutable):
* debugger/Debugger.cpp:
* runtime/CodeCache.cpp:
(JSC::CodeCache::getCodeBlock):
(JSC::CodeCache::generateFunctionCodeBlock):
(JSC::CodeCache::getFunctionExecutableFromGlobalCode):
(JSC::CodeCache::usedFunctionCode):
(JSC):
* runtime/Executable.cpp:
(JSC::FunctionExecutable::clearUnlinkedCodeForRecompilationIfNotCompiling):
(JSC::FunctionExecutable::clearCode):
* runtime/Executable.h:
(FunctionExecutable):
2012-11-07 Filip Pizlo <fpizlo@apple.com>
DFG constant folding and CFG simplification should be smart enough to know that if a logical op's operand is proven to have a non-masquerading structure then it always evaluates to true
......
......@@ -80,8 +80,6 @@ void UnlinkedFunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visito
COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
Base::visitChildren(thisObject, visitor);
visitor.append(&thisObject->m_codeBlockForCall);
visitor.append(&thisObject->m_codeBlockForConstruct);
visitor.append(&thisObject->m_nameValue);
visitor.append(&thisObject->m_symbolTableForCall);
visitor.append(&thisObject->m_symbolTableForConstruct);
......@@ -112,12 +110,16 @@ UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::codeBlockFor(JSGlobalData
{
switch (specializationKind) {
case CodeForCall:
if (m_codeBlockForCall)
return m_codeBlockForCall.get();
if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get()) {
globalData.codeCache()->usedFunctionCode(globalData, codeBlock);
return codeBlock;
}
break;
case CodeForConstruct:
if (m_codeBlockForConstruct)
return m_codeBlockForConstruct.get();
if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get()) {
globalData.codeCache()->usedFunctionCode(globalData, codeBlock);
return codeBlock;
}
break;
}
......@@ -128,11 +130,11 @@ UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::codeBlockFor(JSGlobalData
switch (specializationKind) {
case CodeForCall:
m_codeBlockForCall.set(globalData, this, result);
m_codeBlockForCall = PassWeak<UnlinkedFunctionCodeBlock>(result);
m_symbolTableForCall.set(globalData, this, result->symbolTable());
break;
case CodeForConstruct:
m_codeBlockForConstruct.set(globalData, this, result);
m_codeBlockForConstruct = PassWeak<UnlinkedFunctionCodeBlock>(result);
m_symbolTableForConstruct.set(globalData, this, result->symbolTable());
break;
}
......
......@@ -36,6 +36,7 @@
#include "Nodes.h"
#include "RegExp.h"
#include "SpecialPointer.h"
#include "Weak.h"
#include <wtf/RefCountedArray.h>
#include <wtf/Vector.h>
......@@ -108,7 +109,7 @@ public:
FunctionExecutable* link(JSGlobalData&, const SourceCode&, size_t lineOffset, size_t sourceOffset);
void clearCode()
void clearCodeForRecompilation()
{
m_symbolTableForCall.clear();
m_symbolTableForConstruct.clear();
......@@ -136,8 +137,8 @@ public:
private:
UnlinkedFunctionExecutable(JSGlobalData*, Structure*, const SourceCode&, FunctionBodyNode*);
WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForCall;
WriteBarrier<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct;
Weak<UnlinkedFunctionCodeBlock> m_codeBlockForCall;
Weak<UnlinkedFunctionCodeBlock> m_codeBlockForConstruct;
unsigned m_numCapturedVariables : 29;
bool m_forceUsesArguments : 1;
......
......@@ -80,7 +80,7 @@ inline void Recompiler::operator()(JSCell* cell)
ExecState* exec = function->scope()->globalObject()->JSGlobalObject::globalExec();
executable->clearCodeIfNotCompiling();
executable->clearUnlinkedCodeIfNotCompiling();
executable->clearUnlinkedCodeForRecompilationIfNotCompiling();
if (m_debugger == function->scope()->globalObject()->debugger())
m_sourceProviders.add(executable->source().provider(), exec);
}
......
......@@ -36,7 +36,6 @@
namespace JSC {
CodeCache::CodeCache()
: m_randomGenerator(static_cast<uint32_t>(randomNumber() * UINT32_MAX))
{
}
......@@ -67,9 +66,9 @@ UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, Executa
CodeBlockKey key = makeCodeBlockKey(source, CacheTypes<UnlinkedCodeBlockType>::codeType, strictness);
bool storeInCache = false;
if (debuggerMode == DebuggerOff && profilerMode == ProfilerOff) {
CodeBlockIndicesMap::iterator result = m_cachedCodeBlockIndices.find(key);
if (result != m_cachedCodeBlockIndices.end()) {
UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(m_cachedCodeBlocks[result->value].second.get());
const Strong<UnlinkedCodeBlock>* result = m_cachedCodeBlocks.find(key);
if (result) {
UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(result->get());
unsigned firstLine = source.firstLine() + unlinkedCode->firstLine();
executable->recordParse(unlinkedCode->codeFeatures(), unlinkedCode->hasCapturedVariables(), firstLine, firstLine + unlinkedCode->lineCount());
return unlinkedCode;
......@@ -91,14 +90,8 @@ UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, Executa
if (error.m_type != ParserError::ErrorNone)
return 0;
if (storeInCache) {
size_t index = m_randomGenerator.getUint32() % kMaxCodeBlockEntries;
if (m_cachedCodeBlocks[index].second)
m_cachedCodeBlockIndices.remove(m_cachedCodeBlocks[index].first);
m_cachedCodeBlockIndices.set(key, index);
m_cachedCodeBlocks[index].second.set(globalData, unlinkedCode);
m_cachedCodeBlocks[index].first = key;
}
if (storeInCache)
m_cachedCodeBlocks.add(key, Strong<UnlinkedCodeBlock>(globalData, unlinkedCode));
return unlinkedCode;
}
......@@ -133,6 +126,7 @@ UnlinkedFunctionCodeBlock* CodeCache::generateFunctionCodeBlock(JSGlobalData& gl
body->destroyData();
if (error.m_type != ParserError::ErrorNone)
return 0;
m_cachedFunctionCode.add(result, Strong<UnlinkedFunctionCodeBlock>(globalData, result));
return result;
}
......@@ -149,9 +143,9 @@ CodeCache::GlobalFunctionKey CodeCache::makeGlobalFunctionKey(const SourceCode&
UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, ParserError& error)
{
GlobalFunctionKey key = makeGlobalFunctionKey(source, name.string());
GlobalFunctionIndicesMap::iterator result = m_cachedGlobalFunctionIndices.find(key);
if (result != m_cachedGlobalFunctionIndices.end())
return m_cachedGlobalFunctions[result->value].second.get();
const Strong<UnlinkedFunctionExecutable>* result = m_cachedGlobalFunctions.find(key);
if (result)
return result->get();
RefPtr<ProgramNode> program = parse<ProgramNode>(&globalData, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error);
if (!program) {
......@@ -173,14 +167,13 @@ UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlo
UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&globalData, source, body);
functionExecutable->m_nameValue.set(globalData, functionExecutable, jsString(&globalData, name.string()));
size_t index = m_randomGenerator.getUint32() % kMaxGlobalFunctionEntries;
if (m_cachedGlobalFunctions[index].second)
m_cachedGlobalFunctionIndices.remove(m_cachedGlobalFunctions[index].first);
m_cachedGlobalFunctionIndices.set(key, index);
m_cachedGlobalFunctions[index].second.set(globalData, functionExecutable);
m_cachedGlobalFunctions[index].first = key;
m_cachedGlobalFunctions.add(key, Strong<UnlinkedFunctionExecutable>(globalData, functionExecutable));
return functionExecutable;
}
void CodeCache::usedFunctionCode(JSGlobalData& globalData, UnlinkedFunctionCodeBlock* codeBlock)
{
m_cachedFunctionCode.add(codeBlock, Strong<UnlinkedFunctionCodeBlock>(globalData, codeBlock));
}
}
......@@ -34,6 +34,7 @@
#include <wtf/FixedArray.h>
#include <wtf/Forward.h>
#include <wtf/PassOwnPtr.h>
#include <wtf/RandomNumber.h>
#include <wtf/text/WTFString.h>
namespace JSC {
......@@ -51,6 +52,41 @@ struct ParserError;
class SourceCode;
class SourceProvider;
template <typename KeyType, typename EntryType, int CacheSize> class Thingy {
typedef typename HashMap<KeyType, unsigned>::iterator iterator;
public:
Thingy()
: m_randomGenerator((static_cast<uint32_t>(randomNumber() * UINT32_MAX)))
{
}
const EntryType* find(const KeyType& key)
{
iterator result = m_map.find(key);
if (result == m_map.end())
return 0;
return &m_data[result->value].second;
}
void add(const KeyType& key, const EntryType& value)
{
iterator result = m_map.find(key);
if (result != m_map.end()) {
m_data[result->value].second = value;
return;
}
size_t newIndex = m_randomGenerator.getUint32() % CacheSize;
if (m_data[newIndex].second)
m_map.remove(m_data[newIndex].first);
m_map.add(key, newIndex);
m_data[newIndex].first = key;
m_data[newIndex].second = value;
ASSERT(m_map.size() <= CacheSize);
}
private:
HashMap<KeyType, unsigned> m_map;
FixedArray<std::pair<KeyType, EntryType>, CacheSize> m_data;
WeakRandom m_randomGenerator;
};
class CodeCache {
public:
static PassOwnPtr<CodeCache> create() { return adoptPtr(new CodeCache); }
......@@ -59,13 +95,12 @@ public:
UnlinkedEvalCodeBlock* getEvalCodeBlock(JSGlobalData&, EvalExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
UnlinkedFunctionCodeBlock* getFunctionCodeBlock(JSGlobalData&, UnlinkedFunctionExecutable*, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, ParserError&);
UnlinkedFunctionExecutable* getFunctionExecutableFromGlobalCode(JSGlobalData&, const Identifier&, const SourceCode&, ParserError&);
void usedFunctionCode(JSGlobalData&, UnlinkedFunctionCodeBlock*);
~CodeCache();
enum CodeType { EvalType, ProgramType, FunctionType };
typedef std::pair<String, unsigned> CodeBlockKey;
typedef HashMap<CodeBlockKey, unsigned> CodeBlockIndicesMap;
typedef std::pair<String, String> GlobalFunctionKey;
typedef HashMap<GlobalFunctionKey, unsigned> GlobalFunctionIndicesMap;
private:
CodeCache();
......@@ -74,18 +109,17 @@ private:
template <class UnlinkedCodeBlockType, class ExecutableType> inline UnlinkedCodeBlockType* getCodeBlock(JSGlobalData&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
CodeBlockKey makeCodeBlockKey(const SourceCode&, CodeType, JSParserStrictness);
CodeBlockIndicesMap m_cachedCodeBlockIndices;
GlobalFunctionKey makeGlobalFunctionKey(const SourceCode&, const String&);
GlobalFunctionIndicesMap m_cachedGlobalFunctionIndices;
enum {
kMaxCodeBlockEntries = 1024,
kMaxGlobalFunctionEntries = 1024
kMaxGlobalFunctionEntries = 1024,
kMaxFunctionCodeBlocks = 1024
};
FixedArray<std::pair<CodeBlockKey, Strong<UnlinkedCodeBlock> >, kMaxCodeBlockEntries> m_cachedCodeBlocks;
FixedArray<std::pair<GlobalFunctionKey, Strong<UnlinkedFunctionExecutable> >, kMaxGlobalFunctionEntries> m_cachedGlobalFunctions;
WeakRandom m_randomGenerator;
Thingy<CodeBlockKey, Strong<UnlinkedCodeBlock>, kMaxCodeBlockEntries> m_cachedCodeBlocks;
Thingy<GlobalFunctionKey, Strong<UnlinkedFunctionExecutable>, kMaxGlobalFunctionEntries> m_cachedGlobalFunctions;
Thingy<UnlinkedFunctionCodeBlock*, Strong<UnlinkedFunctionCodeBlock>, kMaxFunctionCodeBlocks> m_cachedFunctionCode;
};
}
......
......@@ -620,18 +620,17 @@ void FunctionExecutable::clearCodeIfNotCompiling()
clearCode();
}
void FunctionExecutable::clearUnlinkedCodeIfNotCompiling()
void FunctionExecutable::clearUnlinkedCodeForRecompilationIfNotCompiling()
{
if (isCompiling())
return;
m_unlinkedExecutable->clearCode();
m_unlinkedExecutable->clearCodeForRecompilation();
}
void FunctionExecutable::clearCode()
{
m_codeBlockForCall.clear();
m_codeBlockForConstruct.clear();
m_unlinkedExecutable->clearCode();
Base::clearCode();
}
......
......@@ -704,7 +704,7 @@ namespace JSC {
SharedSymbolTable* symbolTable(CodeSpecializationKind kind) const { return m_unlinkedExecutable->symbolTable(kind); }
void clearCodeIfNotCompiling();
void clearUnlinkedCodeIfNotCompiling();
void clearUnlinkedCodeForRecompilationIfNotCompiling();
static void visitChildren(JSCell*, SlotVisitor&);
static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment