Commit 036ef312 authored by ggaren@apple.com's avatar ggaren@apple.com

Merged the global function cache into the source code cache

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

Reviewed by Sam Weinig.

This has a few benefits:

    (*) Saves a few kB by removing a second cache data structure.

    (*) Reduces the worst case memory usage of the cache by 1.75X. (Heavy
    use of 'new Function' and other techniques could cause us to fill
    both root caches, and they didn't trade off against each other.)

    (*) Paves the way for future improvements based on a non-trivial
    cache key (for example, shrinkable pointer to the key string, and
    more precise cache size accounting).

Also cleaned up the cache implementation and simplified it a bit.

* heap/Handle.h:
(HandleBase):
* heap/Strong.h:
(Strong): Build!

* runtime/CodeCache.cpp:
(JSC):
(JSC::CodeCache::getCodeBlock):
(JSC::CodeCache::generateFunctionCodeBlock):
(JSC::CodeCache::getFunctionExecutableFromGlobalCode):
(JSC::CodeCache::usedFunctionCode): Updated for three interface changes:

    (*) SourceCodeKey is a class, not a pair.

    (*) Table values are abstract pointers, since they can be executables
    or code blocks. (In a future patch, I'd like to change this so we
    always store only code blocks. But that's too much for one patch.)

    (*) The cache function is named "set" because it always overwrites
    unconditionally.

* runtime/CodeCache.h:
(CacheMap):
(JSC::CacheMap::find):
(JSC::CacheMap::set):
(JSC::CacheMap::clear): Added support for specifying hash traits, so we
can use a SourceCodeKey.

Removed side table and random number generator to save space and reduce
complexity. Hash tables are already random, so we don't need another source
of randomness.

(SourceCodeKey):
(JSC::SourceCodeKey::SourceCodeKey):
(JSC::SourceCodeKey::isHashTableDeletedValue):
(JSC::SourceCodeKey::hash):
(JSC::SourceCodeKey::isNull):
(JSC::SourceCodeKey::operator==):
(JSC::SourceCodeKeyHash::hash):
(JSC::SourceCodeKeyHash::equal):
(SourceCodeKeyHash):
(SourceCodeKeyHashTraits):
(JSC::SourceCodeKeyHashTraits::isEmptyValue): A SourceCodeKey is just a
fancy triplet: source code string; function name (or null, for non-functions);
and flags. Flags and function name distinguish between functions and programs
with identical code, so they can live in the same cache.

I chose to use the source code string as the primary hashing reference
because it's likely to be unique. We can use profiling to choose another
technique in future, if collisions between functions and programs prove
to be hot. I suspect they won't.

(JSC::CodeCache::clear):
(CodeCache): Removed the second cache.

* heap/Handle.h:
(HandleBase):
* heap/Strong.h:
(Strong):
* runtime/CodeCache.cpp:
(JSC):
(JSC::CodeCache::getCodeBlock):
(JSC::CodeCache::generateFunctionCodeBlock):
(JSC::CodeCache::getFunctionExecutableFromGlobalCode):
(JSC::CodeCache::usedFunctionCode):
* runtime/CodeCache.h:
(JSC):
(CacheMap):
(JSC::CacheMap::find):
(JSC::CacheMap::set):
(JSC::CacheMap::clear):
(SourceCodeKey):
(JSC::SourceCodeKey::SourceCodeKey):
(JSC::SourceCodeKey::isHashTableDeletedValue):
(JSC::SourceCodeKey::hash):
(JSC::SourceCodeKey::isNull):
(JSC::SourceCodeKey::operator==):
(JSC::SourceCodeKeyHash::hash):
(JSC::SourceCodeKeyHash::equal):
(SourceCodeKeyHash):
(SourceCodeKeyHashTraits):
(JSC::SourceCodeKeyHashTraits::isEmptyValue):
(JSC::CodeCache::clear):
(CodeCache):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@142966 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent e2603c22
2013-02-14 Geoffrey Garen <ggaren@apple.com>
Merged the global function cache into the source code cache
https://bugs.webkit.org/show_bug.cgi?id=108660
Reviewed by Sam Weinig.
This has a few benefits:
(*) Saves a few kB by removing a second cache data structure.
(*) Reduces the worst case memory usage of the cache by 1.75X. (Heavy
use of 'new Function' and other techniques could cause us to fill
both root caches, and they didn't trade off against each other.)
(*) Paves the way for future improvements based on a non-trivial
cache key (for example, shrinkable pointer to the key string, and
more precise cache size accounting).
Also cleaned up the cache implementation and simplified it a bit.
* heap/Handle.h:
(HandleBase):
* heap/Strong.h:
(Strong): Build!
* runtime/CodeCache.cpp:
(JSC):
(JSC::CodeCache::getCodeBlock):
(JSC::CodeCache::generateFunctionCodeBlock):
(JSC::CodeCache::getFunctionExecutableFromGlobalCode):
(JSC::CodeCache::usedFunctionCode): Updated for three interface changes:
(*) SourceCodeKey is a class, not a pair.
(*) Table values are abstract pointers, since they can be executables
or code blocks. (In a future patch, I'd like to change this so we
always store only code blocks. But that's too much for one patch.)
(*) The cache function is named "set" because it always overwrites
unconditionally.
* runtime/CodeCache.h:
(CacheMap):
(JSC::CacheMap::find):
(JSC::CacheMap::set):
(JSC::CacheMap::clear): Added support for specifying hash traits, so we
can use a SourceCodeKey.
Removed side table and random number generator to save space and reduce
complexity. Hash tables are already random, so we don't need another source
of randomness.
(SourceCodeKey):
(JSC::SourceCodeKey::SourceCodeKey):
(JSC::SourceCodeKey::isHashTableDeletedValue):
(JSC::SourceCodeKey::hash):
(JSC::SourceCodeKey::isNull):
(JSC::SourceCodeKey::operator==):
(JSC::SourceCodeKeyHash::hash):
(JSC::SourceCodeKeyHash::equal):
(SourceCodeKeyHash):
(SourceCodeKeyHashTraits):
(JSC::SourceCodeKeyHashTraits::isEmptyValue): A SourceCodeKey is just a
fancy triplet: source code string; function name (or null, for non-functions);
and flags. Flags and function name distinguish between functions and programs
with identical code, so they can live in the same cache.
I chose to use the source code string as the primary hashing reference
because it's likely to be unique. We can use profiling to choose another
technique in future, if collisions between functions and programs prove
to be hot. I suspect they won't.
(JSC::CodeCache::clear):
(CodeCache): Removed the second cache.
* heap/Handle.h:
(HandleBase):
* heap/Strong.h:
(Strong):
* runtime/CodeCache.cpp:
(JSC):
(JSC::CodeCache::getCodeBlock):
(JSC::CodeCache::generateFunctionCodeBlock):
(JSC::CodeCache::getFunctionExecutableFromGlobalCode):
(JSC::CodeCache::usedFunctionCode):
* runtime/CodeCache.h:
(JSC):
(CacheMap):
(JSC::CacheMap::find):
(JSC::CacheMap::set):
(JSC::CacheMap::clear):
(SourceCodeKey):
(JSC::SourceCodeKey::SourceCodeKey):
(JSC::SourceCodeKey::isHashTableDeletedValue):
(JSC::SourceCodeKey::hash):
(JSC::SourceCodeKey::isNull):
(JSC::SourceCodeKey::operator==):
(JSC::SourceCodeKeyHash::hash):
(JSC::SourceCodeKeyHash::equal):
(SourceCodeKeyHash):
(SourceCodeKeyHashTraits):
(JSC::SourceCodeKeyHashTraits::isEmptyValue):
(JSC::CodeCache::clear):
(CodeCache):
2013-02-14 Tony Chang <tony@chromium.org>
Unreviewed, set svn:eol-style native for .sln, .vcproj, and .vsprops files.
......
......@@ -45,6 +45,7 @@ template <> class Handle<JSValue>;
class HandleBase {
template <typename T> friend class Weak;
template <typename T> friend class Strong;
friend class HandleSet;
friend struct JSCallbackObjectData;
......
......@@ -38,6 +38,7 @@ class JSGlobalData;
template <typename T> class Strong : public Handle<T> {
using Handle<T>::slot;
using Handle<T>::setSlot;
template <typename U> friend class Strong;
public:
typedef typename Handle<T>::ExternalType ExternalType;
......
......@@ -44,30 +44,25 @@ CodeCache::~CodeCache()
{
}
CodeCache::SourceCodeKey CodeCache::makeSourceCodeKey(const SourceCode& source, CodeCache::CodeType type, JSParserStrictness strictness)
{
return std::make_pair(source.toString(), (type << 1) | strictness);
}
template <typename T> struct CacheTypes { };
template <> struct CacheTypes<UnlinkedProgramCodeBlock> {
typedef JSC::ProgramNode RootNode;
static const CodeCache::CodeType codeType = CodeCache::ProgramType;
static const SourceCodeKey::CodeType codeType = SourceCodeKey::ProgramType;
};
template <> struct CacheTypes<UnlinkedEvalCodeBlock> {
typedef JSC::EvalNode RootNode;
static const CodeCache::CodeType codeType = CodeCache::EvalType;
static const SourceCodeKey::CodeType codeType = SourceCodeKey::EvalType;
};
template <class UnlinkedCodeBlockType, class ExecutableType>
UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, ExecutableType* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
{
SourceCodeKey key = makeSourceCodeKey(source, CacheTypes<UnlinkedCodeBlockType>::codeType, strictness);
SourceCodeKey key = SourceCodeKey(source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictness);
bool storeInCache = false;
if (debuggerMode == DebuggerOff && profilerMode == ProfilerOff) {
const Strong<UnlinkedCodeBlock>* result = m_sourceCode.find(key);
const Strong<JSCell>* result = m_sourceCode.find(key);
if (result) {
UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(result->get());
unsigned firstLine = source.firstLine() + unlinkedCode->firstLine();
......@@ -92,7 +87,7 @@ UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, Executa
return 0;
if (storeInCache)
m_sourceCode.add(key, Strong<UnlinkedCodeBlock>(globalData, unlinkedCode));
m_sourceCode.set(key, Strong<UnlinkedCodeBlock>(globalData, unlinkedCode));
return unlinkedCode;
}
......@@ -127,7 +122,7 @@ UnlinkedFunctionCodeBlock* CodeCache::generateFunctionCodeBlock(JSGlobalData& gl
body->destroyData();
if (error.m_type != ParserError::ErrorNone)
return 0;
m_recentlyUsedFunctions.add(result, Strong<UnlinkedFunctionCodeBlock>(globalData, result));
m_recentlyUsedFunctions.set(result, Strong<UnlinkedFunctionCodeBlock>(globalData, result));
return result;
}
......@@ -136,17 +131,12 @@ UnlinkedFunctionCodeBlock* CodeCache::getFunctionCodeBlock(JSGlobalData& globalD
return generateFunctionCodeBlock(globalData, executable, source, kind, debuggerMode, profilerMode, error);
}
CodeCache::FunctionKey CodeCache::makeFunctionKey(const SourceCode& source, const String& name)
{
return FunctionKey(source.toString(), name);
}
UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, ParserError& error)
{
FunctionKey key = makeFunctionKey(source, name.string());
const Strong<UnlinkedFunctionExecutable>* result = m_globalFunctions.find(key);
SourceCodeKey key = SourceCodeKey(source, name.string(), SourceCodeKey::FunctionCallType, JSParseNormal);
const Strong<JSCell>* result = m_sourceCode.find(key);
if (result)
return result->get();
return jsCast<UnlinkedFunctionExecutable*>(result->get());
RefPtr<ProgramNode> program = parse<ProgramNode>(&globalData, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error);
if (!program) {
......@@ -168,13 +158,13 @@ UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlo
UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&globalData, source, body);
functionExecutable->m_nameValue.set(globalData, functionExecutable, jsString(&globalData, name.string()));
m_globalFunctions.add(key, Strong<UnlinkedFunctionExecutable>(globalData, functionExecutable));
m_sourceCode.set(key, Strong<UnlinkedFunctionExecutable>(globalData, functionExecutable));
return functionExecutable;
}
void CodeCache::usedFunctionCode(JSGlobalData& globalData, UnlinkedFunctionCodeBlock* codeBlock)
{
m_recentlyUsedFunctions.add(codeBlock, Strong<UnlinkedFunctionCodeBlock>(globalData, codeBlock));
m_recentlyUsedFunctions.set(codeBlock, Strong<UnlinkedFunctionCodeBlock>(globalData, codeBlock));
}
}
......@@ -28,9 +28,9 @@
#include "CodeSpecializationKind.h"
#include "ParserModes.h"
#include "SourceCode.h"
#include "Strong.h"
#include "WeakRandom.h"
#include <wtf/FixedArray.h>
#include <wtf/Forward.h>
#include <wtf/PassOwnPtr.h>
......@@ -53,50 +53,85 @@ struct ParserError;
class SourceCode;
class SourceProvider;
template <typename KeyType, typename EntryType, int CacheSize> class CacheMap {
typedef typename HashMap<KeyType, unsigned>::iterator iterator;
template <int CacheSize, typename KeyType, typename EntryType, typename HashArg = typename DefaultHash<KeyType>::Hash, typename KeyTraitsArg = HashTraits<KeyType> >
class CacheMap {
typedef HashMap<KeyType, EntryType, HashArg, KeyTraitsArg> MapType;
typedef typename MapType::iterator iterator;
public:
CacheMap()
: m_randomGenerator((static_cast<uint32_t>(randomNumber() * std::numeric_limits<uint32_t>::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;
return &result->value;
}
void add(const KeyType& key, const EntryType& value)
void set(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;
RELEASE_ASSERT(m_map.size() <= CacheSize);
if (m_map.size() >= CacheSize)
m_map.remove(m_map.begin());
m_map.set(key, value);
}
void clear()
void clear() { m_map.clear(); }
private:
MapType m_map;
};
class SourceCodeKey {
public:
enum CodeType { EvalType, ProgramType, FunctionCallType, FunctionConstructType };
SourceCodeKey()
: m_flags(0)
{
}
SourceCodeKey(const SourceCode& sourceCode, const String& name, CodeType codeType, JSParserStrictness jsParserStrictness)
: m_sourceString(sourceCode.toString())
, m_name(name)
, m_flags((codeType << 1) | jsParserStrictness)
{
}
SourceCodeKey(WTF::HashTableDeletedValueType)
: m_sourceString(WTF::HashTableDeletedValue)
, m_name(WTF::HashTableDeletedValue)
, m_flags(0)
{
m_map.clear();
for (size_t i = 0; i < CacheSize; i++) {
m_data[i].first = KeyType();
m_data[i].second = EntryType();
}
}
bool isHashTableDeletedValue() const { return m_sourceString.isHashTableDeletedValue(); }
unsigned hash() const { return m_sourceString.impl()->hash(); }
bool isNull() const { return m_sourceString.isNull(); }
bool operator==(const SourceCodeKey& other) const
{
return m_flags == other.m_flags
&& m_name == other.m_name
&& m_sourceString == other.m_sourceString;
}
private:
HashMap<KeyType, unsigned> m_map;
FixedArray<std::pair<KeyType, EntryType>, CacheSize> m_data;
WeakRandom m_randomGenerator;
String m_sourceString;
String m_name;
unsigned m_flags;
};
struct SourceCodeKeyHash {
static unsigned hash(const SourceCodeKey& key) { return key.hash(); }
static bool equal(const SourceCodeKey& a, const SourceCodeKey& b) { return a == b; }
static const bool safeToCompareToEmptyOrDeleted = false;
};
struct SourceCodeKeyHashTraits : WTF::SimpleClassHashTraits<SourceCodeKey> {
static const bool hasIsEmptyValueFunction = true;
static bool isEmptyValue(const SourceCodeKey& sourceCodeKey) { return sourceCodeKey.isNull(); }
};
class CodeCache {
......@@ -113,33 +148,25 @@ public:
void clear()
{
m_sourceCode.clear();
m_globalFunctions.clear();
m_recentlyUsedFunctions.clear();
}
enum CodeType { EvalType, ProgramType, FunctionCallType, FunctionConstructType };
typedef std::pair<String, unsigned> SourceCodeKey;
typedef std::pair<String, String> FunctionKey;
private:
CodeCache();
UnlinkedFunctionCodeBlock* generateFunctionCodeBlock(JSGlobalData&, UnlinkedFunctionExecutable*, const SourceCode&, CodeSpecializationKind, DebuggerMode, ProfilerMode, ParserError&);
template <class UnlinkedCodeBlockType, class ExecutableType> inline UnlinkedCodeBlockType* getCodeBlock(JSGlobalData&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
SourceCodeKey makeSourceCodeKey(const SourceCode&, CodeType, JSParserStrictness);
FunctionKey makeFunctionKey(const SourceCode&, const String&);
enum {
MaxRootEntries = 1024, // Top-level code such as <script>, eval(), JSEvaluateScript(), etc.
MaxRootEntries = 1280, // Top-level code such as <script>, eval(), JSEvaluateScript(), etc.
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.
};
CacheMap<SourceCodeKey, Strong<UnlinkedCodeBlock>, MaxRootEntries> m_sourceCode;
CacheMap<FunctionKey, Strong<UnlinkedFunctionExecutable>, MaxRootEntries> m_globalFunctions;
CacheMap<UnlinkedFunctionCodeBlock*, Strong<UnlinkedFunctionCodeBlock>, MaxChildFunctionEntries> m_recentlyUsedFunctions;
CacheMap<MaxRootEntries, SourceCodeKey, Strong<JSCell>, SourceCodeKeyHash, SourceCodeKeyHashTraits> m_sourceCode;
CacheMap<MaxChildFunctionEntries, UnlinkedFunctionCodeBlock*, Strong<UnlinkedFunctionCodeBlock> > m_recentlyUsedFunctions;
};
}
#endif
#endif // CodeCache_h
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