From 260d65d58477f005e6a3b1facaf623b34b9b9f6f Mon Sep 17 00:00:00 2001 From: "erik.corry@gmail.com" Date: Wed, 7 Sep 2011 11:28:48 +0000 Subject: [PATCH] Optimize the common obfuscator pattern where ["foo","bar","baz"] 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 | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/heap.h | 22 +++++++++++++++ src/runtime.cc | 21 ++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/src/heap.cc b/src/heap.cc index 526fea04b3..a2f0420cf3 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -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. diff --git a/src/heap.h b/src/heap.h index 8eb42d3a31..cc689df17b 100644 --- a/src/heap.h +++ b/src/heap.h @@ -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}; diff --git a/src/runtime.cc b/src/runtime.cc index e894bf242c..3e07b99823 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -5984,6 +5984,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { int pattern_length = pattern->length(); RUNTIME_ASSERT(pattern_length > 0); + if (limit == 0xffffffffu) { + Handle cached_answer(StringSplitCache::Lookup( + isolate->heap()->string_split_cache(), + *subject, + *pattern)); + if (*cached_answer != Smi::FromInt(0)) { + Handle result = + isolate->factory()->NewJSArrayWithElements( + Handle::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; }