Author: [email protected]
Date: Thu May 14 23:45:50 2009
New Revision: 1955
Modified:
branches/bleeding_edge/src/compilation-cache.cc
branches/bleeding_edge/src/compilation-cache.h
branches/bleeding_edge/test/cctest/test-api.cc
branches/bleeding_edge/test/cctest/test-debug.cc
Log:
Add multiple generations (5) to the script compilation cache
to allow scripts that are used alot to survive a number of GCs
in the compilation cache.
Review URL: http://codereview.chromium.org/113445
Modified: branches/bleeding_edge/src/compilation-cache.cc
==============================================================================
--- branches/bleeding_edge/src/compilation-cache.cc (original)
+++ branches/bleeding_edge/src/compilation-cache.cc Thu May 14 23:45:50 2009
@@ -32,12 +32,20 @@
namespace v8 { namespace internal {
enum {
- NUMBER_OF_ENTRY_KINDS = CompilationCache::LAST_ENTRY + 1
+ // The number of script generations tell how many GCs a script can
+ // survive in the compilation cache, before it will be flushed if it
+ // hasn't been used.
+ NUMBER_OF_SCRIPT_GENERATIONS = 5,
+
+ // The compilation cache consists of tables - one for each entry
+ // kind plus extras for the script generations.
+ NUMBER_OF_TABLE_ENTRIES =
+ CompilationCache::LAST_ENTRY + NUMBER_OF_SCRIPT_GENERATIONS
};
// Keep separate tables for the different entry kinds.
-static Object* tables[NUMBER_OF_ENTRY_KINDS] = { 0, };
+static Object* tables[NUMBER_OF_TABLE_ENTRIES] = { 0, };
static Handle<CompilationCacheTable> AllocateTable(int size) {
@@ -121,41 +129,52 @@
}
-static Handle<JSFunction> Lookup(Handle<String> source,
- CompilationCache::Entry entry) {
- // Make sure not to leak the table into the surrounding handle
- // scope. Otherwise, we risk keeping old tables around even after
- // having cleared the cache.
- Object* result;
- { HandleScope scope;
- Handle<CompilationCacheTable> table = GetTable(entry);
- result = table->Lookup(*source);
- }
- if (result->IsJSFunction()) {
- return Handle<JSFunction>(JSFunction::cast(result));
- } else {
- return Handle<JSFunction>::null();
- }
-}
-
-
-// TODO(245): Need to allow identical code from different contexts to be
-// cached. Currently the first use will be cached, but subsequent code
-// from different source / line won't.
+// TODO(245): Need to allow identical code from different contexts to
+// be cached in the same script generation. Currently the first use
+// will be cached, but subsequent code from different source / line
+// won't.
Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source,
Handle<Object> name,
int line_offset,
int column_offset) {
- Handle<JSFunction> result = Lookup(source, SCRIPT);
- if (result.is_null()) {
- Counters::compilation_cache_misses.Increment();
- } else if (HasOrigin(result, name, line_offset, column_offset)) {
+ Object* result = NULL;
+ Entry generation = SCRIPT; // First generation.
+
+ // Probe the script generation tables. Make sure not to leak handles
+ // into the caller's handle scope.
+ { HandleScope scope;
+ while (generation < SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS) {
+ Handle<CompilationCacheTable> table = GetTable(generation);
+ Handle<Object> probe(table->Lookup(*source));
+ if (probe->IsJSFunction()) {
+ Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(probe);
+ // Break when we've found a suitable boilerplate function that
+ // matches the origin.
+ if (HasOrigin(boilerplate, name, line_offset, column_offset)) {
+ result = *boilerplate;
+ break;
+ }
+ }
+ // Go to the next generation.
+ generation = static_cast<Entry>(generation + 1);
+ }
+ }
+
+ // Once outside the menacles of the handle scope, we need to recheck
+ // to see if we actually found a cached script. If so, we return a
+ // handle created in the caller's handle scope.
+ if (result != NULL) {
+ Handle<JSFunction> boilerplate(JSFunction::cast(result));
+ ASSERT(HasOrigin(boilerplate, name, line_offset, column_offset));
+ // If the script was found in a later generation, we promote it to
+ // the first generation to let it survive longer in the cache.
+ if (generation != SCRIPT) PutScript(source, boilerplate);
Counters::compilation_cache_hits.Increment();
+ return boilerplate;
} else {
- result = Handle<JSFunction>::null();
Counters::compilation_cache_misses.Increment();
+ return Handle<JSFunction>::null();
}
- return result;
}
@@ -216,14 +235,25 @@
void CompilationCache::Clear() {
- for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) {
+ for (int i = 0; i < NUMBER_OF_TABLE_ENTRIES; i++) {
tables[i] = Heap::undefined_value();
}
}
void CompilationCache::Iterate(ObjectVisitor* v) {
- v->VisitPointers(&tables[0], &tables[NUMBER_OF_ENTRY_KINDS]);
+ v->VisitPointers(&tables[0], &tables[NUMBER_OF_TABLE_ENTRIES]);
+}
+
+
+void CompilationCache::MarkCompactPrologue() {
+ ASSERT(LAST_ENTRY == SCRIPT);
+ for (int i = NUMBER_OF_TABLE_ENTRIES - 1; i > SCRIPT; i--) {
+ tables[i] = tables[i - 1];
+ }
+ for (int j = 0; j <= LAST_ENTRY; j++) {
+ tables[j] = Heap::undefined_value();
+ }
}
Modified: branches/bleeding_edge/src/compilation-cache.h
==============================================================================
--- branches/bleeding_edge/src/compilation-cache.h (original)
+++ branches/bleeding_edge/src/compilation-cache.h Thu May 14 23:45:50 2009
@@ -40,11 +40,11 @@
// scripts and evals. Internally, we use separate caches to avoid
// getting the wrong kind of entry when looking up.
enum Entry {
- SCRIPT,
EVAL_GLOBAL,
EVAL_CONTEXTUAL,
REGEXP,
- LAST_ENTRY = REGEXP
+ SCRIPT,
+ LAST_ENTRY = SCRIPT
};
// Finds the script function boilerplate for a source
@@ -93,10 +93,8 @@
// Notify the cache that a mark-sweep garbage collection is about to
// take place. This is used to retire entries from the cache to
- // avoid keeping them alive too long without using them. For now, we
- // just clear the cache but we should consider are more
- // sophisticated LRU scheme.
- static void MarkCompactPrologue() { Clear(); }
+ // avoid keeping them alive too long without using them.
+ static void MarkCompactPrologue();
};
Modified: branches/bleeding_edge/test/cctest/test-api.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-api.cc (original)
+++ branches/bleeding_edge/test/cctest/test-api.cc Thu May 14 23:45:50 2009
@@ -30,6 +30,7 @@
#include "v8.h"
#include "api.h"
+#include "compilation-cache.h"
#include "snapshot.h"
#include "platform.h"
#include "top.h"
@@ -464,6 +465,7 @@
v8::internal::Heap::CollectAllGarbage();
CHECK_EQ(0, TestResource::dispose_count);
}
+ v8::internal::CompilationCache::Clear();
v8::internal::Heap::CollectAllGarbage();
CHECK_EQ(1, TestResource::dispose_count);
}
@@ -484,6 +486,7 @@
v8::internal::Heap::CollectAllGarbage();
CHECK_EQ(0, TestAsciiResource::dispose_count);
}
+ v8::internal::CompilationCache::Clear();
v8::internal::Heap::CollectAllGarbage();
CHECK_EQ(1, TestAsciiResource::dispose_count);
}
@@ -505,6 +508,7 @@
v8::internal::Heap::CollectAllGarbage();
CHECK_EQ(0, TestResource::dispose_count);
}
+ v8::internal::CompilationCache::Clear();
v8::internal::Heap::CollectAllGarbage();
CHECK_EQ(1, TestResource::dispose_count);
}
@@ -527,6 +531,7 @@
v8::internal::Heap::CollectAllGarbage();
CHECK_EQ(0, TestAsciiResource::dispose_count);
}
+ v8::internal::CompilationCache::Clear();
v8::internal::Heap::CollectAllGarbage();
CHECK_EQ(1, TestAsciiResource::dispose_count);
}
Modified: branches/bleeding_edge/test/cctest/test-debug.cc
==============================================================================
--- branches/bleeding_edge/test/cctest/test-debug.cc (original)
+++ branches/bleeding_edge/test/cctest/test-debug.cc Thu May 14 23:45:50
2009
@@ -30,6 +30,7 @@
#include "v8.h"
#include "api.h"
+#include "compilation-cache.h"
#include "debug.h"
#include "platform.h"
#include "stub-cache.h"
@@ -1677,6 +1678,11 @@
f->Call(env->Global(), 0, NULL);
}
CHECK_EQ(5, break_point_hit_count);
+
+ // BUG(343): It should not really be necessary to clear the
+ // compilation cache here, but right now the debugger relies on the
+ // script being recompiled, not just fetched from the cache.
+ i::CompilationCache::Clear();
// Reload the script and get f again checking that the ignore survives.
v8::Script::Compile(script, &origin)->Run();
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---