Revision: 22027
Author: [email protected]
Date: Thu Jun 26 00:40:45 2014 UTC
Log: Optimize Map/Set.prototype.forEach
Instead of using an iterator result object and an entries array
(for Map) we introduce a new runtime function that uses an array
as an out param.
On the Map ForEach perf test this leads to a 2.5x performance
improvement. On the overall Map and Set tests this leads to a 18%
and 13% improvement respectively.
BUG=None
LOG=Y
[email protected]
Review URL: https://codereview.chromium.org/355663002
Patch from Erik Arvidsson <[email protected]>.
http://code.google.com/p/v8/source/detail?r=22027
Modified:
/branches/bleeding_edge/src/collection-iterator.js
/branches/bleeding_edge/src/collection.js
/branches/bleeding_edge/src/factory.cc
/branches/bleeding_edge/src/factory.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/runtime.h
/branches/bleeding_edge/test/mjsunit/runtime-gen/mapiteratornext.js
/branches/bleeding_edge/test/mjsunit/runtime-gen/setiteratornext.js
=======================================
--- /branches/bleeding_edge/src/collection-iterator.js Thu Jun 12 08:53:07
2014 UTC
+++ /branches/bleeding_edge/src/collection-iterator.js Thu Jun 26 00:40:45
2014 UTC
@@ -21,7 +21,23 @@
throw MakeTypeError('incompatible_method_receiver',
['Set Iterator.prototype.next', this]);
}
- return %SetIteratorNext(this);
+
+ var value_array = [UNDEFINED, UNDEFINED];
+ var entry = {value: value_array, done: false};
+ switch (%SetIteratorNext(this, value_array)) {
+ case 0:
+ entry.value = UNDEFINED;
+ entry.done = true;
+ break;
+ case ITERATOR_KIND_VALUES:
+ entry.value = value_array[0];
+ break;
+ case ITERATOR_KIND_ENTRIES:
+ value_array[1] = value_array[0];
+ break;
+ }
+
+ return entry;
}
@@ -97,7 +113,24 @@
throw MakeTypeError('incompatible_method_receiver',
['Map Iterator.prototype.next', this]);
}
- return %MapIteratorNext(this);
+
+ var value_array = [UNDEFINED, UNDEFINED];
+ var entry = {value: value_array, done: false};
+ switch (%MapIteratorNext(this, value_array)) {
+ case 0:
+ entry.value = UNDEFINED;
+ entry.done = true;
+ break;
+ case ITERATOR_KIND_KEYS:
+ entry.value = value_array[0];
+ break;
+ case ITERATOR_KIND_VALUES:
+ entry.value = value_array[1];
+ break;
+ // ITERATOR_KIND_ENTRIES does not need any processing.
+ }
+
+ return entry;
}
=======================================
--- /branches/bleeding_edge/src/collection.js Mon Jun 23 18:05:57 2014 UTC
+++ /branches/bleeding_edge/src/collection.js Thu Jun 26 00:40:45 2014 UTC
@@ -129,11 +129,13 @@
}
var iterator = new SetIterator(this, ITERATOR_KIND_VALUES);
- var entry;
+ var key;
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
- while (!(entry = %SetIteratorNext(iterator)).done) {
+ var value_array = [UNDEFINED];
+ while (%SetIteratorNext(iterator, value_array)) {
if (stepping) %DebugPrepareStepInIfStepping(f);
- %_CallFunction(receiver, entry.value, entry.value, this, f);
+ key = value_array[0];
+ %_CallFunction(receiver, key, key, this, f);
}
}
@@ -264,11 +266,11 @@
}
var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES);
- var entry;
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
- while (!(entry = %MapIteratorNext(iterator)).done) {
+ var value_array = [UNDEFINED, UNDEFINED];
+ while (%MapIteratorNext(iterator, value_array)) {
if (stepping) %DebugPrepareStepInIfStepping(f);
- %_CallFunction(receiver, entry.value[1], entry.value[0], this, f);
+ %_CallFunction(receiver, value_array[1], value_array[0], this, f);
}
}
=======================================
--- /branches/bleeding_edge/src/factory.cc Tue Jun 24 14:53:48 2014 UTC
+++ /branches/bleeding_edge/src/factory.cc Thu Jun 26 00:40:45 2014 UTC
@@ -1392,18 +1392,6 @@
}
return result;
}
-
-
-Handle<JSObject> Factory::NewIteratorResultObject(Handle<Object> value,
- bool done) {
- Handle<Map> map(isolate()->native_context()->iterator_result_map());
- Handle<JSObject> result = NewJSObjectFromMap(map, NOT_TENURED, false);
- result->InObjectPropertyAtPut(
- JSGeneratorObject::kResultValuePropertyIndex, *value);
- result->InObjectPropertyAtPut(
- JSGeneratorObject::kResultDonePropertyIndex, *ToBoolean(done));
- return result;
-}
Handle<ScopeInfo> Factory::NewScopeInfo(int length) {
=======================================
--- /branches/bleeding_edge/src/factory.h Wed Jun 18 13:26:02 2014 UTC
+++ /branches/bleeding_edge/src/factory.h Thu Jun 26 00:40:45 2014 UTC
@@ -551,8 +551,6 @@
Handle<Object> NewEvalError(const char* message,
Vector< Handle<Object> > args);
- Handle<JSObject> NewIteratorResultObject(Handle<Object> value, bool
done);
-
Handle<String> NumberToString(Handle<Object> number,
bool check_number_string_cache = true);
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Wed Jun 25 12:12:21 2014 UTC
+++ /branches/bleeding_edge/src/objects-inl.h Thu Jun 26 00:40:45 2014 UTC
@@ -6962,6 +6962,36 @@
v->VisitPointers(HeapObject::RawField(obj, start_offset),
HeapObject::RawField(obj, object_size));
}
+
+
+template<class Derived, class TableType>
+Object* OrderedHashTableIterator<Derived, TableType>::CurrentKey() {
+ TableType* table(TableType::cast(this->table()));
+ int index = Smi::cast(this->index())->value();
+ Object* key = table->KeyAt(index);
+ ASSERT(!key->IsTheHole());
+ return key;
+}
+
+
+void JSSetIterator::PopulateValueArray(FixedArray* array) {
+ array->set(0, CurrentKey());
+}
+
+
+void JSMapIterator::PopulateValueArray(FixedArray* array) {
+ array->set(0, CurrentKey());
+ array->set(1, CurrentValue());
+}
+
+
+Object* JSMapIterator::CurrentValue() {
+ OrderedHashMap* table(OrderedHashMap::cast(this->table()));
+ int index = Smi::cast(this->index())->value();
+ Object* value = table->ValueAt(index);
+ ASSERT(!value->IsTheHole());
+ return value;
+}
#undef TYPE_CHECKER
=======================================
--- /branches/bleeding_edge/src/objects.cc Tue Jun 24 15:56:36 2014 UTC
+++ /branches/bleeding_edge/src/objects.cc Thu Jun 26 00:40:45 2014 UTC
@@ -16245,50 +16245,17 @@
table->set(index + kValueOffset, *value);
return table;
}
-
-
-template<class Derived, class TableType>
-Handle<JSObject> OrderedHashTableIterator<Derived, TableType>::Next(
- Handle<Derived> iterator) {
- Isolate* isolate = iterator->GetIsolate();
- Factory* factory = isolate->factory();
-
- Handle<Object> maybe_table(iterator->table(), isolate);
- if (!maybe_table->IsUndefined()) {
- iterator->Transition();
-
- Handle<TableType> table(TableType::cast(iterator->table()), isolate);
- int index = Smi::cast(iterator->index())->value();
- int used_capacity = table->UsedCapacity();
-
- while (index < used_capacity && table->KeyAt(index)->IsTheHole()) {
- index++;
- }
-
- if (index < used_capacity) {
- int entry_index = table->EntryToIndex(index);
- Handle<Object> value =
- Derived::ValueForKind(iterator, entry_index);
- iterator->set_index(Smi::FromInt(index + 1));
- return factory->NewIteratorResultObject(value, false);
- }
-
- iterator->set_table(iterator->GetHeap()->undefined_value());
- }
-
- return factory->NewIteratorResultObject(factory->undefined_value(),
true);
-}
template<class Derived, class TableType>
void OrderedHashTableIterator<Derived, TableType>::Transition() {
- Isolate* isolate = GetIsolate();
- Handle<TableType> table(TableType::cast(this->table()), isolate);
+ DisallowHeapAllocation no_allocation;
+ TableType* table = TableType::cast(this->table());
if (!table->IsObsolete()) return;
int index = Smi::cast(this->index())->value();
while (table->IsObsolete()) {
- Handle<TableType> next_table(table->NextTable(), isolate);
+ TableType* next_table = table->NextTable();
if (index > 0) {
int nod = table->NumberOfDeletedElements();
@@ -16309,82 +16276,80 @@
table = next_table;
}
- set_table(*table);
+ set_table(table);
set_index(Smi::FromInt(index));
}
-template Handle<JSObject>
-OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Next(
- Handle<JSSetIterator> iterator);
+template<class Derived, class TableType>
+bool OrderedHashTableIterator<Derived, TableType>::HasMore() {
+ DisallowHeapAllocation no_allocation;
+ if (this->table()->IsUndefined()) return false;
-template void
-OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Transition();
+ Transition();
+ TableType* table = TableType::cast(this->table());
+ int index = Smi::cast(this->index())->value();
+ int used_capacity = table->UsedCapacity();
-template Handle<JSObject>
-OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Next(
- Handle<JSMapIterator> iterator);
+ while (index < used_capacity && table->KeyAt(index)->IsTheHole()) {
+ index++;
+ }
-template void
-OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Transition();
+ set_index(Smi::FromInt(index));
+ if (index < used_capacity) return true;
-Handle<Object> JSSetIterator::ValueForKind(
- Handle<JSSetIterator> iterator, int entry_index) {
- int kind = iterator->kind()->value();
- // Set.prototype only has values and entries.
- ASSERT(kind == kKindValues || kind == kKindEntries);
+ set_table(GetHeap()->undefined_value());
+ return false;
+}
+
+
+template<class Derived, class TableType>
+Smi* OrderedHashTableIterator<Derived, TableType>::Next(JSArray*
value_array) {
+ DisallowHeapAllocation no_allocation;
+ if (HasMore()) {
+ FixedArray* array = FixedArray::cast(value_array->elements());
+ static_cast<Derived*>(this)->PopulateValueArray(array);
+ MoveNext();
+ return kind();
+ }
+ return Smi::FromInt(0);
+}
- Isolate* isolate = iterator->GetIsolate();
- Factory* factory = isolate->factory();
- Handle<OrderedHashSet> table(
- OrderedHashSet::cast(iterator->table()), isolate);
- Handle<Object> value = Handle<Object>(table->get(entry_index), isolate);
+template Smi*
+OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Next(
+ JSArray* value_array);
- if (kind == kKindEntries) {
- Handle<FixedArray> array = factory->NewFixedArray(2);
- array->set(0, *value);
- array->set(1, *value);
- return factory->NewJSArrayWithElements(array);
- }
+template bool
+OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::HasMore();
- return value;
-}
+template void
+OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::MoveNext();
+template Object*
+OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::CurrentKey();
-Handle<Object> JSMapIterator::ValueForKind(
- Handle<JSMapIterator> iterator, int entry_index) {
- int kind = iterator->kind()->value();
- ASSERT(kind == kKindKeys || kind == kKindValues || kind == kKindEntries);
+template void
+OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Transition();
- Isolate* isolate = iterator->GetIsolate();
- Factory* factory = isolate->factory();
- Handle<OrderedHashMap> table(
- OrderedHashMap::cast(iterator->table()), isolate);
+template Smi*
+OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Next(
+ JSArray* value_array);
- switch (kind) {
- case kKindKeys:
- return Handle<Object>(table->get(entry_index), isolate);
+template bool
+OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::HasMore();
- case kKindValues:
- return Handle<Object>(table->get(entry_index + 1), isolate);
+template void
+OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::MoveNext();
- case kKindEntries: {
- Handle<Object> key(table->get(entry_index), isolate);
- Handle<Object> value(table->get(entry_index + 1), isolate);
- Handle<FixedArray> array = factory->NewFixedArray(2);
- array->set(0, *key);
- array->set(1, *value);
- return factory->NewJSArrayWithElements(array);
- }
- }
+template Object*
+OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::CurrentKey();
- UNREACHABLE();
- return factory->undefined_value();
-}
+template void
+OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Transition();
DeclaredAccessorDescriptorIterator::DeclaredAccessorDescriptorIterator(
=======================================
--- /branches/bleeding_edge/src/objects.h Wed Jun 25 12:12:21 2014 UTC
+++ /branches/bleeding_edge/src/objects.h Thu Jun 26 00:40:45 2014 UTC
@@ -4463,11 +4463,11 @@
Handle<Object> key,
Handle<Object> value);
- private:
Object* ValueAt(int entry) {
return get(EntryToIndex(entry) + kValueOffset);
}
+ private:
static const int kValueOffset = 1;
};
@@ -10104,13 +10104,26 @@
kKindEntries = 3
};
- // Returns an iterator result object: {value: any, done: boolean} and
moves
- // the index to the next valid entry. Closes the iterator if moving past
the
- // end.
- static Handle<JSObject> Next(Handle<Derived> iterator);
+ // Whether the iterator has more elements. This needs to be called before
+ // calling |CurrentKey| and/or |CurrentValue|.
+ bool HasMore();
+
+ // Move the index forward one.
+ void MoveNext() {
+ set_index(Smi::FromInt(Smi::cast(index())->value() + 1));
+ }
+
+ // Populates the array with the next key and value and then moves the
iterator
+ // forward.
+ // This returns the |kind| or 0 if the iterator is already at the end.
+ Smi* Next(JSArray* value_array);
+
+ // Returns the current key of the iterator. This should only be called
when
+ // |HasMore| returns true.
+ inline Object* CurrentKey();
private:
- // Transitions the iterator to the non obsolote backing store. This is a
NOP
+ // Transitions the iterator to the non obsolete backing store. This is a
NOP
// if the [table] is not obsolete.
void Transition();
@@ -10127,9 +10140,9 @@
DECLARE_CAST(JSSetIterator)
- static Handle<Object> ValueForKind(
- Handle<JSSetIterator> iterator,
- int entry_index);
+ // Called by |Next| to populate the array. This allows the subclasses to
+ // populate the array differently.
+ inline void PopulateValueArray(FixedArray* array);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSSetIterator);
@@ -10145,11 +10158,15 @@
DECLARE_CAST(JSMapIterator)
- static Handle<Object> ValueForKind(
- Handle<JSMapIterator> iterator,
- int entry_index);
+ // Called by |Next| to populate the array. This allows the subclasses to
+ // populate the array differently.
+ inline void PopulateValueArray(FixedArray* array);
private:
+ // Returns the current value of the iterator. This should only be called
when
+ // |HasMore| returns true.
+ inline Object* CurrentValue();
+
DISALLOW_IMPLICIT_CONSTRUCTORS(JSMapIterator);
};
=======================================
--- /branches/bleeding_edge/src/runtime.cc Wed Jun 25 15:26:10 2014 UTC
+++ /branches/bleeding_edge/src/runtime.cc Thu Jun 26 00:40:45 2014 UTC
@@ -1604,10 +1604,11 @@
RUNTIME_FUNCTION(Runtime_SetIteratorNext) {
- HandleScope scope(isolate);
- ASSERT(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
- return *JSSetIterator::Next(holder);
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSSetIterator, holder, 0);
+ CONVERT_ARG_CHECKED(JSArray, value_array, 1);
+ return holder->Next(value_array);
}
@@ -1708,10 +1709,11 @@
RUNTIME_FUNCTION(Runtime_MapIteratorNext) {
- HandleScope scope(isolate);
- ASSERT(args.length() == 1);
- CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
- return *JSMapIterator::Next(holder);
+ SealHandleScope shs(isolate);
+ ASSERT(args.length() == 2);
+ CONVERT_ARG_CHECKED(JSMapIterator, holder, 0);
+ CONVERT_ARG_CHECKED(JSArray, value_array, 1);
+ return holder->Next(value_array);
}
=======================================
--- /branches/bleeding_edge/src/runtime.h Wed Jun 25 15:26:10 2014 UTC
+++ /branches/bleeding_edge/src/runtime.h Thu Jun 26 00:40:45 2014 UTC
@@ -274,7 +274,7 @@
F(SetGetSize, 1, 1) \
\
F(SetIteratorInitialize, 3, 1) \
- F(SetIteratorNext, 1, 1) \
+ F(SetIteratorNext, 2, 1) \
\
/* Harmony maps */ \
F(MapInitialize, 1, 1) \
@@ -286,7 +286,7 @@
F(MapGetSize, 1, 1) \
\
F(MapIteratorInitialize, 3, 1) \
- F(MapIteratorNext, 1, 1) \
+ F(MapIteratorNext, 2, 1) \
\
/* Harmony weak maps and sets */ \
F(WeakCollectionInitialize, 1, 1) \
=======================================
--- /branches/bleeding_edge/test/mjsunit/runtime-gen/mapiteratornext.js Tue
Jun 3 00:34:01 2014 UTC
+++ /branches/bleeding_edge/test/mjsunit/runtime-gen/mapiteratornext.js Thu
Jun 26 00:40:45 2014 UTC
@@ -2,4 +2,5 @@
// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
// Flags: --allow-natives-syntax --harmony
var _holder = new Map().entries();
-%MapIteratorNext(_holder);
+var _value_array = new Array();
+%MapIteratorNext(_holder, _value_array);
=======================================
--- /branches/bleeding_edge/test/mjsunit/runtime-gen/setiteratornext.js Tue
Jun 3 00:34:01 2014 UTC
+++ /branches/bleeding_edge/test/mjsunit/runtime-gen/setiteratornext.js Thu
Jun 26 00:40:45 2014 UTC
@@ -2,4 +2,5 @@
// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
// Flags: --allow-natives-syntax --harmony
var _holder = new Set().values();
-%SetIteratorNext(_holder);
+var _value_array = new Array();
+%SetIteratorNext(_holder, _value_array);
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.