Commit f31bae03 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[builtins] Add fast-path for JSTypedArray to CreateListFromArrayLike.

It's quite common today to use Function#apply together with typed
arrays, for example to construct a String from character codes (or code
points) within a Uint8Array or Uint16Array, i.e.

  String.fromCharCode.apply(undefined, uint8array)

is seen quite often on the web. But there are other interesting cases
like

  Math.max.apply(undefined, float64array)

to compute the maximum value in a Float64Array, which is definitely not
the fastest implementation, but quite convenient and readable.
Unfortunately these cases hit the super-slow-path of the Function#apply
machinery in V8 currently, because Function#apply doesn't have any
fast-path for TypedArrays.

This CL adds a proper fast-path to CreateListFromArrayLike to the
ElementsAccessor, which can be used as long as the typed array that's
passed wasn't neutered. With this fast-path in place, the performance on
the micro-benchmark mentioned in the issue improves from

  stringFromCharCode: 6386 ms.
  stringFromCodePoint: 8752 ms.

to

  stringFromCharCode: 1932 ms.
  stringFromCodePoint: 4262 ms.

which corresponds to a 2.0x-3.3x improvement.

Bug: v8:2435
Change-Id: I4d39666e53644b11d5856982b005928e26f296fe
Reviewed-on: https://chromium-review.googlesource.com/657405Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47936}
parent 6716a7f6
......@@ -1327,13 +1327,15 @@ class ElementsAccessorBase : public ElementsAccessor {
return Subclass::GetDetailsImpl(holder, entry);
}
Handle<FixedArray> CreateListFromArray(Isolate* isolate,
Handle<JSArray> array) final {
return Subclass::CreateListFromArrayImpl(isolate, array);
Handle<FixedArray> CreateListFromArrayLike(Isolate* isolate,
Handle<JSObject> object,
uint32_t length) final {
return Subclass::CreateListFromArrayLikeImpl(isolate, object, length);
};
static Handle<FixedArray> CreateListFromArrayImpl(Isolate* isolate,
Handle<JSArray> array) {
static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
Handle<JSObject> object,
uint32_t length) {
UNREACHABLE();
}
......@@ -2376,14 +2378,13 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
}
}
static Handle<FixedArray> CreateListFromArrayImpl(Isolate* isolate,
Handle<JSArray> array) {
uint32_t length = 0;
array->length()->ToArrayLength(&length);
static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
Handle<JSObject> object,
uint32_t length) {
Handle<FixedArray> result = isolate->factory()->NewFixedArray(length);
Handle<FixedArrayBase> elements(array->elements(), isolate);
Handle<FixedArrayBase> elements(object->elements(), isolate);
for (uint32_t i = 0; i < length; i++) {
if (!Subclass::HasElementImpl(isolate, *array, i, *elements)) continue;
if (!Subclass::HasElementImpl(isolate, *object, i, *elements)) continue;
Handle<Object> value;
value = Subclass::GetImpl(isolate, *elements, i);
if (value->IsName()) {
......@@ -3094,6 +3095,20 @@ class TypedElementsAccessor
std::reverse(data, data + len);
}
static Handle<FixedArray> CreateListFromArrayLikeImpl(Isolate* isolate,
Handle<JSObject> object,
uint32_t length) {
DCHECK(!WasNeutered(*object));
DCHECK(object->IsJSTypedArray());
Handle<FixedArray> result = isolate->factory()->NewFixedArray(length);
Handle<BackingStore> elements(BackingStore::cast(object->elements()));
for (uint32_t i = 0; i < length; i++) {
Handle<Object> value = AccessorClass::GetImpl(isolate, *elements, i);
result->set(i, *value);
}
return result;
}
static Handle<JSObject> SliceWithResultImpl(Handle<JSObject> receiver,
uint32_t start, uint32_t end,
Handle<JSObject> result) {
......
......@@ -194,8 +194,9 @@ class ElementsAccessor {
virtual Object* CopyElements(Handle<JSReceiver> source,
Handle<JSObject> destination, size_t length) = 0;
virtual Handle<FixedArray> CreateListFromArray(Isolate* isolate,
Handle<JSArray> array) = 0;
virtual Handle<FixedArray> CreateListFromArrayLike(Isolate* isolate,
Handle<JSObject> object,
uint32_t length) = 0;
protected:
friend class LookupIterator;
......
......@@ -872,26 +872,40 @@ MaybeHandle<Object> Object::GetMethod(Handle<JSReceiver> receiver,
}
namespace {
MaybeHandle<FixedArray> CreateListFromArrayLikeFastPath(
Isolate* isolate, Handle<Object> object, ElementTypes element_types) {
if (element_types != ElementTypes::kAll || !object->IsJSArray()) {
return MaybeHandle<FixedArray>();
}
Handle<JSArray> array = Handle<JSArray>::cast(object);
uint32_t length;
if (!array->HasArrayPrototype(isolate) ||
!array->length()->ToUint32(&length) || !array->HasFastElements() ||
!JSObject::PrototypeHasNoElements(isolate, *array)) {
return MaybeHandle<FixedArray>();
if (element_types == ElementTypes::kAll) {
if (object->IsJSArray()) {
Handle<JSArray> array = Handle<JSArray>::cast(object);
uint32_t length;
if (!array->HasArrayPrototype(isolate) ||
!array->length()->ToUint32(&length) || !array->HasFastElements() ||
!JSObject::PrototypeHasNoElements(isolate, *array)) {
return MaybeHandle<FixedArray>();
}
return array->GetElementsAccessor()->CreateListFromArrayLike(
isolate, array, length);
} else if (object->IsJSTypedArray()) {
Handle<JSTypedArray> array = Handle<JSTypedArray>::cast(object);
uint32_t length = array->length_value();
if (array->WasNeutered() ||
length > static_cast<uint32_t>(FixedArray::kMaxLength)) {
return MaybeHandle<FixedArray>();
}
return array->GetElementsAccessor()->CreateListFromArrayLike(
isolate, array, length);
}
}
return array->GetElementsAccessor()->CreateListFromArray(isolate, array);
return MaybeHandle<FixedArray>();
}
} // namespace
// static
MaybeHandle<FixedArray> Object::CreateListFromArrayLike(
Isolate* isolate, Handle<Object> object, ElementTypes element_types) {
// Fast-path for JS_ARRAY_TYPE.
// Fast-path for JSArray and JSTypedArray.
MaybeHandle<FixedArray> fast_result =
CreateListFromArrayLikeFastPath(isolate, object, element_types);
if (!fast_result.is_null()) return fast_result;
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function arrayLikeToString(a) {
return String.fromCharCode.apply(this, a);
}
const klasses = [
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array
];
const string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
for (const klass of klasses) {
const array = klass.from(string, s => s.charCodeAt(0));
assertEquals(string, arrayLikeToString(array));
}
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