Revision: 3336
Author: [email protected]
Date: Fri Nov 20 02:11:45 2009
Log: Some optimizations for packer.js.
Review URL: http://codereview.chromium.org/409007
http://code.google.com/p/v8/source/detail?r=3336
Modified:
/branches/bleeding_edge/src/heap.cc
/branches/bleeding_edge/src/heap.h
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/runtime.cc
=======================================
--- /branches/bleeding_edge/src/heap.cc Wed Nov 11 07:25:51 2009
+++ /branches/bleeding_edge/src/heap.cc Fri Nov 20 02:11:45 2009
@@ -1760,6 +1760,41 @@
share->set_this_property_assignments(undefined_value());
return result;
}
+
+
+// Returns true for a character in a range. Both limits are inclusive.
+static inline bool Between(uint32_t character, uint32_t from, uint32_t to)
{
+ // This makes uses of the the unsigned wraparound.
+ return character - from <= to - from;
+}
+
+
+static inline Object* MakeOrFindTwoCharacterString(uint32_t c1, uint32_t
c2) {
+ String* symbol;
+ // Numeric strings have a different hash algorithm not known by
+ // LookupTwoCharsSymbolIfExists, so we skip this step for such strings.
+ if ((!Between(c1, '0', '9') || !Between(c2, '0', '9')) &&
+ Heap::symbol_table()->LookupTwoCharsSymbolIfExists(c1, c2, &symbol))
{
+ return symbol;
+ // Now we know the length is 2, we might as well make use of that fact
+ // when building the new string.
+ } else if ((c1 | c2) <= String::kMaxAsciiCharCodeU) { // We can do this
+ ASSERT(IsPowerOf2(String::kMaxAsciiCharCodeU + 1)); // because of
this.
+ Object* result = Heap::AllocateRawAsciiString(2);
+ if (result->IsFailure()) return result;
+ char* dest = SeqAsciiString::cast(result)->GetChars();
+ dest[0] = c1;
+ dest[1] = c2;
+ return result;
+ } else {
+ Object* result = Heap::AllocateRawTwoByteString(2);
+ if (result->IsFailure()) return result;
+ uc16* dest = SeqTwoByteString::cast(result)->GetChars();
+ dest[0] = c1;
+ dest[1] = c2;
+ return result;
+ }
+}
Object* Heap::AllocateConsString(String* first, String* second) {
@@ -1774,6 +1809,16 @@
}
int length = first_length + second_length;
+
+ // Optimization for 2-byte strings often used as keys in a decompression
+ // dictionary. Check whether we already have the string in the symbol
+ // table to prevent creation of many unneccesary strings.
+ if (length == 2) {
+ unsigned c1 = first->Get(0);
+ unsigned c2 = second->Get(0);
+ return MakeOrFindTwoCharacterString(c1, c2);
+ }
+
bool is_ascii = first->IsAsciiRepresentation()
&& second->IsAsciiRepresentation();
@@ -1843,6 +1888,13 @@
if (length == 1) {
return Heap::LookupSingleCharacterStringFromCode(
buffer->Get(start));
+ } else if (length == 2) {
+ // Optimization for 2-byte strings often used as keys in a
decompression
+ // dictionary. Check whether we already have the string in the symbol
+ // table to prevent creation of many unneccesary strings.
+ unsigned c1 = buffer->Get(start);
+ unsigned c2 = buffer->Get(start + 1);
+ return MakeOrFindTwoCharacterString(c1, c2);
}
// Make an attempt to flatten the buffer to reduce access time.
=======================================
--- /branches/bleeding_edge/src/heap.h Tue Nov 10 05:23:05 2009
+++ /branches/bleeding_edge/src/heap.h Fri Nov 20 02:11:45 2009
@@ -631,6 +631,7 @@
}
static Object* LookupSymbol(String* str);
static bool LookupSymbolIfExists(String* str, String** symbol);
+ static bool LookupTwoCharsSymbolIfExists(String* str, String** symbol);
// Compute the matching symbol map for a string if possible.
// NULL is returned if string is in new space or not flattened.
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Thu Nov 12 08:34:52 2009
+++ /branches/bleeding_edge/src/objects-inl.h Fri Nov 20 02:11:45 2009
@@ -3103,8 +3103,19 @@
void JSArray::EnsureSize(int required_size) {
ASSERT(HasFastElements());
- if (elements()->length() >= required_size) return;
- Expand(required_size);
+ Array* elts = elements();
+ const int kArraySizeThatFitsComfortablyInNewSpace = 128;
+ if (elts->length() < required_size) {
+ // Doubling in size would be overkill, but leave some slack to avoid
+ // constantly growing.
+ Expand(required_size + (required_size >> 3));
+ // It's a performance benefit to keep a frequently used array in
new-space.
+ } else if (!Heap::new_space()->Contains(elts) &&
+ required_size < kArraySizeThatFitsComfortablyInNewSpace) {
+ // Expand will allocate a new backing store in new space even if the
size
+ // we asked for isn't larger than what we had before.
+ Expand(required_size);
+ }
}
=======================================
--- /branches/bleeding_edge/src/objects.cc Thu Nov 12 08:34:52 2009
+++ /branches/bleeding_edge/src/objects.cc Fri Nov 20 02:11:45 2009
@@ -1463,8 +1463,8 @@
Object* JSObject::ReplaceSlowProperty(String* name,
- Object* value,
- PropertyAttributes attributes) {
+ Object* value,
+ PropertyAttributes attributes) {
StringDictionary* dictionary = property_dictionary();
int old_index = dictionary->FindEntry(name);
int new_enumeration_index = 0; // 0 means "Use the next available
index."
@@ -1477,6 +1477,7 @@
PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
return SetNormalizedProperty(name, value, new_details);
}
+
Object* JSObject::ConvertDescriptorToFieldAndMapTransition(
String* name,
@@ -1868,6 +1869,14 @@
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc;
+
+ // Optimization for 2-byte strings often used as keys in a decompression
+ // dictionary. We make these short keys into symbols to avoid constantly
+ // reallocating them.
+ if (!name->IsSymbol() && name->length() <= 2) {
+ Object* symbol_version = Heap::LookupSymbol(name);
+ if (!symbol_version->IsFailure()) name = String::cast(symbol_version);
+ }
// Check access rights if needed.
if (IsAccessCheckNeeded()
@@ -5240,9 +5249,7 @@
Handle<JSArray> self(this);
Handle<FixedArray> old_backing(FixedArray::cast(elements()));
int old_size = old_backing->length();
- // Doubling in size would be overkill, but leave some slack to avoid
- // constantly growing.
- int new_size = required_size + (required_size >> 3);
+ int new_size = required_size > old_size ? required_size : old_size;
Handle<FixedArray> new_backing = Factory::NewFixedArray(new_size);
// Can't use this any more now because we may have had a GC!
for (int i = 0; i < old_size; i++) new_backing->set(i,
old_backing->get(i));
@@ -7325,6 +7332,67 @@
SymbolKey key(string);
return LookupKey(&key, s);
}
+
+
+// This class is used for looking up two character strings in the symbol
table.
+// If we don't have a hit we don't want to waste much time so we unroll the
+// string hash calculation loop here for speed. Doesn't work if the two
+// characters form a decimal integer, since such strings have a different
hash
+// algorithm.
+class TwoCharHashTableKey : public HashTableKey {
+ public:
+ TwoCharHashTableKey(uint32_t c1, uint32_t c2)
+ : c1_(c1), c2_(c2) {
+ // Char 1.
+ uint32_t hash = c1 + (c1 << 10);
+ hash ^= hash >> 6;
+ // Char 2.
+ hash += c2;
+ hash += hash << 10;
+ hash ^= hash >> 6;
+ // GetHash.
+ hash += hash << 3;
+ hash ^= hash >> 11;
+ hash += hash << 15;
+ if (hash == 0) hash = 27;
+#ifdef DEBUG
+ StringHasher hasher(2);
+ hasher.AddCharacter(c1);
+ hasher.AddCharacter(c2);
+ // If this assert fails then we failed to reproduce the two-character
+ // version of the string hashing algorithm above. One reason could be
+ // that we were passed two digits as characters, since the hash
+ // algorithm is different in that case.
+ ASSERT_EQ(static_cast<int>(hasher.GetHash()), static_cast<int>(hash));
+#endif
+ hash_ = hash;
+ }
+
+ bool IsMatch(Object* o) {
+ if (!o->IsString()) return false;
+ String* other = String::cast(o);
+ if (other->length() != 2) return false;
+ if (other->Get(0) != c1_) return false;
+ return other->Get(1) == c2_;
+ }
+
+ uint32_t Hash() { return hash_; }
+ uint32_t HashForObject(Object* key) {
+ if (!key->IsString()) return 0;
+ return String::cast(key)->Hash();
+ }
+
+ Object* AsObject() {
+ // The TwoCharHashTableKey is only used for looking in the symbol
+ // table, not for adding to it.
+ UNREACHABLE();
+ return NULL;
+ }
+ private:
+ uint32_t c1_;
+ uint32_t c2_;
+ uint32_t hash_;
+};
bool SymbolTable::LookupSymbolIfExists(String* string, String** symbol) {
@@ -7339,6 +7407,22 @@
return true;
}
}
+
+
+bool SymbolTable::LookupTwoCharsSymbolIfExists(uint32_t c1,
+ uint32_t c2,
+ String** symbol) {
+ TwoCharHashTableKey key(c1, c2);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) {
+ return false;
+ } else {
+ String* result = String::cast(KeyAt(entry));
+ ASSERT(StringShape(result).IsSymbol());
+ *symbol = result;
+ return true;
+ }
+}
Object* SymbolTable::LookupSymbol(Vector<const char> str, Object** s) {
=======================================
--- /branches/bleeding_edge/src/objects.h Thu Nov 12 08:52:48 2009
+++ /branches/bleeding_edge/src/objects.h Fri Nov 20 02:11:45 2009
@@ -2188,6 +2188,7 @@
// true if it is found, assigning the symbol to the given output
// parameter.
bool LookupSymbolIfExists(String* str, String** symbol);
+ bool LookupTwoCharsSymbolIfExists(uint32_t c1, uint32_t c2, String**
symbol);
// Casting.
static inline SymbolTable* cast(Object* obj);
@@ -3846,6 +3847,7 @@
bool is_array_index_;
bool is_first_char_;
bool is_valid_;
+ friend class TwoCharHashTableKey;
};
=======================================
--- /branches/bleeding_edge/src/runtime.cc Wed Nov 18 10:48:04 2009
+++ /branches/bleeding_edge/src/runtime.cc Fri Nov 20 02:11:45 2009
@@ -2356,12 +2356,20 @@
ASSERT(args.length() == 3);
CONVERT_CHECKED(String, value, args[0]);
- CONVERT_DOUBLE_CHECKED(from_number, args[1]);
- CONVERT_DOUBLE_CHECKED(to_number, args[2]);
-
- int start = FastD2I(from_number);
- int end = FastD2I(to_number);
-
+ Object* from = args[1];
+ Object* to = args[2];
+ int start, end;
+ // We have a fast integer-only case here to avoid a conversion to double
in
+ // the common case where from and to are Smis.
+ if (from->IsSmi() && to->IsSmi()) {
+ start = Smi::cast(from)->value();
+ end = Smi::cast(to)->value();
+ } else {
+ CONVERT_DOUBLE_CHECKED(from_number, from);
+ CONVERT_DOUBLE_CHECKED(to_number, to);
+ start = FastD2I(from_number);
+ end = FastD2I(to_number);
+ }
RUNTIME_ASSERT(end >= start);
RUNTIME_ASSERT(start >= 0);
RUNTIME_ASSERT(end <= value->length());
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---