Log Message
[JSC] Optimize Object.keys by using careful array allocation https://bugs.webkit.org/show_bug.cgi?id=176654
Reviewed by Darin Adler.
JSTests:
* microbenchmarks/object-keys.js: Added.
(test):
Source/_javascript_Core:
SixSpeed object-assign.es6 stresses Object.keys. Object.keys is one of frequently used
function in JS apps. Luckily Object.keys has several good features.
1. Once PropertyNameArray is allocated, we know the length of the result array since
we do not need to filter out keys listed in PropertyNameArray. The execption is ProxyObject,
but it rarely appears. ProxyObject case goes to the generic path.
2. Object.keys does not need to access object after listing PropertyNameArray. It means
that we do not need to worry about enumeration attribute change by touching object.
This patch adds a fast path for Object.keys's array allocation. We allocate the JSArray
with the size and ArrayContiguous indexing shape.
This further improves SixSpeed object-assign.es5 by 13%.
baseline patched
Microbenchmarks:
object-keys-map-values 73.4324+-2.5397 ^ 62.5933+-2.6677 ^ definitely 1.1732x faster
object-keys 40.8828+-1.5851 ^ 29.2066+-1.8944 ^ definitely 1.3998x faster
baseline patched
SixSpeed:
object-assign.es5 384.8719+-10.7204 ^ 340.2734+-12.0947 ^ definitely 1.1311x faster
BTW, the further optimization of Object.keys can be considered: introducing own property keys
cache which is similar to the current enumeration cache. But this patch is orthogonal to
this optimization!
* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorValues):
(JSC::ownPropertyKeys):
* runtime/ObjectConstructor.h:
Modified Paths
- trunk/JSTests/ChangeLog
- trunk/Source/_javascript_Core/ChangeLog
- trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp
Added Paths
Diff
Modified: trunk/JSTests/ChangeLog (221852 => 221853)
--- trunk/JSTests/ChangeLog 2017-09-11 07:19:39 UTC (rev 221852)
+++ trunk/JSTests/ChangeLog 2017-09-11 08:10:06 UTC (rev 221853)
@@ -1,3 +1,13 @@
+2017-09-09 Yusuke Suzuki <[email protected]>
+
+ [JSC] Optimize Object.keys by using careful array allocation
+ https://bugs.webkit.org/show_bug.cgi?id=176654
+
+ Reviewed by Darin Adler.
+
+ * microbenchmarks/object-keys.js: Added.
+ (test):
+
2017-09-09 Filip Pizlo <[email protected]>
Error should compute .stack and friends lazily
Added: trunk/JSTests/microbenchmarks/object-keys.js (0 => 221853)
--- trunk/JSTests/microbenchmarks/object-keys.js (rev 0)
+++ trunk/JSTests/microbenchmarks/object-keys.js 2017-09-11 08:10:06 UTC (rev 221853)
@@ -0,0 +1,13 @@
+var object = {};
+for (var i = 0; i < 1e3; ++i) {
+ object[i + 'prop'] = i;
+}
+
+function test(object)
+{
+ return Object.keys(object);
+}
+noInline(test);
+
+for (var i = 0; i < 1e3; ++i)
+ test(object);
Modified: trunk/Source/_javascript_Core/ChangeLog (221852 => 221853)
--- trunk/Source/_javascript_Core/ChangeLog 2017-09-11 07:19:39 UTC (rev 221852)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-09-11 08:10:06 UTC (rev 221853)
@@ -1,3 +1,43 @@
+2017-09-09 Yusuke Suzuki <[email protected]>
+
+ [JSC] Optimize Object.keys by using careful array allocation
+ https://bugs.webkit.org/show_bug.cgi?id=176654
+
+ Reviewed by Darin Adler.
+
+ SixSpeed object-assign.es6 stresses Object.keys. Object.keys is one of frequently used
+ function in JS apps. Luckily Object.keys has several good features.
+
+ 1. Once PropertyNameArray is allocated, we know the length of the result array since
+ we do not need to filter out keys listed in PropertyNameArray. The execption is ProxyObject,
+ but it rarely appears. ProxyObject case goes to the generic path.
+
+ 2. Object.keys does not need to access object after listing PropertyNameArray. It means
+ that we do not need to worry about enumeration attribute change by touching object.
+
+ This patch adds a fast path for Object.keys's array allocation. We allocate the JSArray
+ with the size and ArrayContiguous indexing shape.
+
+ This further improves SixSpeed object-assign.es5 by 13%.
+
+ baseline patched
+ Microbenchmarks:
+ object-keys-map-values 73.4324+-2.5397 ^ 62.5933+-2.6677 ^ definitely 1.1732x faster
+ object-keys 40.8828+-1.5851 ^ 29.2066+-1.8944 ^ definitely 1.3998x faster
+
+ baseline patched
+ SixSpeed:
+ object-assign.es5 384.8719+-10.7204 ^ 340.2734+-12.0947 ^ definitely 1.1311x faster
+
+ BTW, the further optimization of Object.keys can be considered: introducing own property keys
+ cache which is similar to the current enumeration cache. But this patch is orthogonal to
+ this optimization!
+
+ * runtime/ObjectConstructor.cpp:
+ (JSC::objectConstructorValues):
+ (JSC::ownPropertyKeys):
+ * runtime/ObjectConstructor.h:
+
2017-09-10 Mark Lam <[email protected]>
Fix all ExceptionScope verification failures in _javascript_Core.
Modified: trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp (221852 => 221853)
--- trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp 2017-09-11 07:19:39 UTC (rev 221852)
+++ trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp 2017-09-11 08:10:06 UTC (rev 221853)
@@ -379,7 +379,7 @@
JSObject* target = targetValue.toObject(exec);
RETURN_IF_EXCEPTION(scope, { });
- JSArray* values = constructEmptyArray(exec, 0);
+ JSArray* values = constructEmptyArray(exec, nullptr);
RETURN_IF_EXCEPTION(scope, { });
PropertyNameArray properties(exec, PropertyNameMode::Strings);
@@ -386,6 +386,7 @@
target->methodTable(vm)->getOwnPropertyNames(target, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
RETURN_IF_EXCEPTION(scope, { });
+ unsigned index = 0;
auto addValue = [&] (PropertyName propertyName) {
PropertySlot slot(target, PropertySlot::InternalMethodType::GetOwnProperty);
bool hasProperty = target->methodTable(vm)->getOwnPropertySlot(target, exec, propertyName, slot);
@@ -402,7 +403,7 @@
value = target->get(exec, propertyName);
RETURN_IF_EXCEPTION(scope, void());
- values->push(exec, value);
+ values->putDirectIndex(exec, index++, value);
};
for (unsigned i = 0, numProperties = properties.size(); i < numProperties; i++) {
@@ -839,9 +840,6 @@
object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode));
RETURN_IF_EXCEPTION(scope, nullptr);
- JSArray* keys = constructEmptyArray(exec, 0);
- RETURN_IF_EXCEPTION(scope, nullptr);
-
// https://tc39.github.io/ecma262/#sec-enumerableownproperties
// If {object} is a Proxy, an explicit and observable [[GetOwnProperty]] op is required to filter out non-enumerable properties.
// In other cases, filtering has already been performed.
@@ -854,6 +852,28 @@
return object->getOwnPropertyDescriptor(exec, name, descriptor) && descriptor.enumerable();
};
+ // If !mustFilterProperty and PropertyNameMode::Strings mode, we do not need to filter out any entries in PropertyNameArray.
+ // We can use fast allocation and initialization.
+ if (!mustFilterProperty && propertyNameMode == PropertyNameMode::Strings && properties.size() < MIN_SPARSE_ARRAY_INDEX) {
+ size_t numProperties = properties.size();
+ JSArray* keys = JSArray::create(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), numProperties);
+ WriteBarrier<Unknown>* buffer = keys->butterfly()->contiguous().data();
+ for (size_t i = 0; i < numProperties; i++) {
+ const auto& identifier = properties[i];
+ ASSERT(!identifier.isSymbol());
+ buffer[i].set(vm, keys, jsOwnedString(&vm, identifier.string()));
+ }
+ return keys;
+ }
+
+ JSArray* keys = constructEmptyArray(exec, nullptr);
+ RETURN_IF_EXCEPTION(scope, nullptr);
+
+ unsigned index = 0;
+ auto pushDirect = [&] (ExecState* exec, JSArray* array, JSValue value) {
+ array->putDirectIndex(exec, index++, value);
+ };
+
switch (propertyNameMode) {
case PropertyNameMode::Strings: {
size_t numProperties = properties.size();
@@ -863,7 +883,7 @@
bool hasProperty = filterPropertyIfNeeded(identifier);
EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
if (hasProperty)
- keys->push(exec, jsOwnedString(exec, identifier.string()));
+ pushDirect(exec, keys, jsOwnedString(exec, identifier.string()));
RETURN_IF_EXCEPTION(scope, nullptr);
}
break;
@@ -878,7 +898,7 @@
bool hasProperty = filterPropertyIfNeeded(identifier);
EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
if (hasProperty)
- keys->push(exec, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
+ pushDirect(exec, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
RETURN_IF_EXCEPTION(scope, nullptr);
}
}
@@ -896,7 +916,7 @@
bool hasProperty = filterPropertyIfNeeded(identifier);
EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
if (hasProperty)
- keys->push(exec, jsOwnedString(exec, identifier.string()));
+ pushDirect(exec, keys, jsOwnedString(exec, identifier.string()));
RETURN_IF_EXCEPTION(scope, nullptr);
}
}
@@ -906,7 +926,7 @@
bool hasProperty = filterPropertyIfNeeded(identifier);
EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
if (hasProperty)
- keys->push(exec, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
+ pushDirect(exec, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
RETURN_IF_EXCEPTION(scope, nullptr);
}
_______________________________________________ webkit-changes mailing list [email protected] https://lists.webkit.org/mailman/listinfo/webkit-changes
