Optimize the common obfuscator pattern where ["foo","bar","baz"]
authorerik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 7 Sep 2011 11:28:48 +0000 (11:28 +0000)
committererik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 7 Sep 2011 11:28:48 +0000 (11:28 +0000)
gets converted fo "foo,bar,baz".split(",").  If the inputs are
symbols we cache the result and make the substrings into symbols.
Review URL: http://codereview.chromium.org/7782025

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9164 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/heap.cc
src/heap.h
src/runtime.cc

index 526fea0..a2f0420 100644 (file)
@@ -842,6 +842,7 @@ void Heap::MarkCompactPrologue(bool is_compacting) {
   isolate_->keyed_lookup_cache()->Clear();
   isolate_->context_slot_cache()->Clear();
   isolate_->descriptor_lookup_cache()->Clear();
+  StringSplitCache::Clear(string_split_cache());
 
   isolate_->compilation_cache()->MarkCompactPrologue();
 
@@ -2223,6 +2224,13 @@ bool Heap::CreateInitialObjects() {
   }
   set_single_character_string_cache(FixedArray::cast(obj));
 
+  // Allocate cache for string split.
+  { MaybeObject* maybe_obj =
+        AllocateFixedArray(StringSplitCache::kStringSplitCacheSize, TENURED);
+    if (!maybe_obj->ToObject(&obj)) return false;
+  }
+  set_string_split_cache(FixedArray::cast(obj));
+
   // Allocate cache for external strings pointing to native source code.
   { MaybeObject* maybe_obj = AllocateFixedArray(Natives::GetBuiltinsCount());
     if (!maybe_obj->ToObject(&obj)) return false;
@@ -2248,6 +2256,75 @@ bool Heap::CreateInitialObjects() {
 }
 
 
+Object* StringSplitCache::Lookup(
+    FixedArray* cache, String* string, String* pattern) {
+  if (!string->IsSymbol() || !pattern->IsSymbol()) return Smi::FromInt(0);
+  uintptr_t hash = string->Hash();
+  uintptr_t index = ((hash & (kStringSplitCacheSize - 1)) &
+      ~(kArrayEntriesPerCacheEntry - 1));
+  if (cache->get(index + kStringOffset) == string &&
+      cache->get(index + kPatternOffset) == pattern) {
+    return cache->get(index + kArrayOffset);
+  }
+  index = ((index + kArrayEntriesPerCacheEntry) & (kStringSplitCacheSize - 1));
+  if (cache->get(index + kStringOffset) == string &&
+      cache->get(index + kPatternOffset) == pattern) {
+    return cache->get(index + kArrayOffset);
+  }
+  return Smi::FromInt(0);
+}
+
+void StringSplitCache::Enter(Heap* heap,
+                             FixedArray* cache,
+                             String* string,
+                             String* pattern,
+                             FixedArray* array) {
+  if (!string->IsSymbol() || !pattern->IsSymbol()) return;
+  uintptr_t hash = string->Hash();
+  array->set_map(heap->fixed_cow_array_map());
+  uintptr_t index = ((hash & (kStringSplitCacheSize - 1)) &
+      ~(kArrayEntriesPerCacheEntry - 1));
+  if (cache->get(index + kStringOffset) == Smi::FromInt(0)) {
+    cache->set(index + kStringOffset, string);
+    cache->set(index + kPatternOffset, pattern);
+    cache->set(index + kArrayOffset, array);
+    return;
+  }
+  uintptr_t index2 =
+      ((index + kArrayEntriesPerCacheEntry) & (kStringSplitCacheSize - 1));
+  if (cache->get(index2 + kStringOffset) == Smi::FromInt(0)) {
+    cache->set(index2 + kStringOffset, string);
+    cache->set(index2 + kPatternOffset, pattern);
+    cache->set(index2 + kArrayOffset, array);
+    return;
+  }
+  cache->set(index2 + kStringOffset, Smi::FromInt(0));
+  cache->set(index2 + kPatternOffset, Smi::FromInt(0));
+  cache->set(index2 + kArrayOffset, Smi::FromInt(0));
+  cache->set(index + kStringOffset, string);
+  cache->set(index + kPatternOffset, pattern);
+  cache->set(index + kArrayOffset, array);
+  if (array->length() < 100) {  // Limit how many new symbols we want to make.
+    for (int i = 0; i < array->length(); i++) {
+      String* str = String::cast(array->get(i));
+      Object* symbol;
+      MaybeObject* maybe_symbol = heap->LookupSymbol(str);
+      if (maybe_symbol->ToObject(&symbol)) {
+        array->set(i, symbol);
+      }
+    }
+  }
+}
+
+
+void StringSplitCache::Clear(FixedArray* cache) {
+  for (int i = 0; i < kStringSplitCacheSize; i++) {
+    cache->set(i, Smi::FromInt(0));
+  }
+}
+
+
 MaybeObject* Heap::InitializeNumberStringCache() {
   // Compute the size of the number string cache based on the max heap size.
   // max_semispace_size_ == 512 KB => number_string_cache_size = 32.
index 8eb42d3..cc689df 100644 (file)
@@ -77,6 +77,7 @@ inline Heap* _inline_get_heap_();
   V(Object, instanceof_cache_map, InstanceofCacheMap)                          \
   V(Object, instanceof_cache_answer, InstanceofCacheAnswer)                    \
   V(FixedArray, single_character_string_cache, SingleCharacterStringCache)     \
+  V(FixedArray, string_split_cache, StringSplitCache)                          \
   V(Object, termination_exception, TerminationException)                       \
   V(FixedArray, empty_fixed_array, EmptyFixedArray)                            \
   V(ByteArray, empty_byte_array, EmptyByteArray)                               \
@@ -2176,6 +2177,27 @@ class GCTracer BASE_EMBEDDED {
 };
 
 
+class StringSplitCache {
+ public:
+  static Object* Lookup(FixedArray* cache, String* string, String* pattern);
+  static void Enter(Heap* heap,
+                    FixedArray* cache,
+                    String* string,
+                    String* pattern,
+                    FixedArray* array);
+  static void Clear(FixedArray* cache);
+  static const int kStringSplitCacheSize = 0x100;
+
+ private:
+  static const int kArrayEntriesPerCacheEntry = 4;
+  static const int kStringOffset = 0;
+  static const int kPatternOffset = 1;
+  static const int kArrayOffset = 2;
+
+  static MaybeObject* WrapFixedArrayInJSArray(Object* fixed_array);
+};
+
+
 class TranscendentalCache {
  public:
   enum Type {ACOS, ASIN, ATAN, COS, EXP, LOG, SIN, TAN, kNumberOfCaches};
index e894bf2..3e07b99 100644 (file)
@@ -5984,6 +5984,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) {
   int pattern_length = pattern->length();
   RUNTIME_ASSERT(pattern_length > 0);
 
+  if (limit == 0xffffffffu) {
+    Handle<Object> cached_answer(StringSplitCache::Lookup(
+        isolate->heap()->string_split_cache(),
+        *subject,
+        *pattern));
+    if (*cached_answer != Smi::FromInt(0)) {
+      Handle<JSArray> result =
+          isolate->factory()->NewJSArrayWithElements(
+              Handle<FixedArray>::cast(cached_answer));
+      return *result;
+    }
+  }
+
   // The limit can be very large (0xffffffffu), but since the pattern
   // isn't empty, we can never create more parts than ~half the length
   // of the subject.
@@ -6077,6 +6090,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) {
     part_start = part_end + pattern_length;
   }
 
+  if (limit == 0xffffffffu) {
+    StringSplitCache::Enter(isolate->heap(),
+                            isolate->heap()->string_split_cache(),
+                            *subject,
+                            *pattern,
+                            *elements);
+  }
+
   return *result;
 }