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
-~----------~----~----~----~------~----~------~--~---

Reply via email to