From f2362442ffc6d7a8848136b8ae3d38e230435b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Fri, 11 Sep 2015 23:16:42 +0200 Subject: [PATCH] Improve TypedArray performance Cherry-picked from Node.js: commit acb6779c19abf248a4c6d5c3d3389805e7ee8b8d Author: Fedor Indutny Date: Thu Sep 10 12:26:50 2015 -0700 deps: cherry-pick 6da51b4 from v8's upstream Original commit message: TypedArray accessor detection: consider entire prototype chain When looking up a special accessor for known TypedArray fields ("length", "byteLength", "byteOffset"), consider the entire prototype chain, not only the direct prototype. This allows subclasses of TypedArrays to benefit from fast specialized accesses. Review URL: https://codereview.chromium.org/1313493005 Cr-Commit-Position: refs/heads/master@{#30678} Benchmark results: buffers/buffer-iterate.js size=16386 type=slow method=for n=1000: ./node: 71607 node: 8702.3 ............ 722.85% Improvement depends on the code, but generally brings us back to the performance that we had before the v8 update (if not making it faster). Fixes: https://github.com/nodejs/node/issues/2463 PR-URL: https://github.com/nodejs/node/pull/2801 Reviewed-By: Ben Noordhuis Reviewed-By: Trevor Norris --- src/accessors.cc | 41 +++++++++++++------ .../regress/regress-typedarray-length.js | 37 +++++++++++++++++ 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/src/accessors.cc b/src/accessors.cc index 9850cd388e4..e77dedbaeb8 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -100,22 +100,37 @@ bool Accessors::IsJSArrayBufferViewFieldAccessor(Handle map, Isolate* isolate = name->GetIsolate(); switch (map->instance_type()) { - case JS_TYPED_ARRAY_TYPE: - // %TypedArray%.prototype is non-configurable, and so are the following - // named properties on %TypedArray%.prototype, so we can directly inline - // the field-load for typed array maps that still use their - // %TypedArray%.prototype. - if (JSFunction::cast(map->GetConstructor())->prototype() != - map->prototype()) { + case JS_TYPED_ARRAY_TYPE: { + if (!CheckForName(name, isolate->factory()->length_string(), + JSTypedArray::kLengthOffset, object_offset) && + !CheckForName(name, isolate->factory()->byte_length_string(), + JSTypedArray::kByteLengthOffset, object_offset) && + !CheckForName(name, isolate->factory()->byte_offset_string(), + JSTypedArray::kByteOffsetOffset, object_offset)) { return false; } - return CheckForName(name, isolate->factory()->length_string(), - JSTypedArray::kLengthOffset, object_offset) || - CheckForName(name, isolate->factory()->byte_length_string(), - JSTypedArray::kByteLengthOffset, object_offset) || - CheckForName(name, isolate->factory()->byte_offset_string(), - JSTypedArray::kByteOffsetOffset, object_offset); + if (map->is_dictionary_map()) return false; + + // Check if the property is overridden on the instance. + DescriptorArray* descriptors = map->instance_descriptors(); + int descriptor = descriptors->SearchWithCache(*name, *map); + if (descriptor != DescriptorArray::kNotFound) return false; + + Handle proto = Handle(map->prototype(), isolate); + if (!proto->IsJSReceiver()) return false; + + // Check if the property is defined in the prototype chain. + LookupIterator it(proto, name); + if (!it.IsFound()) return false; + + Object* original_proto = + JSFunction::cast(map->GetConstructor())->prototype(); + + // Property is not configurable. It is enough to verify that + // the holder is the same. + return *it.GetHolder() == original_proto; + } case JS_DATA_VIEW_TYPE: return CheckForName(name, isolate->factory()->byte_length_string(), JSDataView::kByteLengthOffset, object_offset) || diff --git a/test/mjsunit/regress/regress-typedarray-length.js b/test/mjsunit/regress/regress-typedarray-length.js index cae55731f92..ee853647355 100644 --- a/test/mjsunit/regress/regress-typedarray-length.js +++ b/test/mjsunit/regress/regress-typedarray-length.js @@ -71,6 +71,43 @@ assertEquals(undefined, get(a)); assertEquals(undefined, get(a)); })(); +(function() { + "use strict"; + + class MyTypedArray extends Int32Array { + constructor(length) { + super(length); + } + } + + a = new MyTypedArray(1024); + + get = function(a) { + return a.length; + } + + assertEquals(1024, get(a)); + assertEquals(1024, get(a)); + assertEquals(1024, get(a)); + %OptimizeFunctionOnNextCall(get); + assertEquals(1024, get(a)); +})(); + +(function() { + "use strict"; + var a = new Uint8Array(4); + Object.defineProperty(a, "length", {get: function() { return "blah"; }}); + get = function(a) { + return a.length; + } + + assertEquals("blah", get(a)); + assertEquals("blah", get(a)); + assertEquals("blah", get(a)); + %OptimizeFunctionOnNextCall(get); + assertEquals("blah", get(a)); +})(); + // Ensure we cannot delete length, byteOffset, byteLength. assertTrue(Int32Array.prototype.hasOwnProperty("length")); assertTrue(Int32Array.prototype.hasOwnProperty("byteOffset"));