From 4ebeda216de6cfba8fea666295ff38fb1a2569ba Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Thu, 1 Aug 2013 08:52:21 +0000 Subject: [PATCH] Speed-up 'new TypedArray(arrayLike)'. Handle specially the cases when the argument is a typed array, in particular of the same type as the one we create. Allocate backing store uninitialized in cases when we can guarantee complete initialization. R=bmeurer@chromium.org Committed: https://code.google.com/p/v8/source/detail?r=15998 Review URL: https://codereview.chromium.org/21369002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16005 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 12 ++++ src/d8.cc | 9 ++- src/runtime.cc | 181 +++++++++++++++++++++++++++++++++++++++++------------- src/runtime.h | 4 +- src/typedarray.js | 9 ++- 5 files changed, 165 insertions(+), 50 deletions(-) diff --git a/include/v8.h b/include/v8.h index a4eae96..11fccdf 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2410,8 +2410,20 @@ class V8EXPORT ArrayBuffer : public Object { /** * Allocate |length| bytes. Return NULL if allocation is not successful. + * Memory should be initialized to zeroes. */ virtual void* Allocate(size_t length) = 0; + + /** + * Allocate |length| bytes. Return NULL if allocation is not successful. + * Memory does not have to be initialized. + */ + virtual void* AllocateUninitialized(size_t length) { + // Override with call to |Allocate| for compatibility + // with legacy version. + return Allocate(length); + } + /** * Free the memory pointed to |data|. That memory is guaranteed to be * previously allocated by |Allocate|. diff --git a/src/d8.cc b/src/d8.cc index 1efe2ae..cde8404 100644 --- a/src/d8.cc +++ b/src/d8.cc @@ -1562,7 +1562,14 @@ static void SetStandaloneFlagsViaCommandLine() { class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: - virtual void* Allocate(size_t length) { return malloc(length); } + virtual void* Allocate(size_t length) { + void* result = malloc(length); + memset(result, 0, length); + return result; + } + virtual void* AllocateUninitialized(size_t length) { + return malloc(length); + } virtual void Free(void* data) { free(data); } }; diff --git a/src/runtime.cc b/src/runtime.cc index 2cbaad1..0970d0d 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -712,13 +712,18 @@ void Runtime::SetupArrayBuffer(Isolate* isolate, bool Runtime::SetupArrayBufferAllocatingData( Isolate* isolate, Handle array_buffer, - size_t allocated_length) { + size_t allocated_length, + bool initialize) { void* data; CHECK(V8::ArrayBufferAllocator() != NULL); if (allocated_length != 0) { - data = V8::ArrayBufferAllocator()->Allocate(allocated_length); + if (initialize) { + data = V8::ArrayBufferAllocator()->Allocate(allocated_length); + } else { + data = + V8::ArrayBufferAllocator()->AllocateUninitialized(allocated_length); + } if (data == NULL) return false; - memset(data, 0, allocated_length); } else { data = NULL; } @@ -805,65 +810,69 @@ enum TypedArrayId { ARRAY_ID_UINT8C = 9 }; - -RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { - HandleScope scope(isolate); - ASSERT(args.length() == 5); - CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0); - CONVERT_SMI_ARG_CHECKED(arrayId, 1); - CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 2); - CONVERT_ARG_HANDLE_CHECKED(Object, byte_offset_object, 3); - CONVERT_ARG_HANDLE_CHECKED(Object, byte_length_object, 4); - - ASSERT(holder->GetInternalFieldCount() == - v8::ArrayBufferView::kInternalFieldCount); - for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { - holder->SetInternalField(i, Smi::FromInt(0)); - } - - ExternalArrayType arrayType; - size_t elementSize; +static void ArrayIdToTypeAndSize( + int arrayId, ExternalArrayType* array_type, size_t* element_size) { switch (arrayId) { case ARRAY_ID_UINT8: - arrayType = kExternalUnsignedByteArray; - elementSize = 1; + *array_type = kExternalUnsignedByteArray; + *element_size = 1; break; case ARRAY_ID_INT8: - arrayType = kExternalByteArray; - elementSize = 1; + *array_type = kExternalByteArray; + *element_size = 1; break; case ARRAY_ID_UINT16: - arrayType = kExternalUnsignedShortArray; - elementSize = 2; + *array_type = kExternalUnsignedShortArray; + *element_size = 2; break; case ARRAY_ID_INT16: - arrayType = kExternalShortArray; - elementSize = 2; + *array_type = kExternalShortArray; + *element_size = 2; break; case ARRAY_ID_UINT32: - arrayType = kExternalUnsignedIntArray; - elementSize = 4; + *array_type = kExternalUnsignedIntArray; + *element_size = 4; break; case ARRAY_ID_INT32: - arrayType = kExternalIntArray; - elementSize = 4; + *array_type = kExternalIntArray; + *element_size = 4; break; case ARRAY_ID_FLOAT32: - arrayType = kExternalFloatArray; - elementSize = 4; + *array_type = kExternalFloatArray; + *element_size = 4; break; case ARRAY_ID_FLOAT64: - arrayType = kExternalDoubleArray; - elementSize = 8; + *array_type = kExternalDoubleArray; + *element_size = 8; break; case ARRAY_ID_UINT8C: - arrayType = kExternalPixelArray; - elementSize = 1; + *array_type = kExternalPixelArray; + *element_size = 1; break; default: UNREACHABLE(); - return NULL; } +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { + HandleScope scope(isolate); + ASSERT(args.length() == 5); + CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0); + CONVERT_SMI_ARG_CHECKED(arrayId, 1); + CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, buffer, 2); + CONVERT_ARG_HANDLE_CHECKED(Object, byte_offset_object, 3); + CONVERT_ARG_HANDLE_CHECKED(Object, byte_length_object, 4); + + ASSERT(holder->GetInternalFieldCount() == + v8::ArrayBufferView::kInternalFieldCount); + for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { + holder->SetInternalField(i, Smi::FromInt(0)); + } + + ExternalArrayType array_type = kExternalByteArray; // Bogus initialization. + size_t element_size = 1; // Bogus initialization. + ArrayIdToTypeAndSize(arrayId, &array_type, &element_size); holder->set_buffer(*buffer); holder->set_byte_offset(*byte_offset_object); @@ -871,8 +880,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { size_t byte_offset = NumberToSize(isolate, *byte_offset_object); size_t byte_length = NumberToSize(isolate, *byte_length_object); - ASSERT(byte_length % elementSize == 0); - size_t length = byte_length / elementSize; + ASSERT(byte_length % element_size == 0); + size_t length = byte_length / element_size; Handle length_obj = isolate->factory()->NewNumberFromSize(length); holder->set_length(*length_obj); @@ -881,13 +890,99 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { Handle elements = isolate->factory()->NewExternalArray( - static_cast(length), arrayType, + static_cast(length), array_type, static_cast(buffer->backing_store()) + byte_offset); holder->set_elements(*elements); return isolate->heap()->undefined_value(); } +// Initializes a typed array from an array-like object. +// If an array-like object happens to be a typed array of the same type, +// initializes backing store using memove. +// +// Returns true if backing store was initialized or false otherwise. +RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitializeFromArrayLike) { + HandleScope scope(isolate); + ASSERT(args.length() == 4); + CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0); + CONVERT_SMI_ARG_CHECKED(arrayId, 1); + CONVERT_ARG_HANDLE_CHECKED(Object, source, 2); + CONVERT_ARG_HANDLE_CHECKED(Object, length_obj, 3); + + ASSERT(holder->GetInternalFieldCount() == + v8::ArrayBufferView::kInternalFieldCount); + for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { + holder->SetInternalField(i, Smi::FromInt(0)); + } + + ExternalArrayType array_type = kExternalByteArray; // Bogus initialization. + size_t element_size = 1; // Bogus initialization. + ArrayIdToTypeAndSize(arrayId, &array_type, &element_size); + + Handle buffer = isolate->factory()->NewJSArrayBuffer(); + size_t length = NumberToSize(isolate, *length_obj); + size_t byte_length = length * element_size; + if (byte_length < length) { // Overflow + return isolate->Throw(*isolate->factory()-> + NewRangeError("invalid_array_buffer_length", + HandleVector(NULL, 0))); + } + + // We assume that the caller of this function will initialize holder + // with the loop + // for(i = 0; i < length; i++) { holder[i] = source[i]; } + // If source is a typed array, this loop will always run to completion, + // so we are sure that the backing store will be initialized. + // Otherwise, we do not know (the indexing operation might throw). + // Hence we require zero initialization unless our source is a typed array. + bool should_zero_initialize = !source->IsJSTypedArray(); + + if (!Runtime::SetupArrayBufferAllocatingData( + isolate, buffer, byte_length, should_zero_initialize)) { + return isolate->Throw(*isolate->factory()-> + NewRangeError("invalid_array_buffer_length", + HandleVector(NULL, 0))); + } + + holder->set_buffer(*buffer); + holder->set_byte_offset(Smi::FromInt(0)); + Handle byte_length_obj( + isolate->factory()->NewNumberFromSize(byte_length)); + holder->set_byte_length(*byte_length_obj); + holder->set_length(*length_obj); + holder->set_weak_next(buffer->weak_first_view()); + buffer->set_weak_first_view(*holder); + + Handle elements = + isolate->factory()->NewExternalArray( + static_cast(length), array_type, + static_cast(buffer->backing_store())); + holder->set_elements(*elements); + + if (source->IsJSTypedArray()) { + Handle typed_array(JSTypedArray::cast(*source)); + + if (typed_array->type() == holder->type()) { + uint8_t* backing_store = + static_cast( + JSArrayBuffer::cast(typed_array->buffer())->backing_store()); + size_t source_byte_offset = + NumberToSize(isolate, typed_array->byte_offset()); + OS::MemCopy( + buffer->backing_store(), + backing_store + source_byte_offset, + byte_length); + return *isolate->factory()->true_value(); + } else { + return *isolate->factory()->false_value(); + } + } + + return *isolate->factory()->false_value(); +} + + #define TYPED_ARRAY_GETTER(getter, accessor) \ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayGet##getter) { \ HandleScope scope(isolate); \ diff --git a/src/runtime.h b/src/runtime.h index 17e3b43..398cb3b 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -361,6 +361,7 @@ namespace internal { F(ArrayBufferSliceImpl, 3, 1) \ \ F(TypedArrayInitialize, 5, 1) \ + F(TypedArrayInitializeFromArrayLike, 4, 1) \ F(TypedArrayGetBuffer, 1, 1) \ F(TypedArrayGetByteLength, 1, 1) \ F(TypedArrayGetByteOffset, 1, 1) \ @@ -784,7 +785,8 @@ class Runtime : public AllStatic { static bool SetupArrayBufferAllocatingData( Isolate* isolate, Handle array_buffer, - size_t allocated_length); + size_t allocated_length, + bool initialize = true); static void FreeArrayBuffer( Isolate* isolate, diff --git a/src/typedarray.js b/src/typedarray.js index f9e732f..7bd16f6 100644 --- a/src/typedarray.js +++ b/src/typedarray.js @@ -77,11 +77,10 @@ function CreateTypedArrayConstructor(name, elementSize, arrayId, constructor) { function ConstructByArrayLike(obj, arrayLike) { var length = arrayLike.length; var l = ToPositiveInteger(length, "invalid_typed_array_length"); - var byteLength = l * elementSize; - var buffer = new $ArrayBuffer(byteLength); - %TypedArrayInitialize(obj, arrayId, buffer, 0, byteLength); - for (var i = 0; i < l; i++) { - obj[i] = arrayLike[i]; + if(!%TypedArrayInitializeFromArrayLike(obj, arrayId, arrayLike, l)) { + for (var i = 0; i < l; i++) { + obj[i] = arrayLike[i]; + } } } -- 2.7.4