Skip to content

Commit 29e5f28

Browse files
committed
[JSC] Add Array.from(Array) fast path
https://bugs.webkit.org/show_bug.cgi?id=263490 rdar://117290480 Reviewed by Michael Saboff. This patch adds Array.from fast path for simple arrays without callbacks, like, `Array.from(array)`. This is because this pattern can be used for fast array cloning. The fast path is 8.14x faster than the Array.from generic path. We should take Array.from fast path when array iterator protocol allows. This ensures that we can iterate an array without creating an iterator. And it also ensures that our Array prototype chain is sane. So, we can skip many costly operations and directly copy data. array-from-array 2.6646+-0.0174 ^ 0.3271+-0.0155 ^ definitely 8.1469x faster * JSTests/microbenchmarks/array-from-array.js: Added. * Source/JavaScriptCore/builtins/ArrayConstructor.js: * Source/JavaScriptCore/builtins/BuiltinNames.h: * Source/JavaScriptCore/bytecode/LinkTimeConstant.h: * Source/JavaScriptCore/runtime/ArrayPrototype.cpp: (JSC::JSC_DEFINE_HOST_FUNCTION): * Source/JavaScriptCore/runtime/ArrayPrototype.h: * Source/JavaScriptCore/runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::init): Canonical link: https://commits.webkit.org/269690@main
1 parent d0b6e88 commit 29e5f28

7 files changed

Lines changed: 71 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var a1 = new Array(1024);
2+
a1.fill(99);
3+
for (var i = 0; i < 1e2; ++i)
4+
var a2 = Array.from(a1);

‎Source/JavaScriptCore/builtins/ArrayConstructor.js‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ function from(items /*, mapFn, thisArg */)
5252

5353
var arrayLike = @toObject(items, "Array.from requires an array-like object - not null or undefined");
5454

55+
if (!mapFn) {
56+
var fastResult = @arrayFromFast(this, arrayLike);
57+
if (fastResult)
58+
return fastResult;
59+
}
60+
5561
var iteratorMethod = items.@@iterator;
5662
if (!@isUndefinedOrNull(iteratorMethod)) {
5763
if (!@isCallable(iteratorMethod))

‎Source/JavaScriptCore/builtins/BuiltinNames.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ namespace JSC {
198198
macro(sentinelString) \
199199
macro(createRemoteFunction) \
200200
macro(isRemoteFunction) \
201+
macro(arrayFromFast) \
201202
macro(arraySort) \
202203
macro(jsonParse) \
203204
macro(jsonStringify) \

‎Source/JavaScriptCore/bytecode/LinkTimeConstant.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ class JSGlobalObject;
122122
v(sentinelString, nullptr) \
123123
v(createRemoteFunction, nullptr) \
124124
v(isRemoteFunction, nullptr) \
125+
v(arrayFromFast, nullptr) \
125126
v(arraySort, nullptr) \
126127
v(jsonParse, nullptr) \
127128
v(jsonStringify, nullptr) \

‎Source/JavaScriptCore/runtime/ArrayPrototype.cpp‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,4 +1523,59 @@ JSC_DEFINE_HOST_FUNCTION(arrayProtoPrivateFuncAppendMemcpy, (JSGlobalObject* glo
15231523
return JSValue::encode(jsUndefined());
15241524
}
15251525

1526+
JSC_DEFINE_HOST_FUNCTION(arrayProtoPrivateFuncFromFast, (JSGlobalObject* globalObject, CallFrame* callFrame))
1527+
{
1528+
VM& vm = globalObject->vm();
1529+
auto scope = DECLARE_THROW_SCOPE(vm);
1530+
1531+
JSValue constructor = callFrame->uncheckedArgument(0);
1532+
if (UNLIKELY(constructor != globalObject->arrayConstructor() && constructor.isObject()))
1533+
return JSValue::encode(jsUndefined());
1534+
1535+
auto* array = jsDynamicCast<JSArray*>(callFrame->uncheckedArgument(1));
1536+
if (!array)
1537+
return JSValue::encode(jsUndefined());
1538+
1539+
if (UNLIKELY(!array->isIteratorProtocolFastAndNonObservable()))
1540+
return JSValue::encode(jsUndefined());
1541+
1542+
IndexingType type = array->indexingType();
1543+
if (UNLIKELY(shouldUseSlowPut(type)))
1544+
return JSValue::encode(jsUndefined());
1545+
1546+
Butterfly* butterfly= array->butterfly();
1547+
unsigned resultSize = butterfly->publicLength();
1548+
if (hasAnyArrayStorage(type) || resultSize >= MIN_SPARSE_ARRAY_INDEX) {
1549+
JSArray* result = constructEmptyArray(globalObject, nullptr, resultSize);
1550+
RETURN_IF_EXCEPTION(scope, { });
1551+
1552+
scope.release();
1553+
moveElements(globalObject, vm, result, 0, array, resultSize);
1554+
return JSValue::encode(result);
1555+
}
1556+
1557+
Structure* resultStructure = globalObject->arrayStructureForIndexingTypeDuringAllocation(type);
1558+
if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
1559+
return JSValue::encode(jsUndefined());
1560+
1561+
ASSERT(!globalObject->isHavingABadTime());
1562+
ObjectInitializationScope initializationScope(vm);
1563+
JSArray* result = JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure, resultSize);
1564+
if (UNLIKELY(!result)) {
1565+
throwOutOfMemoryError(globalObject, scope);
1566+
return { };
1567+
}
1568+
1569+
if (type == ArrayWithDouble) {
1570+
double* buffer = result->butterfly()->contiguousDouble().data();
1571+
copyElements(buffer, 0, butterfly->contiguousDouble().data(), resultSize, ArrayWithDouble);
1572+
} else if (type != ArrayWithUndecided) {
1573+
WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
1574+
copyElements(buffer, 0, butterfly->contiguous().data(), resultSize, type);
1575+
}
1576+
1577+
ASSERT(result->butterfly()->publicLength() == resultSize);
1578+
return JSValue::encode(result);
1579+
}
1580+
15261581
} // namespace JSC

‎Source/JavaScriptCore/runtime/ArrayPrototype.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncToString);
5050
JSC_DECLARE_HOST_FUNCTION(arrayProtoFuncValues);
5151
JSC_DECLARE_HOST_FUNCTION(arrayProtoPrivateFuncConcatMemcpy);
5252
JSC_DECLARE_HOST_FUNCTION(arrayProtoPrivateFuncAppendMemcpy);
53+
JSC_DECLARE_HOST_FUNCTION(arrayProtoPrivateFuncFromFast);
5354

5455
} // namespace JSC

‎Source/JavaScriptCore/runtime/JSGlobalObject.cpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,9 @@ capitalName ## Constructor* lowerName ## Constructor = featureFlag ? capitalName
15371537
m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::typedArrayFromFast)].initLater([] (const Initializer<JSCell>& init) {
15381538
init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 2, "typedArrayViewTypedArrayFromFast"_s, typedArrayViewPrivateFuncTypedArrayFromFast, ImplementationVisibility::Private));
15391539
});
1540+
m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::arrayFromFast)].initLater([] (const Initializer<JSCell>& init) {
1541+
init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 2, "arrayFromFast"_s, arrayProtoPrivateFuncFromFast, ImplementationVisibility::Private));
1542+
});
15401543
m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::isDetached)].initLater([] (const Initializer<JSCell>& init) {
15411544
init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 1, "typedArrayViewIsDetached"_s, typedArrayViewPrivateFuncIsDetached, ImplementationVisibility::Private));
15421545
});

0 commit comments

Comments
 (0)