Commit 42a2b4ed authored by vitalyr@chromium.org's avatar vitalyr@chromium.org

Improve fast to slow elements conversion:

o Use a more strict limit for old arrays.

o Initial capacity of a slow elements dictionary should be the number
  of used elements and not the old array capacity.

R=danno@chromium.org

Review URL: http://codereview.chromium.org/7464032

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8744 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 6c58013b
...@@ -1966,6 +1966,17 @@ void DescriptorArray::Swap(int first, int second) { ...@@ -1966,6 +1966,17 @@ void DescriptorArray::Swap(int first, int second) {
} }
template<typename Shape, typename Key>
int HashTable<Shape, Key>::ComputeCapacity(int at_least_space_for) {
const int kMinCapacity = 32;
int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
if (capacity < kMinCapacity) {
capacity = kMinCapacity; // Guarantee min capacity.
}
return capacity;
}
template<typename Shape, typename Key> template<typename Shape, typename Key>
int HashTable<Shape, Key>::FindEntry(Key key) { int HashTable<Shape, Key>::FindEntry(Key key) {
return FindEntry(GetIsolate(), key); return FindEntry(GetIsolate(), key);
......
...@@ -2899,9 +2899,12 @@ MaybeObject* JSObject::NormalizeElements() { ...@@ -2899,9 +2899,12 @@ MaybeObject* JSObject::NormalizeElements() {
int length = IsJSArray() int length = IsJSArray()
? Smi::cast(JSArray::cast(this)->length())->value() ? Smi::cast(JSArray::cast(this)->length())->value()
: array->length(); : array->length();
int old_capacity = 0;
int used_elements = 0;
GetElementsCapacityAndUsage(&old_capacity, &used_elements);
NumberDictionary* dictionary = NULL; NumberDictionary* dictionary = NULL;
{ Object* object; { Object* object;
MaybeObject* maybe = NumberDictionary::Allocate(length); MaybeObject* maybe = NumberDictionary::Allocate(used_elements);
if (!maybe->ToObject(&object)) return maybe; if (!maybe->ToObject(&object)) return maybe;
dictionary = NumberDictionary::cast(object); dictionary = NumberDictionary::cast(object);
} }
...@@ -8618,7 +8621,7 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, ...@@ -8618,7 +8621,7 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index,
} else { } else {
new_length = dictionary->max_number_key() + 1; new_length = dictionary->max_number_key() + 1;
} }
MaybeObject* result = ShouldConvertToFastDoubleElements() MaybeObject* result = CanConvertToFastDoubleElements()
? SetFastDoubleElementsCapacityAndLength(new_length, new_length) ? SetFastDoubleElementsCapacityAndLength(new_length, new_length)
: SetFastElementsCapacityAndLength(new_length, new_length); : SetFastElementsCapacityAndLength(new_length, new_length);
if (result->IsFailure()) return result; if (result->IsFailure()) return result;
...@@ -9160,7 +9163,15 @@ MaybeObject* JSObject::GetExternalElement(uint32_t index) { ...@@ -9160,7 +9163,15 @@ MaybeObject* JSObject::GetExternalElement(uint32_t index) {
bool JSObject::HasDenseElements() { bool JSObject::HasDenseElements() {
int capacity = 0; int capacity = 0;
int number_of_elements = 0; int used = 0;
GetElementsCapacityAndUsage(&capacity, &used);
return (capacity == 0) || (used > (capacity / 2));
}
void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
*capacity = 0;
*used = 0;
FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements()); FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements());
FixedArray* backing_store = NULL; FixedArray* backing_store = NULL;
...@@ -9171,34 +9182,33 @@ bool JSObject::HasDenseElements() { ...@@ -9171,34 +9182,33 @@ bool JSObject::HasDenseElements() {
backing_store = FixedArray::cast(backing_store_base); backing_store = FixedArray::cast(backing_store_base);
if (backing_store->IsDictionary()) { if (backing_store->IsDictionary()) {
NumberDictionary* dictionary = NumberDictionary::cast(backing_store); NumberDictionary* dictionary = NumberDictionary::cast(backing_store);
capacity = dictionary->Capacity(); *capacity = dictionary->Capacity();
number_of_elements = dictionary->NumberOfElements(); *used = dictionary->NumberOfElements();
break; break;
} }
// Fall through. // Fall through.
case FAST_ELEMENTS: case FAST_ELEMENTS:
backing_store = FixedArray::cast(backing_store_base); backing_store = FixedArray::cast(backing_store_base);
capacity = backing_store->length(); *capacity = backing_store->length();
for (int i = 0; i < capacity; ++i) { for (int i = 0; i < *capacity; ++i) {
if (!backing_store->get(i)->IsTheHole()) ++number_of_elements; if (!backing_store->get(i)->IsTheHole()) ++(*used);
} }
break; break;
case DICTIONARY_ELEMENTS: { case DICTIONARY_ELEMENTS: {
NumberDictionary* dictionary = NumberDictionary* dictionary =
NumberDictionary::cast(FixedArray::cast(elements())); NumberDictionary::cast(FixedArray::cast(elements()));
capacity = dictionary->Capacity(); *capacity = dictionary->Capacity();
number_of_elements = dictionary->NumberOfElements(); *used = dictionary->NumberOfElements();
break; break;
} }
case FAST_DOUBLE_ELEMENTS: { case FAST_DOUBLE_ELEMENTS: {
FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
capacity = elms->length(); *capacity = elms->length();
for (int i = 0; i < capacity; i++) { for (int i = 0; i < *capacity; i++) {
if (!elms->is_the_hole(i)) number_of_elements++; if (!elms->is_the_hole(i)) ++(*used);
} }
break; break;
} }
case EXTERNAL_PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
case EXTERNAL_SHORT_ELEMENTS: case EXTERNAL_SHORT_ELEMENTS:
...@@ -9206,31 +9216,34 @@ bool JSObject::HasDenseElements() { ...@@ -9206,31 +9216,34 @@ bool JSObject::HasDenseElements() {
case EXTERNAL_INT_ELEMENTS: case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS: { case EXTERNAL_DOUBLE_ELEMENTS:
return true; case EXTERNAL_PIXEL_ELEMENTS:
} // External arrays are considered 100% used.
ExternalArray* external_array = ExternalArray::cast(elements());
*capacity = external_array->length();
*used = external_array->length();
break;
} }
return (capacity == 0) || (number_of_elements > (capacity / 2));
} }
bool JSObject::ShouldConvertToSlowElements(int new_capacity) { bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
if (new_capacity <= kMaxFastElementsLength) return false; STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
// Keep the array in fast case if the current backing storage is kMaxUncheckedFastElementsLength);
// almost filled and if the new capacity is no more than twice the if (new_capacity <= kMaxUncheckedOldFastElementsLength ||
// old capacity. (new_capacity <= kMaxUncheckedFastElementsLength &&
int old_capacity = 0; GetHeap()->InNewSpace(this))) {
if (elements()->map() == GetHeap()->non_strict_arguments_elements_map()) { return false;
FixedArray* backing_store = FixedArray::cast(elements());
old_capacity = FixedArray::cast(backing_store->get(1))->length();
} else if (HasFastElements()) {
old_capacity = FixedArray::cast(elements())->length();
} else if (HasFastDoubleElements()) {
old_capacity = FixedDoubleArray::cast(elements())->length();
} else {
UNREACHABLE();
} }
return !HasDenseElements() || ((new_capacity / 2) > old_capacity); // If the fast-case backing storage takes up roughly three times as
// much space (in machine words) as a dictionary backing storage
// would, the object should have slow elements.
int old_capacity = 0;
int used_elements = 0;
GetElementsCapacityAndUsage(&old_capacity, &used_elements);
int dictionary_size = NumberDictionary::ComputeCapacity(used_elements) *
NumberDictionary::kEntrySize;
return 3 * dictionary_size <= new_capacity;
} }
...@@ -9253,20 +9266,21 @@ bool JSObject::ShouldConvertToFastElements() { ...@@ -9253,20 +9266,21 @@ bool JSObject::ShouldConvertToFastElements() {
// dictionary, we cannot go back to fast case. // dictionary, we cannot go back to fast case.
if (dictionary->requires_slow_elements()) return false; if (dictionary->requires_slow_elements()) return false;
// If the dictionary backing storage takes up roughly half as much // If the dictionary backing storage takes up roughly half as much
// space as a fast-case backing storage would the array should have // space (in machine words) as a fast-case backing storage would,
// fast elements. // the object should have fast elements.
uint32_t length = 0; uint32_t array_size = 0;
if (IsJSArray()) { if (IsJSArray()) {
CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size));
} else { } else {
length = dictionary->max_number_key(); array_size = dictionary->max_number_key();
} }
return static_cast<uint32_t>(dictionary->Capacity()) >= uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
(length / (2 * NumberDictionary::kEntrySize)); NumberDictionary::kEntrySize;
return 2 * dictionary_size >= array_size;
} }
bool JSObject::ShouldConvertToFastDoubleElements() { bool JSObject::CanConvertToFastDoubleElements() {
if (FLAG_unbox_double_arrays) { if (FLAG_unbox_double_arrays) {
ASSERT(HasDictionaryElements()); ASSERT(HasDictionaryElements());
NumberDictionary* dictionary = NumberDictionary::cast(elements()); NumberDictionary* dictionary = NumberDictionary::cast(elements());
...@@ -10197,11 +10211,8 @@ void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) { ...@@ -10197,11 +10211,8 @@ void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) {
template<typename Shape, typename Key> template<typename Shape, typename Key>
MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for, MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for,
PretenureFlag pretenure) { PretenureFlag pretenure) {
const int kMinCapacity = 32; int capacity = ComputeCapacity(at_least_space_for);
int capacity = RoundUpToPowerOf2(at_least_space_for * 2); if (capacity > HashTable::kMaxCapacity) {
if (capacity < kMinCapacity) {
capacity = kMinCapacity; // Guarantee min capacity.
} else if (capacity > HashTable::kMaxCapacity) {
return Failure::OutOfMemoryException(); return Failure::OutOfMemoryException();
} }
......
...@@ -1654,7 +1654,7 @@ class JSObject: public JSReceiver { ...@@ -1654,7 +1654,7 @@ class JSObject: public JSReceiver {
bool ShouldConvertToFastElements(); bool ShouldConvertToFastElements();
// Returns true if the elements of JSObject contains only values that can be // Returns true if the elements of JSObject contains only values that can be
// represented in a FixedDoubleArray. // represented in a FixedDoubleArray.
bool ShouldConvertToFastDoubleElements(); bool CanConvertToFastDoubleElements();
// Tells whether the index'th element is present. // Tells whether the index'th element is present.
inline bool HasElement(uint32_t index); inline bool HasElement(uint32_t index);
...@@ -1948,8 +1948,21 @@ class JSObject: public JSReceiver { ...@@ -1948,8 +1948,21 @@ class JSObject: public JSReceiver {
// Also maximal value of JSArray's length property. // Also maximal value of JSArray's length property.
static const uint32_t kMaxElementCount = 0xffffffffu; static const uint32_t kMaxElementCount = 0xffffffffu;
// Constants for heuristics controlling conversion of fast elements
// to slow elements.
// Maximal gap that can be introduced by adding an element beyond
// the current elements length.
static const uint32_t kMaxGap = 1024; static const uint32_t kMaxGap = 1024;
static const int kMaxFastElementsLength = 5000;
// Maximal length of fast elements array that won't be checked for
// being dense enough on expansion.
static const int kMaxUncheckedFastElementsLength = 5000;
// Same as above but for old arrays. This limit is more strict. We
// don't want to be wasteful with long lived objects.
static const int kMaxUncheckedOldFastElementsLength = 500;
static const int kInitialMaxFastElementArray = 100000; static const int kInitialMaxFastElementArray = 100000;
static const int kMaxFastProperties = 12; static const int kMaxFastProperties = 12;
static const int kMaxInstanceSize = 255 * kPointerSize; static const int kMaxInstanceSize = 255 * kPointerSize;
...@@ -2015,6 +2028,9 @@ class JSObject: public JSReceiver { ...@@ -2015,6 +2028,9 @@ class JSObject: public JSReceiver {
// Returns true if most of the elements backing storage is used. // Returns true if most of the elements backing storage is used.
bool HasDenseElements(); bool HasDenseElements();
// Gets the current elements capacity and the number of used elements.
void GetElementsCapacityAndUsage(int* capacity, int* used);
bool CanSetCallback(String* name); bool CanSetCallback(String* name);
MUST_USE_RESULT MaybeObject* SetElementCallback( MUST_USE_RESULT MaybeObject* SetElementCallback(
uint32_t index, uint32_t index,
...@@ -2491,6 +2507,10 @@ class HashTable: public FixedArray { ...@@ -2491,6 +2507,10 @@ class HashTable: public FixedArray {
int at_least_space_for, int at_least_space_for,
PretenureFlag pretenure = NOT_TENURED); PretenureFlag pretenure = NOT_TENURED);
// Computes the required capacity for a table holding the given
// number of elements. May be more than HashTable::kMaxCapacity.
static int ComputeCapacity(int at_least_space_for);
// Returns the key at entry. // Returns the key at entry.
Object* KeyAt(int entry) { return get(EntryToIndex(entry)); } Object* KeyAt(int entry) { return get(EntryToIndex(entry)); }
......
...@@ -1666,7 +1666,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) { ...@@ -1666,7 +1666,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) {
RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpConstructResult) { RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpConstructResult) {
ASSERT(args.length() == 3); ASSERT(args.length() == 3);
CONVERT_SMI_ARG_CHECKED(elements_count, 0); CONVERT_SMI_ARG_CHECKED(elements_count, 0);
if (elements_count > JSArray::kMaxFastElementsLength) { if (elements_count < 0 ||
elements_count > FixedArray::kMaxLength ||
!Smi::IsValid(elements_count)) {
return isolate->ThrowIllegalOperation(); return isolate->ThrowIllegalOperation();
} }
Object* new_object; Object* new_object;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment