WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
211279
Merge putLength() into setLength()
https://bugs.webkit.org/show_bug.cgi?id=211279
Summary
Merge putLength() into setLength()
Saam Barati
Reported
2020-04-30 23:04:32 PDT
Seems superfluous and costly to do the method table dispatch.
Attachments
Patch
(9.70 KB, patch)
2020-08-26 04:03 PDT
,
Alexey Shvayka
darin
: review+
Details
Formatted Diff
Diff
View All
Add attachment
proposed patch, testcase, etc.
Alexey Shvayka
Comment 1
2020-08-26 04:03:22 PDT
Created
attachment 407289
[details]
Patch
Alexey Shvayka
Comment 2
2020-08-26 04:05:19 PDT
(In reply to Alexey Shvayka from
comment #1
)
> Created
attachment 407289
[details]
> Patch
Warmed-up runs, --outer 50:
r266157
patch array-shift-unshift-empty 64.8823+-1.0893 ^ 45.5567+-1.4411 ^ definitely 1.4242x faster
Saam Barati
Comment 3
2020-08-26 08:37:35 PDT
Comment on
attachment 407289
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=407289&action=review
> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:874 > + JSValue result = thisObj->get(globalObject, index);
Isn’t the whole point of the previous code that we need the Identifier when at the MAX_ARRAY_INDEX boundary?
Darin Adler
Comment 4
2020-08-26 11:53:08 PDT
Comment on
attachment 407289
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=407289&action=review
Ah, the runtime. One area of JavaScriptCore that I still have enough expertise in that I can review.
> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:166 > + if (LIKELY(isJSArray(obj))) {
Obviously this *is* likely, but I do like to follow the principle of only adding these when we know they’re beneficial so not 100% sure I would have added it. Hard to argue against it strongly, though.
>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:874 >> + JSValue result = thisObj->get(globalObject, index); > > Isn’t the whole point of the previous code that we need the Identifier when at the MAX_ARRAY_INDEX boundary?
Perhaps, but since Object::get(JSObject*, uint64_t) takes care of creating the identifier when it’s needed, this code will work properly without separate branches for valid array index values and other values. Object::deleteProperty(JSGlobalObject*, uint64_t) also does. A bit unfortunate that the two functions will do the work to make the Identifier string twice, but I suppose it’s an exotic case that we don’t need to optimize carefully. Similarly, we will end up calling jsNumber on the same number repeatedly, which would waste a bit of heap, but again only in exotic cases.
Saam Barati
Comment 5
2020-08-26 12:43:31 PDT
Comment on
attachment 407289
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=407289&action=review
r=me too
>>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:874 >>> + JSValue result = thisObj->get(globalObject, index); >> >> Isn’t the whole point of the previous code that we need the Identifier when at the MAX_ARRAY_INDEX boundary? > > Perhaps, but since Object::get(JSObject*, uint64_t) takes care of creating the identifier when it’s needed, this code will work properly without separate branches for valid array index values and other values. Object::deleteProperty(JSGlobalObject*, uint64_t) also does. A bit unfortunate that the two functions will do the work to make the Identifier string twice, but I suppose it’s an exotic case that we don’t need to optimize carefully. Similarly, we will end up calling jsNumber on the same number repeatedly, which would waste a bit of heap, but again only in exotic cases.
Ah! I forgot I added the uint64_t version of this method. I thought we were implicit casting to the uint32_t version and was worried. jsNumber on the same number repeatedly is cheap, and doesn't allocate anything in the heap.
Darin Adler
Comment 6
2020-08-26 14:14:03 PDT
Comment on
attachment 407289
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=407289&action=review
>>>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:874 >>>> + JSValue result = thisObj->get(globalObject, index); >>> >>> Isn’t the whole point of the previous code that we need the Identifier when at the MAX_ARRAY_INDEX boundary? >> >> Perhaps, but since Object::get(JSObject*, uint64_t) takes care of creating the identifier when it’s needed, this code will work properly without separate branches for valid array index values and other values. Object::deleteProperty(JSGlobalObject*, uint64_t) also does. A bit unfortunate that the two functions will do the work to make the Identifier string twice, but I suppose it’s an exotic case that we don’t need to optimize carefully. Similarly, we will end up calling jsNumber on the same number repeatedly, which would waste a bit of heap, but again only in exotic cases. > > Ah! I forgot I added the uint64_t version of this method. I thought we were implicit casting to the uint32_t version and was worried. > > jsNumber on the same number repeatedly is cheap, and doesn't allocate anything in the heap.
Oh, right, they all get converted to double, and a double always fits in a double. Some values lose precision but they all fit. When we added the uint64_t version it did create one subtle poor design thing. The 32-bit version has a required precondition that the number is an array index; you must not pass it the value 0xFFFFFFFF. The 64-bit version does not have that precondition, nor do other overloads. Although maybe there is an Identifier one that you must not call with a string that just happens to be a serialized array index. Requiring one number be avoided a strange difference to based only on the passed integer’s width. So I might have introduced a difference in name by renaming the 32-bit integer functions.
Saam Barati
Comment 7
2020-08-26 14:44:57 PDT
(In reply to Darin Adler from
comment #6
)
> Comment on
attachment 407289
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=407289&action=review
> > >>>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:874 > >>>> + JSValue result = thisObj->get(globalObject, index); > >>> > >>> Isn’t the whole point of the previous code that we need the Identifier when at the MAX_ARRAY_INDEX boundary? > >> > >> Perhaps, but since Object::get(JSObject*, uint64_t) takes care of creating the identifier when it’s needed, this code will work properly without separate branches for valid array index values and other values. Object::deleteProperty(JSGlobalObject*, uint64_t) also does. A bit unfortunate that the two functions will do the work to make the Identifier string twice, but I suppose it’s an exotic case that we don’t need to optimize carefully. Similarly, we will end up calling jsNumber on the same number repeatedly, which would waste a bit of heap, but again only in exotic cases. > > > > Ah! I forgot I added the uint64_t version of this method. I thought we were implicit casting to the uint32_t version and was worried. > > > > jsNumber on the same number repeatedly is cheap, and doesn't allocate anything in the heap. > > Oh, right, they all get converted to double, and a double always fits in a > double. Some values lose precision but they all fit. > > When we added the uint64_t version it did create one subtle poor design > thing. The 32-bit version has a required precondition that the number is an > array index; you must not pass it the value 0xFFFFFFFF. The 64-bit version > does not have that precondition, nor do other overloads. Although maybe > there is an Identifier one that you must not call with a string that just > happens to be a serialized array index. Requiring one number be avoided a > strange difference to based only on the passed integer’s width. So I might > have introduced a difference in name by renaming the 32-bit integer > functions.
We could rename them. Or invent a new type to pass in as a wrapper to the index. Something like ArrayIndex, and it asserts the value is in range, and it's not implicitly constructible.
Darin Adler
Comment 8
2020-08-26 14:50:23 PDT
(In reply to Saam Barati from
comment #7
)
> We could rename them. Or invent a new type to pass in as a wrapper to the > index. Something like ArrayIndex, and it asserts the value is in range, and > it's not implicitly constructible.
We are thinking similarly: I literally considered that option too when writing the comment above, but thought it was too complicated to explain so left it out.
Saam Barati
Comment 9
2020-08-26 14:53:43 PDT
(In reply to Darin Adler from
comment #8
)
> (In reply to Saam Barati from
comment #7
) > > We could rename them. Or invent a new type to pass in as a wrapper to the > > index. Something like ArrayIndex, and it asserts the value is in range, and > > it's not implicitly constructible. > > We are thinking similarly: I literally considered that option too when > writing the comment above, but thought it was too complicated to explain so > left it out.
I think the right way to do this is to change those helper methods, but also to change the method table methods of: - putByIndex - deletePropertyByIndex - getOwnPropertySlotByIndex
Saam Barati
Comment 10
2020-08-26 14:55:40 PDT
(In reply to Saam Barati from
comment #9
)
> (In reply to Darin Adler from
comment #8
) > > (In reply to Saam Barati from
comment #7
) > > > We could rename them. Or invent a new type to pass in as a wrapper to the > > > index. Something like ArrayIndex, and it asserts the value is in range, and > > > it's not implicitly constructible. > > > > We are thinking similarly: I literally considered that option too when > > writing the comment above, but thought it was too complicated to explain so > > left it out. > > I think the right way to do this is to change those helper methods, but also > to change the method table methods of: > - putByIndex > - deletePropertyByIndex > - getOwnPropertySlotByIndex
I might actually be wrong. Reading some more code.
Saam Barati
Comment 11
2020-08-26 15:01:54 PDT
(In reply to Darin Adler from
comment #6
)
> Comment on
attachment 407289
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=407289&action=review
> > >>>> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:874 > >>>> + JSValue result = thisObj->get(globalObject, index); > >>> > >>> Isn’t the whole point of the previous code that we need the Identifier when at the MAX_ARRAY_INDEX boundary? > >> > >> Perhaps, but since Object::get(JSObject*, uint64_t) takes care of creating the identifier when it’s needed, this code will work properly without separate branches for valid array index values and other values. Object::deleteProperty(JSGlobalObject*, uint64_t) also does. A bit unfortunate that the two functions will do the work to make the Identifier string twice, but I suppose it’s an exotic case that we don’t need to optimize carefully. Similarly, we will end up calling jsNumber on the same number repeatedly, which would waste a bit of heap, but again only in exotic cases. > > > > Ah! I forgot I added the uint64_t version of this method. I thought we were implicit casting to the uint32_t version and was worried. > > > > jsNumber on the same number repeatedly is cheap, and doesn't allocate anything in the heap. > > Oh, right, they all get converted to double, and a double always fits in a > double. Some values lose precision but they all fit. > > When we added the uint64_t version it did create one subtle poor design > thing. The 32-bit version has a required precondition that the number is an > array index; you must not pass it the value 0xFFFFFFFF. The 64-bit version > does not have that precondition, nor do other overloads. Although maybe > there is an Identifier one that you must not call with a string that just > happens to be a serialized array index. Requiring one number be avoided a > strange difference to based only on the passed integer’s width. So I might > have introduced a difference in name by renaming the 32-bit integer > functions.
After reading some code, it doesn't appear that there are restrictions on the range of the unsigned passed into these methods. For example, 0xFFFFFFFF should be handled just fine: ``` ALWAYS_INLINE bool putByIndexInline(JSGlobalObject* globalObject, unsigned propertyName, JSValue value, bool shouldThrow) { VM& vm = getVM(globalObject); if (canSetIndexQuickly(propertyName, value)) { setIndexQuickly(vm, propertyName, value); return true; } return methodTable(vm)->putByIndex(this, globalObject, propertyName, value, shouldThrow); } ``` canSetIndexQuickly for 0xFFFFFFFF should return false. This will lead us to taking the putByIndex path. Let's look at one example implementation: ``` bool JSObject::putByIndex(JSCell* cell, JSGlobalObject* globalObject, unsigned propertyName, JSValue value, bool shouldThrow) { VM& vm = globalObject->vm(); JSObject* thisObject = jsCast<JSObject*>(cell); if (propertyName > MAX_ARRAY_INDEX) { PutPropertySlot slot(cell, shouldThrow); return thisObject->methodTable(vm)->put(thisObject, globalObject, Identifier::from(vm, propertyName), value, slot); } ... ... ``` So it's expected that the various method table methods I listed properly handle all ranges of values passed into the various byIndex MethodTable methods.
Darin Adler
Comment 12
2020-08-26 15:09:11 PDT
(In reply to Saam Barati from
comment #11
)
> it doesn't appear that there are restrictions on > the range of the unsigned passed into these methods
OK, glad to hear it. Guess I remembered wrong. I’m pretty sure I’m the one who created these separate overloads so that integers could pass through the runtime without being converted to and from Identifier strings, but I must have forgotten some of the details over the years.
Saam Barati
Comment 13
2020-08-26 15:17:31 PDT
(In reply to Darin Adler from
comment #12
)
> (In reply to Saam Barati from
comment #11
) > > it doesn't appear that there are restrictions on > > the range of the unsigned passed into these methods > > OK, glad to hear it. Guess I remembered wrong. I’m pretty sure I’m the one > who created these separate overloads so that integers could pass through the > runtime without being converted to and from Identifier strings, but I must > have forgotten some of the details over the years.
Yeah, the code itself is a bit subtle, and I have to re-read the code to convince myself of its correctness. It took me a second, just now, to realize that our "isIndex" function and MAX_ARRAY_INDEX are indeed in sync. One nit I have with the code base is to rewrite isIndex in terms of MAX_ARRAY_INDEX, since we have code that relies on > MAX_ARRAY_INDEX having "isIndex" return false. Currently, we just hard code numbers in two different places.
Darin Adler
Comment 14
2020-08-26 15:27:14 PDT
(In reply to Saam Barati from
comment #13
)
> One nit I have with the code base is to rewrite isIndex in terms of > MAX_ARRAY_INDEX, since we have code that relies on > MAX_ARRAY_INDEX having > "isIndex" return false. Currently, we just hard code numbers in two > different places.
Compared to MAX_ARRAY_INDEX, isIndex is new. Yusuke added it in 2015:
https://trac.webkit.org/changeset/182452
MAX_ARRAY_INDEX is many years older and used many more places.
Saam Barati
Comment 15
2020-08-26 16:16:10 PDT
(In reply to Darin Adler from
comment #14
)
> (In reply to Saam Barati from
comment #13
) > > One nit I have with the code base is to rewrite isIndex in terms of > > MAX_ARRAY_INDEX, since we have code that relies on > MAX_ARRAY_INDEX having > > "isIndex" return false. Currently, we just hard code numbers in two > > different places. > > Compared to MAX_ARRAY_INDEX, isIndex is new. Yusuke added it in 2015: > >
https://trac.webkit.org/changeset/182452
> > MAX_ARRAY_INDEX is many years older and used many more places.
Will do a small cleanup in:
https://bugs.webkit.org/show_bug.cgi?id=215872
Alexey Shvayka
Comment 16
2020-08-26 18:55:12 PDT
Committed
r266215
: <
https://trac.webkit.org/changeset/266215
>
Radar WebKit Bug Importer
Comment 17
2020-08-26 18:56:18 PDT
<
rdar://problem/67843783
>
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug