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
This commit is contained in:
parent
3e7e3f70a6
commit
4ebeda216d
12
include/v8.h
12
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|.
|
||||
|
@ -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); }
|
||||
};
|
||||
|
||||
|
193
src/runtime.cc
193
src/runtime.cc
@ -712,13 +712,18 @@ void Runtime::SetupArrayBuffer(Isolate* isolate,
|
||||
bool Runtime::SetupArrayBufferAllocatingData(
|
||||
Isolate* isolate,
|
||||
Handle<JSArrayBuffer> 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,6 +810,50 @@ enum TypedArrayId {
|
||||
ARRAY_ID_UINT8C = 9
|
||||
};
|
||||
|
||||
static void ArrayIdToTypeAndSize(
|
||||
int arrayId, ExternalArrayType* array_type, size_t* element_size) {
|
||||
switch (arrayId) {
|
||||
case ARRAY_ID_UINT8:
|
||||
*array_type = kExternalUnsignedByteArray;
|
||||
*element_size = 1;
|
||||
break;
|
||||
case ARRAY_ID_INT8:
|
||||
*array_type = kExternalByteArray;
|
||||
*element_size = 1;
|
||||
break;
|
||||
case ARRAY_ID_UINT16:
|
||||
*array_type = kExternalUnsignedShortArray;
|
||||
*element_size = 2;
|
||||
break;
|
||||
case ARRAY_ID_INT16:
|
||||
*array_type = kExternalShortArray;
|
||||
*element_size = 2;
|
||||
break;
|
||||
case ARRAY_ID_UINT32:
|
||||
*array_type = kExternalUnsignedIntArray;
|
||||
*element_size = 4;
|
||||
break;
|
||||
case ARRAY_ID_INT32:
|
||||
*array_type = kExternalIntArray;
|
||||
*element_size = 4;
|
||||
break;
|
||||
case ARRAY_ID_FLOAT32:
|
||||
*array_type = kExternalFloatArray;
|
||||
*element_size = 4;
|
||||
break;
|
||||
case ARRAY_ID_FLOAT64:
|
||||
*array_type = kExternalDoubleArray;
|
||||
*element_size = 8;
|
||||
break;
|
||||
case ARRAY_ID_UINT8C:
|
||||
*array_type = kExternalPixelArray;
|
||||
*element_size = 1;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) {
|
||||
HandleScope scope(isolate);
|
||||
@ -821,49 +870,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) {
|
||||
holder->SetInternalField(i, Smi::FromInt(0));
|
||||
}
|
||||
|
||||
ExternalArrayType arrayType;
|
||||
size_t elementSize;
|
||||
switch (arrayId) {
|
||||
case ARRAY_ID_UINT8:
|
||||
arrayType = kExternalUnsignedByteArray;
|
||||
elementSize = 1;
|
||||
break;
|
||||
case ARRAY_ID_INT8:
|
||||
arrayType = kExternalByteArray;
|
||||
elementSize = 1;
|
||||
break;
|
||||
case ARRAY_ID_UINT16:
|
||||
arrayType = kExternalUnsignedShortArray;
|
||||
elementSize = 2;
|
||||
break;
|
||||
case ARRAY_ID_INT16:
|
||||
arrayType = kExternalShortArray;
|
||||
elementSize = 2;
|
||||
break;
|
||||
case ARRAY_ID_UINT32:
|
||||
arrayType = kExternalUnsignedIntArray;
|
||||
elementSize = 4;
|
||||
break;
|
||||
case ARRAY_ID_INT32:
|
||||
arrayType = kExternalIntArray;
|
||||
elementSize = 4;
|
||||
break;
|
||||
case ARRAY_ID_FLOAT32:
|
||||
arrayType = kExternalFloatArray;
|
||||
elementSize = 4;
|
||||
break;
|
||||
case ARRAY_ID_FLOAT64:
|
||||
arrayType = kExternalDoubleArray;
|
||||
elementSize = 8;
|
||||
break;
|
||||
case ARRAY_ID_UINT8C:
|
||||
arrayType = kExternalPixelArray;
|
||||
elementSize = 1;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
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<Object> length_obj = isolate->factory()->NewNumberFromSize(length);
|
||||
holder->set_length(*length_obj);
|
||||
@ -881,13 +890,99 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) {
|
||||
|
||||
Handle<ExternalArray> elements =
|
||||
isolate->factory()->NewExternalArray(
|
||||
static_cast<int>(length), arrayType,
|
||||
static_cast<int>(length), array_type,
|
||||
static_cast<uint8_t*>(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<JSArrayBuffer> 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<Object>(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<Object>(NULL, 0)));
|
||||
}
|
||||
|
||||
holder->set_buffer(*buffer);
|
||||
holder->set_byte_offset(Smi::FromInt(0));
|
||||
Handle<Object> 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<ExternalArray> elements =
|
||||
isolate->factory()->NewExternalArray(
|
||||
static_cast<int>(length), array_type,
|
||||
static_cast<uint8_t*>(buffer->backing_store()));
|
||||
holder->set_elements(*elements);
|
||||
|
||||
if (source->IsJSTypedArray()) {
|
||||
Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
|
||||
|
||||
if (typed_array->type() == holder->type()) {
|
||||
uint8_t* backing_store =
|
||||
static_cast<uint8_t*>(
|
||||
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); \
|
||||
|
@ -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<JSArrayBuffer> array_buffer,
|
||||
size_t allocated_length);
|
||||
size_t allocated_length,
|
||||
bool initialize = true);
|
||||
|
||||
static void FreeArrayBuffer(
|
||||
Isolate* isolate,
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user