Modified: trunk/Source/_javascript_Core/ChangeLog (143758 => 143759)
--- trunk/Source/_javascript_Core/ChangeLog 2013-02-22 19:14:22 UTC (rev 143758)
+++ trunk/Source/_javascript_Core/ChangeLog 2013-02-22 19:16:03 UTC (rev 143759)
@@ -1,3 +1,30 @@
+2013-02-21 Geoffrey Garen <[email protected]>
+
+ Code cache size should adapt to workload
+ https://bugs.webkit.org/show_bug.cgi?id=110560
+
+ Reviewed by Antti Koivisto.
+
+ (*) 5% PLT arithmetic mean speedup
+ (*) 10% PLT geometric mean speedup
+ (*) 3.4X microbenchmark speedup
+ (*) Reduces initial cache capacity by 16X
+
+ * runtime/CodeCache.cpp:
+ (JSC::CodeCache::CodeCache): Updated for interface change.
+
+ * runtime/CodeCache.h:
+ (JSC::SourceCodeValue::SourceCodeValue):
+ (SourceCodeValue): Turned the cache value into a struct so it can track its age.
+
+ (CodeCacheMap):
+ (JSC::CodeCacheMap::CodeCacheMap):
+ (JSC::CodeCacheMap::find):
+ (JSC::CodeCacheMap::set):
+ (JSC::CodeCacheMap::clear):
+ (JSC::CodeCacheMap::pruneIfNeeded):
+ (CodeCache): Grow and shrink in response to usage.
+
2013-02-21 Jessie Berlin <[email protected]>
Fix a typo that broke the 32 bit build.
Modified: trunk/Source/_javascript_Core/runtime/CodeCache.cpp (143758 => 143759)
--- trunk/Source/_javascript_Core/runtime/CodeCache.cpp 2013-02-22 19:14:22 UTC (rev 143758)
+++ trunk/Source/_javascript_Core/runtime/CodeCache.cpp 2013-02-22 19:16:03 UTC (rev 143759)
@@ -37,7 +37,6 @@
namespace JSC {
CodeCache::CodeCache()
- : m_sourceCode(CacheSize)
{
}
Modified: trunk/Source/_javascript_Core/runtime/CodeCache.h (143758 => 143759)
--- trunk/Source/_javascript_Core/runtime/CodeCache.h 2013-02-22 19:14:22 UTC (rev 143758)
+++ trunk/Source/_javascript_Core/runtime/CodeCache.h 2013-02-22 19:16:03 UTC (rev 143759)
@@ -113,47 +113,100 @@
static bool isEmptyValue(const SourceCodeKey& sourceCodeKey) { return sourceCodeKey.isNull(); }
};
+struct SourceCodeValue {
+ SourceCodeValue()
+ {
+ }
+
+ SourceCodeValue(const Strong<JSCell>& cell, int64_t age)
+ : cell(cell)
+ , age(age)
+ {
+ }
+
+ Strong<JSCell> cell;
+ int64_t age;
+};
+
class CodeCacheMap {
- typedef HashMap<SourceCodeKey, Strong<JSCell>, SourceCodeKeyHash, SourceCodeKeyHashTraits> MapType;
+ typedef HashMap<SourceCodeKey, SourceCodeValue, SourceCodeKeyHash, SourceCodeKeyHashTraits> MapType;
typedef MapType::iterator iterator;
public:
- CodeCacheMap(size_t capacity)
+ enum { MinCacheCapacity = 1000000 }; // Size in characters
+
+ CodeCacheMap()
: m_size(0)
- , m_capacity(capacity)
+ , m_capacity(MinCacheCapacity)
+ , m_age(0)
{
}
const Strong<JSCell>* find(const SourceCodeKey& key)
{
- iterator result = m_map.find(key);
- if (result == m_map.end())
+ iterator it = m_map.find(key);
+ if (it == m_map.end())
return 0;
- return &result->value;
+
+ size_t age = m_age - it->value.age;
+ if (age > m_capacity) {
+ // A requested object is older than the cache's capacity. We can
+ // infer that requested objects are subject to high eviction probability,
+ // so we grow the cache to improve our hit rate.
+ m_capacity += recencyBias * oldObjectSamplingMultiplier * key.length();
+ } else if (age < m_capacity / 2) {
+ // A requested object is much younger than the cache's capacity. We can
+ // infer that requested objects are subject to low eviction probability,
+ // so we shrink the cache to save memory.
+ m_capacity -= recencyBias * key.length();
+ if (m_capacity < MinCacheCapacity)
+ m_capacity = MinCacheCapacity;
+ }
+
+ it->value.age = m_age;
+ m_age += key.length();
+ return &it->value.cell;
}
void set(const SourceCodeKey& key, const Strong<JSCell>& value)
{
- while (m_size >= m_capacity) {
- MapType::iterator it = m_map.begin();
- m_size -= it->key.length();
- m_map.remove(it);
- }
+ pruneIfNeeded();
m_size += key.length();
- m_map.set(key, value);
+ m_age += key.length();
+ m_map.set(key, SourceCodeValue(value, m_age));
}
void clear()
{
m_size = 0;
+ m_age = 0;
m_map.clear();
}
private:
+ // This constant factor biases cache capacity toward recent activity. We
+ // want to adapt to changing workloads.
+ static const int64_t recencyBias = 4;
+
+ // This constant factor treats a sampled event for one old object as if it
+ // happened for many old objects. Most old objects are evicted before we can
+ // sample them, so we need to extrapolate from the ones we do sample.
+ static const int64_t oldObjectSamplingMultiplier = 32;
+
+ void pruneIfNeeded()
+ {
+ while (m_size >= m_capacity) {
+ MapType::iterator it = m_map.begin();
+ m_size -= it->key.length();
+ m_map.remove(it);
+ }
+ }
+
MapType m_map;
size_t m_size;
size_t m_capacity;
+ int64_t m_age;
};
// Caches top-level code such as <script>, eval(), new Function, and JSEvaluateScript().
@@ -177,8 +230,6 @@
template <class UnlinkedCodeBlockType, class ExecutableType>
UnlinkedCodeBlockType* getCodeBlock(JSGlobalData&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
- enum { CacheSize = 16000000 }; // Size in characters
-
CodeCacheMap m_sourceCode;
};