Reviewers: Mads Ager,

Description:
Don't externalize fresh strings.

With the current API the embedder has to extrenalize a string each
time a string is encountered to avoid the cost of repeated character
copying/conversion. The issue here is that the externalization cost
itself is non-negligible (both in time and space) and should not be
paid for a rarely used string. This change is an attempt to predict a
string's usage frequency based on its freshness. A string is
considered fresh if it was recently allocated in the new space.

Please review this at http://codereview.chromium.org/608006

Affected files:
  M src/api.cc
  M test/cctest/test-api.cc


Index: src/api.cc
diff --git a/src/api.cc b/src/api.cc
index bd5fdd81dc5274c98e4ad6795957f5c06f1c1d0b..e1fedbe21817df0b62e3a78913337a8b2b36f3ee 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -3130,12 +3130,24 @@ Local<String> v8::String::NewExternal(
 }


+static const int kFreshnessLimit = 1024;
+
+// Determines whether a string is fresh, that is whether it's
+// sufficiently close to the new space top.
+static inline bool IsFreshString(i::Handle<i::String> string) {
+  i::Address address = reinterpret_cast<i::Address>(*string);
+  i::Address top = i::Heap::NewSpaceTop();
+  return top - kFreshnessLimit <= address && address <= top;
+}
+
+
 bool v8::String::MakeExternal(
     v8::String::ExternalAsciiStringResource* resource) {
   if (IsDeadCheck("v8::String::MakeExternal()")) return false;
   if (this->IsExternal()) return false;  // Already an external string.
   ENTER_V8;
   i::Handle<i::String> obj = Utils::OpenHandle(this);
+  if (IsFreshString(obj)) return false;
   bool result = obj->MakeExternal(resource);
   if (result && !obj->IsSymbol()) {
     i::ExternalStringTable::AddString(*obj);
@@ -3147,6 +3159,7 @@ bool v8::String::MakeExternal(
 bool v8::String::CanMakeExternal() {
   if (IsDeadCheck("v8::String::CanMakeExternal()")) return false;
   i::Handle<i::String> obj = Utils::OpenHandle(this);
+  if (IsFreshString(obj)) return false;
   int size = obj->Size();  // Byte size of the original string.
   if (size < i::ExternalString::kSize)
     return false;
Index: test/cctest/test-api.cc
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 4e617be41fadc183179a1b881f494121d1782a60..41fb02caf1b3585952c9480f4cc27a1be4ffb8c1 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -394,6 +394,9 @@ THREADED_TEST(ScriptMakingExternalString) {
     v8::HandleScope scope;
     LocalContext env;
     Local<String> source = String::New(two_byte_source);
+    // Trigger GCs so that the newly allocated string moves to old gen.
+    i::Heap::CollectGarbage(0, i::NEW_SPACE);  // in survivor space now
+    i::Heap::CollectGarbage(0, i::NEW_SPACE);  // in old gen now
     bool success = source->MakeExternal(new TestResource(two_byte_source));
     CHECK(success);
     Local<Script> script = Script::Compile(source);
@@ -416,6 +419,9 @@ THREADED_TEST(ScriptMakingExternalAsciiString) {
     v8::HandleScope scope;
     LocalContext env;
     Local<String> source = v8_str(c_source);
+    // Trigger GCs so that the newly allocated string moves to old gen.
+    i::Heap::CollectGarbage(0, i::NEW_SPACE);  // in survivor space now
+    i::Heap::CollectGarbage(0, i::NEW_SPACE);  // in old gen now
     bool success = source->MakeExternal(
         new TestAsciiResource(i::StrDup(c_source)));
     CHECK(success);


--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to